tpackage.py - amprolla - devuan's apt repo merger
 (HTM) git clone https://git.parazyd.org/amprolla
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tpackage.py (7166B)
       ---
            1 # See LICENSE file for copyright and license details.
            2 
            3 """
            4 Package merging functions and helpers
            5 """
            6 
            7 from os import makedirs
            8 from os.path import dirname, isfile, join
            9 from gzip import open as gzip_open
           10 from lzma import open as lzma_open
           11 from shutil import copyfile
           12 
           13 import lib.globalvars as globalvars
           14 from lib.config import mergedir, packages_keys, sources_keys, spooldir
           15 from lib.log import logtofile
           16 from lib.parse import parse_dependencies, parse_packages, cmppkgver
           17 
           18 
           19 def write_packages(packages, filename, sort=True, sources=False):
           20     """
           21     Writes `packages` to a file (per debian Packages format)
           22     If sort=True, the packages are sorted by name.
           23     """
           24     makedirs(dirname(filename), exist_ok=True)
           25 
           26     # Copy the arch-specific Release file from devuan if it's not there
           27     bsnm = 'Packages.gz'
           28     if sources:
           29         bsnm = 'Sources.gz'
           30     rel = filename.replace(bsnm, 'Release')
           31     sprl = rel.replace(mergedir, join(spooldir, 'devuan'))
           32     if not isfile(rel) and isfile(sprl):
           33         copyfile(sprl, rel)
           34 
           35     gzf = gzip_open(filename, 'w')
           36     xzf = lzma_open(filename.replace('.gz', '.xz'), 'w')
           37     # f = open(filename.replace('.gz', ''), 'wb')
           38 
           39     pkg_items = packages.items()
           40     if sort:
           41         pkg_items = sorted(pkg_items, key=lambda x: x[0])
           42 
           43     if sources:
           44         keylist = sources_keys
           45     else:
           46         keylist = packages_keys
           47 
           48     for pkg_name, pkg_contents in pkg_items:
           49         for key in keylist:
           50             if key in pkg_contents:
           51                 sin = '%s: %s\n' % (key, pkg_contents[key])
           52                 gzf.write(sin.encode('utf-8'))
           53                 xzf.write(sin.encode('utf-8'))
           54                 # f.write(sin.encode('utf-8'))
           55         gzf.write(b'\n')
           56         xzf.write(b'\n')
           57         # f.write(b'\n')
           58 
           59     gzf.close()
           60     xzf.close()
           61     # f.close()
           62 
           63 
           64 def load_packages_file(filename):
           65     """
           66     Load a gzip'd packages file.
           67     Returns a dictionary of package name and package key-values.
           68     """
           69     # TODO: should we skip files like this if they don't exist?
           70     if filename is not None and isfile(filename):
           71         packages_contents = gzip_open(filename).read()
           72         packages_contents = packages_contents.decode('utf-8')
           73         return parse_packages(packages_contents)
           74 
           75     return None
           76 
           77 
           78 def depends_on_all_banned(deps, banned_pkgs):
           79     """
           80     Gets a list of dicts of dep alternatives and a set of banned packages and
           81     returns True if any of the dicts consist exclusively of banned_pkgs.
           82     """
           83     for dep in deps:
           84         alt = set(dep.keys())
           85         if len(alt.intersection(banned_pkgs)) == len(alt):
           86             # All the alternatives are banned
           87             return True
           88     return False
           89 
           90 
           91 def depends_on(deps, package_set):
           92     """
           93     Gets a list of dicts of dep alternatives and a set of packages and returns
           94     True if any of the dicts include at least one of the elements in
           95     package_set.
           96     """
           97     for dep in deps:
           98         alt = set(dep.keys())
           99         if alt.intersection(package_set):
          100             # At least one alternative is in package_set
          101             return True
          102     return False
          103 
          104 
          105 def package_banned(pkg, banned_pkgs):
          106     """
          107     Returns True is the package contains a banned dependency.
          108     Currently checks and parses both the 'Depends:' and the 'Pre-Depends'
          109     fields of the package.
          110     """
          111     if pkg.get('Package') in banned_pkgs:
          112         logtofile('bannedpackages.txt', '%s,%s\n' % (globalvars.suite,
          113                                                      pkg.get('Package')))
          114         return True
          115 
          116     depends = parse_dependencies(pkg.get('Depends', ''))
          117     pre_depends = parse_dependencies(pkg.get('Pre-Depends', ''))
          118 
          119     depends.extend(pre_depends)
          120 
          121     if depends_on(depends, set(['libsystemd0'])):
          122         logtofile('libsystemd.txt', '%s,%s\n' % (globalvars.suite,
          123                                                  pkg.get('Package')))
          124 
          125     isbanned = depends_on_all_banned(depends, banned_pkgs)
          126     if isbanned:
          127         logtofile('bannedpackages.txt', '%s,%s\n' % (globalvars.suite,
          128                                                      pkg.get('Package')))
          129     return isbanned
          130 
          131 
          132 def package_newer(pkg1, pkg2):
          133     """
          134     Checks whether the package of a lower priority has a higher version than
          135     the package of the higher priority and returns True if so.
          136 
          137     Ref: https://www.debian.org/doc/debian-policy/#version
          138     """
          139     # Hardcoded list of packages we don't want to check
          140     _skips = []
          141     if pkg1.get('Package') in _skips:
          142         return False
          143 
          144     if cmppkgver(pkg1.get('Version'), pkg2.get('Version')) < 0:
          145         return True
          146 
          147     return False
          148 
          149 
          150 def merge_packages(pkg1, pkg2, name1, name2, banned_packages=set(),
          151                    rewriter=None):
          152     """
          153     Merges two previously loaded/parsed (using load_packages_file) packages
          154     dictionaries, preferring `pkg1` over `pkg2`, and optionally discarding any
          155     banned packages.
          156     """
          157     new_pkgs = {}
          158     package_names = set(pkg1.keys()).union(set(pkg2.keys()))
          159     obsoletepkgs = []
          160 
          161     for pkg in package_names:
          162         pkg1_pkg = pkg1.get(pkg)
          163         pkg2_pkg = pkg2.get(pkg)
          164 
          165         if pkg1_pkg and pkg2_pkg:
          166             if rewriter:
          167                 pkg1_pkg = rewriter(pkg1_pkg, name1)
          168             new_pkgs[pkg] = pkg1_pkg
          169             if package_newer(pkg1_pkg, pkg2_pkg):
          170                 obsoletepkgs.append('%s,%s,%s,%s' % (globalvars.suite,
          171                                                      pkg1_pkg.get('Package'),
          172                                                      pkg1_pkg.get('Version'),
          173                                                      pkg2_pkg.get('Version')))
          174         elif pkg1_pkg:
          175             if not package_banned(pkg1_pkg, banned_packages):
          176                 if rewriter:
          177                     pkg1_pkg = rewriter(pkg1_pkg, name1)
          178                 new_pkgs[pkg] = pkg1_pkg
          179         elif pkg2_pkg:
          180             if not package_banned(pkg2_pkg, banned_packages):
          181                 if rewriter:
          182                     pkg2_pkg = rewriter(pkg2_pkg, name2)
          183                 new_pkgs[pkg] = pkg2_pkg
          184         else:
          185             assert False, 'Impossibru'
          186 
          187     if obsoletepkgs:
          188         obsoletepkgs = '\n'.join(obsoletepkgs) + '\n'
          189         logtofile('%s-oldpackages.txt' % globalvars.suite, obsoletepkgs,
          190                   redo=False)
          191 
          192     return new_pkgs
          193 
          194 
          195 def merge_packages_many(packages, banned_packages=set(), rewriter=None):
          196     """
          197     Merges two (or more) previously loaded/parsed (using load_packages_file)
          198     packages dictionaries, priority is defined by the order of the `packages`
          199     list, optionally discarding any banned packages.
          200     """
          201     if not len(packages) > 1:
          202         while not len(packages) > 1:
          203             packages.append({'name': '', 'packages': {}})
          204 
          205     new_pkgs = {}
          206 
          207     pkg1 = packages[0]
          208     pkg2 = packages[1]
          209 
          210     new_pkgs = merge_packages(pkg1['packages'], pkg2['packages'],
          211                               pkg1['name'], pkg2['name'],
          212                               banned_packages=banned_packages,
          213                               rewriter=rewriter)
          214 
          215     for pkg in packages[2:]:
          216         new_pkgs = merge_packages(new_pkgs, pkg['packages'], '', pkg['name'],
          217                                   banned_packages=banned_packages)
          218 
          219     return new_pkgs