summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Wilcox <AWilcox@Wilcox-Tech.com>2016-03-08 02:17:11 -0600
committerAndrew Wilcox <AWilcox@Wilcox-Tech.com>2016-03-08 02:17:11 -0600
commit2f4c6f451554dce4c52d941a759d696f0b955122 (patch)
tree8ca7afb9181db9fb60dbd9a1fe94a4aa95bb66bb
parent4c5017b6227ebdd74741bbb8cb07878a96d34315 (diff)
downloadapkkit-2f4c6f451554dce4c52d941a759d696f0b955122.tar.gz
apkkit-2f4c6f451554dce4c52d941a759d696f0b955122.tar.bz2
apkkit-2f4c6f451554dce4c52d941a759d696f0b955122.tar.xz
apkkit-2f4c6f451554dce4c52d941a759d696f0b955122.zip
Add support for invocation from Portage++
-rw-r--r--apkkit/io/apkfile.py53
-rw-r--r--apkkit/portage.py194
2 files changed, 242 insertions, 5 deletions
diff --git a/apkkit/io/apkfile.py b/apkkit/io/apkfile.py
index cbab844..4710d0c 100644
--- a/apkkit/io/apkfile.py
+++ b/apkkit/io/apkfile.py
@@ -27,6 +27,34 @@ except ImportError:
LOGGER.warning("cryptography module is unavailable - can't sign packages.")
+FILTERS = None
+
+
+def _add_filter_func(func):
+ """Add a callable to filter files out of the created data.tar.gz.
+
+ :param callable func:
+ The callable. It will be passed a single parameter, filename.
+ """
+ global FILTERS
+
+ if FILTERS is None:
+ FILTERS = set()
+
+ FILTERS.add(func)
+
+
+def _tar_filter(filename):
+ """tarfile exclusion predicate that calls all defined filter functions."""
+ global FILTERS
+
+ for func in FILTERS:
+ if func(filename):
+ return True
+
+ return False
+
+
def _ensure_no_debug(filename):
"""tarfile exclusion predicate to ensure /usr/lib/debug isn't included.
@@ -54,10 +82,14 @@ def _sign_control(control, privkey, pubkey):
signature = None
with open(privkey, "rb") as key_file:
+ #password = getpass()
+ #if password != '':
+ # password.encode('utf-8')
+ #else:
+ password = None
+
private_key = serialization.load_pem_private_key(
- key_file.read(),
- password=getpass() or None,
- backend=default_backend()
+ key_file.read(), password=password, backend=default_backend()
)
signer = private_key.signer(padding.PKCS1v15(), hashes.SHA256())
signer.update(control.getvalue())
@@ -109,7 +141,7 @@ def _make_data_tgz(datadir, mode):
format=tarfile.PAX_FORMAT) as data:
for item in glob.glob(datadir + '/*'):
data.add(item, arcname=os.path.basename(item),
- exclude=_ensure_no_debug)
+ exclude=_tar_filter)
LOGGER.info('Hashing data.tar [pass 1]...')
fdfile.seek(0)
@@ -178,7 +210,7 @@ class APKFile:
@classmethod
def create(cls, package, datadir, sign=True, signfile=None, data_hash=True,
- hash_method='sha256'):
+ hash_method='sha256', **kwargs):
"""Create an APK file in memory from a package and data directory.
:param package:
@@ -199,6 +231,17 @@ class APKFile:
:param str hash_method:
The hash method to use for hashing the data - default is sha256.
"""
+
+ # ensure no stale filters are applied.
+ global FILTERS
+ FILTERS = None
+
+ if 'filters' in kwargs:
+ [_add_filter_func(func) for func in kwargs.pop('filters')]
+
+ # XXX what about -debug split packages? they need this.
+ _add_filter_func(_ensure_no_debug)
+
LOGGER.info('Creating APK from data in: %s', datadir)
package.size = recursive_size(datadir)
diff --git a/apkkit/portage.py b/apkkit/portage.py
new file mode 100644
index 0000000..f483dde
--- /dev/null
+++ b/apkkit/portage.py
@@ -0,0 +1,194 @@
+"""The entry point to APK Kit from Portage.
+
+This module serves as the "bridge" from Portage land (ebuild) to Adélie land
+(APK). Most of EAPI=5 is supported, with a few extensions. More information is
+below. The most important bit of information is that this package is still in
+ACTIVE DEVELOPMENT and may be rough around the edges!
+
+EAPI=0
+======
+
+Supported
+---------
+* Slots: appended to the package name, i.e. dev-lang/python:3.4 = python3.4.
+
+Unsupported
+-----------
+* ! = "unspecified" block: always treated as weak per EAPI=2.
+
+
+EAPI=1
+======
+
+Supported
+---------
+* Slot dependencies: 'mangled' as above for slots.
+
+Unsupported
+-----------
+
+
+EAPI=2
+======
+
+Supported
+---------
+* ! and !! blocks: ! will cause pre-install to warn, !! will emit a conflict in
+ PKGINFO.
+
+Unsupported
+-----------
+
+
+EAPI=3
+======
+
+Supported
+---------
+
+Unsupported
+-----------
+
+
+EAPI=4
+======
+
+Supported
+---------
+* pkg_pretend: this is run on the target (binary) system before pre-install.
+
+Unsupported
+-----------
+* pkg_info: so far, no way for this to run on the target has been found.
+
+
+EAPI=5
+======
+
+Supported
+---------
+
+Unsupported
+-----------
+* Subslots: not yet supported, will choke.
+
+
+Extensions
+==========
+* Triggers: EAPI=5-adelie adds trigger support. For a really contrived example:
+
+```
+EAPI=5-adelie
+[...]
+TRIGGER_ON="/usr/share/fonts:/usr/X11R7/fonts"
+
+pkg_trigger() {
+ fc-cache
+}
+```
+"""
+
+from apkkit.base.package import Package
+from apkkit.io.apkfile import APKFile
+import logging
+import os
+from portage.dep import Atom, use_reduce
+import sys
+
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+ARCH_MAP = {'amd64': 'x86_64', 'hppa': 'parisc'}
+"""Mapping for architectures that have the wrong name in Portage."""
+
+
+def _maybe_xlat(pn, category):
+ """Offers the ability to translate a package name.
+
+ This is mainly useful for package names that exist in multiple categories,
+ for instance 'dev-db/redis' and 'dev-ruby/redis' (redis-ruby).
+
+ Requires at least an empty /etc/portage/package.xlat file.
+
+ Thanks to ryao for pointing out this needs to be done, and Elizafox for the
+ initial concept/prototype.
+
+ :param str pn:
+ The name of the package to possibly translate.
+
+ :param str category:
+ The category of the package to possibly translate.
+
+ :returns str:
+ The name to use in Adélie for the package.
+ """
+
+ return pn
+
+
+def native(settings, mydbapi=None):
+ """Take a Portage settings object and turn it into an APK.
+
+ Surprisingly less difficult than it sounds, but surprisingly more difficult
+ than it appears on second glance.
+
+ :param settings:
+ A Portage settings object.
+
+ :param mydbapi:
+ A Portage DBAPI object for the package.
+ """
+ params = {}
+
+ params['name'] = _maybe_xlat(settings['PN'], settings['CATEGORY'])
+ if 'SLOT' in settings and not settings['SLOT'].startswith('0/') and\
+ settings['SLOT'] != '0':
+ params['name'] += settings['SLOT']
+ params['version'] = settings['PVR'] # include -rX if necessary
+ params['arch'] = ARCH_MAP.get(settings['ARCH'], settings['ARCH'])
+ params['provides'] = list()
+ params['depends'] = list()
+
+ cpv = '%s/%s' % (settings['CATEGORY'], settings['PF'])
+ if mydbapi is None or not mydbapi.cpv_exists(cpv):
+ print('!!! Fatal error: CPV does not exist or DBAPI is missing')
+ sys.exit(-1)
+
+ desc, url = mydbapi.aux_get(cpv, ('DESCRIPTION', 'HOMEPAGE'))
+ params['description'] = desc
+ params['url'] = url
+
+ run_deps = use_reduce(mydbapi.aux_get(cpv, ('RDEPEND',)),
+ uselist=settings['USE'], opconvert=True,
+ token_class=Atom, eapi=settings['EAPI'])
+ for dep in run_deps:
+ category, package = dep.cp.split('/', 1)
+ package = _maybe_xlat(package, category)
+ op = dep.operator
+ ver = dep.version
+
+ if op is None and ver is None:
+ # "Easy" dep.
+ params['depends'].append(package)
+ continue
+
+ # apk-tools/src/package.c:195
+ # there is literally no other documentation for this format.
+ apk_format = '{name}{op}{ver}'.format(name=package, op=op, ver=ver)
+ if dep.blocker:
+ apk_format = '!' + apk_format
+ params['depends'].append(apk_format)
+
+ package = Package(**params)
+ apk = APKFile.create(package, settings['D'])
+ filename = "{name}-{ver}.apk".format(name=package.name, ver=package.version)
+ apk.write(os.path.join(settings.get('PKG_DIR', settings['PKGDIR']),
+ filename))
+
+ return 0
+
+if __name__ == '__main__':
+ import portage
+ print("You are calling from the shell, this is not supported!")
+ native(os.environ, portage.db['/']['porttree'].dbapi)