diff options
author | Andrew Wilcox <AWilcox@Wilcox-Tech.com> | 2016-03-08 02:17:11 -0600 |
---|---|---|
committer | Andrew Wilcox <AWilcox@Wilcox-Tech.com> | 2016-03-08 02:17:11 -0600 |
commit | 2f4c6f451554dce4c52d941a759d696f0b955122 (patch) | |
tree | 8ca7afb9181db9fb60dbd9a1fe94a4aa95bb66bb | |
parent | 4c5017b6227ebdd74741bbb8cb07878a96d34315 (diff) | |
download | apkkit-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.py | 53 | ||||
-rw-r--r-- | apkkit/portage.py | 194 |
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) |