diff options
Diffstat (limited to 'debian/bin/gencontrol.py')
-rwxr-xr-x | debian/bin/gencontrol.py | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/debian/bin/gencontrol.py b/debian/bin/gencontrol.py new file mode 100755 index 0000000..77e4536 --- /dev/null +++ b/debian/bin/gencontrol.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python3 + +import io +import json +import locale +import os +import re +import sys + +sys.path.insert(0, "debian/lib/python") +sys.path.append(sys.argv[1] + "/lib/python") +locale.setlocale(locale.LC_CTYPE, "C.UTF-8") + +from config import Config +from debian_linux.debian import BinaryPackage, PackageRelation, _ControlFileDict +from debian_linux.debian import PackageDescription as PackageDescriptionBase +import debian_linux.gencontrol +from debian_linux.gencontrol import Makefile, MakeFlags, PackagesList +from debian_linux.utils import TextWrapper +from debian_linux.utils import Templates as TemplatesBase +from collections import OrderedDict + +class PackageDescription(PackageDescriptionBase): + __slots__ = () + + def __init__(self, value = None): + self.short = [] + self.long = [] + if value is not None: + value = value.split("\n", 1) + self.append_short(value[0]) + if len(value) > 1: + self.append(value[1]) + + def __str__(self): + wrap = TextWrapper(width = 74, fix_sentence_endings = True).wrap + short = ', '.join(self.short) + long_pars = [] + for t in self.long: + if isinstance(t, str): + t = wrap(t) + long_pars.append('\n '.join(t)) + long = '\n .\n '.join(long_pars) + return short + '\n ' + long + + def append_pre(self, l): + self.long.append(l) + + def extend(self, desc): + if isinstance(desc, PackageDescription): + self.short.extend(desc.short) + self.long.extend(desc.long) + elif isinstance(desc, (list, tuple)): + for i in desc: + self.append(i) + +BinaryPackage._fields['Description'] = PackageDescription + +class Template(_ControlFileDict): + _fields = OrderedDict(( + ('Template', str), + ('Type', str), + ('Default', str), + ('Description', PackageDescriptionBase), + )) + + +class Templates(TemplatesBase): + def get_templates_control(self, key: str, context: dict[str, str] = {}) -> Template: + return Template.read_rfc822(io.StringIO(self.get(key, context))) + + +class GenControl(debian_linux.gencontrol.Gencontrol): + def __init__(self): + self.config = Config() + self.templates = Templates() + + with open('debian/modinfo.json', 'r') as f: + self.modinfo = json.load(f) + + # Make another dict keyed by firmware names + self.firmware_modules = {} + for name, info in self.modinfo.items(): + for firmware_filename in info['firmware']: + self.firmware_modules.setdefault(firmware_filename, []) \ + .append(name) + + def __call__(self): + packages = PackagesList() + makefile = Makefile() + + self.do_source(packages) + self.do_extra(packages, makefile) + self.do_main(packages, makefile) + + self.write(packages, makefile) + + def do_source(self, packages): + packages['source'] = self.templates.get_source_control("control.source", {})[0] + + def do_extra(self, packages, makefile): + config_entry = self.config['base',] + vars = {} + vars.update(config_entry) + + for package_binary in self.templates.get_control("control.extra", {}): + assert package_binary['Package'].startswith('firmware-') + package = package_binary['Package'].replace('firmware-', '') + + makeflags = MakeFlags() + makeflags['FILES'] = '' + makeflags['PACKAGE'] = package + makefile.add_cmds('binary-indep', ["$(MAKE) -f debian/rules.real binary-indep %s" % makeflags]) + + packages.append(package_binary) + + def do_main(self, packages, makefile): + config_entry = self.config['base',] + vars = {} + vars.update(config_entry) + + makeflags = MakeFlags() + + for i in ('build', 'binary-arch', 'setup'): + makefile.add_cmds("%s_%%" % i, ["@true"]) + + for package in config_entry['packages']: + self.do_package(packages, makefile, package, vars.copy(), makeflags.copy()) + + def do_package(self, packages, makefile, package, vars, makeflags): + config_entry = self.config['base', package] + vars.update(config_entry) + vars['package'] = package + vars['package-env-prefix'] = 'FIRMWARE_' + package.upper().replace('-', '_') + + makeflags['PACKAGE'] = package + + # Those might be absent, set them to empty string for replacement to work: + empty_list = ['replaces', 'conflicts', 'breaks', 'provides', 'recommends'] + for optional in ['replaces', 'conflicts', 'breaks', 'provides', 'recommends']: + if optional not in vars: + vars[optional] = '' + + package_dir = "debian/config/%s" % package + + try: + os.unlink('debian/firmware-%s.bug-presubj' % package) + except OSError: + pass + os.symlink('bug-presubj', 'debian/firmware-%s.bug-presubj' % package) + + files_orig = config_entry['files'] + files_real = {} + files_unused = [] + links = {} + links_rev = {} + + # Look for additional and replacement files in binary package config + for root, dirs, files in os.walk(package_dir): + try: + dirs.remove('.svn') + except ValueError: + pass + for f in files: + cur_path = root + '/' + f + if root != package_dir: + f = root[len(package_dir) + 1 : ] + '/' + f + if os.path.islink(cur_path): + if f in files_orig: + links[f] = os.readlink(cur_path) + continue + f1 = f.rsplit('-', 1) + if f in files_orig: + files_real[f] = f, cur_path, None + continue + if len(f1) > 1: + f_base, f_version = f1 + if f_base in files_orig: + if f_base in files_real: + raise RuntimeError("Multiple files for %s" % f_base) + files_real[f_base] = f_base, package_dir + '/' + f, \ + f_version + continue + # Whitelist files not expected to be installed as firmware + if f in ['defines', 'LICENSE.install', + 'update.py', 'update.sh']: + continue + files_unused.append(f) + + # Take all the other files from upstream + for f in files_orig: + if f not in files_real and f not in links: + if os.path.islink(f): + links[f] = os.readlink(f) + elif os.path.isfile(f): + files_real[f] = f, f, None + + for f in links: + link_target = os.path.normpath(os.path.join(f, '..', links[f])) + links_rev.setdefault(link_target, []).append(f) + + if files_unused: + print('W: %s: unused files:' % package, ' '.join(files_unused), + file=sys.stderr) + + makeflags['FILES'] = ' '.join(["%s:%s" % (i[1], i[0]) for i in sorted(files_real.values())]) + vars['files_real'] = ' '.join(["/lib/firmware/%s" % i for i in config_entry['files']]) + + makeflags['LINKS'] = ' '.join(["%s:%s" % (link, target) + for link, target in sorted(links.items())]) + + files_desc = ["Contents:"] + firmware_meta_temp = self.templates.get("metainfo.xml.firmware") + firmware_meta_list = [] + module_names = set() + + wrap = TextWrapper(width = 71, fix_sentence_endings = True, + initial_indent = ' * ', + subsequent_indent = ' ').wrap + for f in config_entry['files']: + firmware_meta_list.append(self.substitute(firmware_meta_temp, + {'filename': f})) + for module_name in self.firmware_modules.get(f, []): + module_names.add(module_name) + if f in links: + continue + f, f_real, version = files_real[f] + c = self.config.get(('base', package, f), {}) + desc = c.get('desc') + if version is None: + version = c.get('version') + try: + f = f + ', ' + ', '.join(sorted(links_rev[f])) + except KeyError: + pass + if desc and version: + desc = "%s, version %s (%s)" % (desc, version, f) + elif desc: + desc = "%s (%s)" % (desc, f) + else: + desc = "%s" % f + files_desc.extend(wrap(desc)) + + modaliases = set() + for module_name in module_names: + for modalias in self.modinfo[module_name]['alias']: + modaliases.add(modalias) + modalias_meta_list = [ + self.substitute(self.templates.get("metainfo.xml.modalias"), + {'alias': alias}) + for alias in sorted(list(modaliases)) + ] + + packages_binary = self.templates.get_control("control.binary", vars) + + packages_binary[0]['Description'].append_pre(files_desc) + + if 'initramfs-tools' in config_entry.get('support', []): + postinst = self.templates.get('postinst.initramfs-tools') + open("debian/firmware-%s.postinst" % package, 'w').write(self.substitute(postinst, vars)) + + if 'license-accept' in config_entry: + license = open("%s/LICENSE.install" % package_dir, 'r').read() + preinst = self.templates.get('preinst.license') + preinst_filename = "debian/firmware-%s.preinst" % package + open(preinst_filename, 'w').write(self.substitute(preinst, vars)) + + templates = self.templates.get_templates_control('templates.license', vars) + templates[0]['Description'].append(re.sub('\n\n', '\n.\n', license)) + templates_filename = "debian/firmware-%s.templates" % package + self.write_rfc822(open(templates_filename, 'w'), templates) + + desc = packages_binary[0]['Description'] + desc.append( +"""This firmware is covered by the %s. +You must agree to the terms of this license before it is installed.""" +% vars['license-title']) + packages_binary[0]['Pre-Depends'] = PackageRelation('debconf | debconf-2.0') + + packages.extend(packages_binary) + + makefile.add_cmds('binary-indep', ["$(MAKE) -f debian/rules.real binary-indep %s" % makeflags]) + + vars['firmware-list'] = ''.join(firmware_meta_list) + vars['modalias-list'] = ''.join(modalias_meta_list) + # Underscores are preferred to hyphens + vars['package-metainfo'] = package.replace('-', '_') + # Summary must not contain line breaks + vars['longdesc-metainfo'] = re.sub(r'\s+', ' ', vars['longdesc']) + package_meta_temp = self.templates.get("metainfo.xml", {}) + # XXX Might need to escape some characters + open("debian/firmware-%s.metainfo.xml" % package, 'w').write(self.substitute(package_meta_temp, vars)) + + def process_template(self, in_entry, vars): + e = Template() + for key, value in in_entry.items(): + if isinstance(value, PackageDescription): + e[key] = self.process_description(value, vars) + elif key[:2] == 'X-': + pass + else: + e[key] = self.substitute(value, vars) + return e + + def process_templates(self, in_entries, vars): + entries = [] + for i in in_entries: + entries.append(self.process_template(i, vars)) + return entries + + def substitute(self, s, vars): + if isinstance(s, (list, tuple)): + return [self.substitute(i, vars) for i in s] + def subst(match): + if match.group(1): + return vars.get(match.group(2), '') + else: + return vars[match.group(2)] + return re.sub(r'@(\??)([-_a-z]+)@', subst, str(s)) + + def write(self, packages, makefile): + self.write_control(packages.values()) + self.write_makefile(makefile) + + def write_control(self, list): + self.write_rfc822(open("debian/control", 'w'), list) + + def write_makefile(self, makefile): + f = open("debian/rules.gen", 'w') + makefile.write(f) + f.close() + + def write_rfc822(self, f, list): + for entry in list: + for key, value in entry.items(): + f.write("%s: %s\n" % (key, value)) + f.write('\n') + +if __name__ == '__main__': + GenControl()() |