Verified Commit 2f4c6f45 authored by A. Wilcox's avatar A. Wilcox 🦊
Browse files

Add support for invocation from Portage++

parent 4c5017b6
......@@ -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)
......
"""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)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment