X-Git-Url: https://git.distorted.org.uk/~mdw/termux-packages/blobdiff_plain/658bba67c018a0b51df08723cd4c3a57c2668d2e..02764a913fc426fc4f4854ec70f424d97aeb3bcf:/scripts/buildorder.py diff --git a/scripts/buildorder.py b/scripts/buildorder.py index fa02f7a9..e371a9d9 100755 --- a/scripts/buildorder.py +++ b/scripts/buildorder.py @@ -2,6 +2,7 @@ # buildorder.py - script to generate a build order respecting package dependencies import os +import re import sys from itertools import filterfalse @@ -30,6 +31,10 @@ def unique_everseen(iterable, key=None): def die(msg): sys.exit('ERROR: ' + msg) +def rchop(thestring, ending): + if thestring.endswith(ending): + return thestring[:-len(ending)] + return thestring class TermuxBuildFile(object): def __init__(self, path): @@ -37,53 +42,58 @@ class TermuxBuildFile(object): def _get_dependencies(self): pkg_dep_prefix = 'TERMUX_PKG_DEPENDS=' + pkg_build_dep_prefix = 'TERMUX_PKG_BUILD_DEPENDS=' subpkg_dep_prefix = 'TERMUX_SUBPKG_DEPENDS=' + comma_deps = '' - with open(self.path) as f: + with open(self.path, encoding="utf-8") as f: prefix = None for line in f: if line.startswith(pkg_dep_prefix): prefix = pkg_dep_prefix + elif line.startswith(pkg_build_dep_prefix): + prefix = pkg_build_dep_prefix elif line.startswith(subpkg_dep_prefix): prefix = subpkg_dep_prefix else: continue - comma_deps = line[len(prefix):].replace('"', '') + comma_deps += line[len(prefix):].replace('"', '').replace("'", '').replace("\n", ",") - return set([ - dep.strip() for dep in comma_deps.split(',') - if 'libandroid-support' not in dep - ]) + # Remove trailing ',' that is otherwise replacing the final newline + comma_deps = comma_deps[:-1] + if not comma_deps: + # no deps found + return set() - # no deps found - return set() + return set([ + # Replace parenthesis to handle version qualifiers, as in "gcc (>= 5.0)": + rchop(re.sub(r'\(.*?\)', '', dep).strip(), '-dev') for dep in comma_deps.split(',') + ]) class TermuxPackage(object): - PACKAGES_DIR = 'packages' - - def __init__(self, name): - self.name = name - self.dir = os.path.join(self.PACKAGES_DIR, name) + def __init__(self, dir_path): + self.dir = dir_path + self.name = os.path.basename(self.dir) # search package build.sh build_sh_path = os.path.join(self.dir, 'build.sh') if not os.path.isfile(build_sh_path): - raise Exception("build.sh not found") + raise Exception("build.sh not found for package '" + self.name + "'") self.buildfile = TermuxBuildFile(build_sh_path) self.deps = self.buildfile._get_dependencies() + if 'libandroid-support' not in self.deps and self.name != 'libandroid-support': + # Every package may depend on libandroid-support without declaring it: + self.deps.add('libandroid-support') # search subpackages self.subpkgs = [] for filename in os.listdir(self.dir): - if not filename.endswith('.subpackage.sh'): - continue - - subpkg_name = filename.split('.subpackage.sh')[0] - subpkg = TermuxSubPackage(subpkg_name, parent=self) + if not filename.endswith('.subpackage.sh'): continue + subpkg = TermuxSubPackage(self.dir + '/' + filename, self) self.subpkgs.append(subpkg) self.deps |= subpkg.deps @@ -100,14 +110,13 @@ class TermuxPackage(object): class TermuxSubPackage(TermuxPackage): - - def __init__(self, name, parent=None): + def __init__(self, subpackage_file_path, parent): if parent is None: raise Exception("SubPackages should have a parent") - self.name = name + self.buildfile = TermuxBuildFile(subpackage_file_path) + self.name = os.path.basename(subpackage_file_path).split('.subpackage.sh')[0] self.parent = parent - self.buildfile = TermuxBuildFile(os.path.join(self.parent.dir, name + '.subpackage.sh')) self.deps = self.buildfile._get_dependencies() def __repr__(self): @@ -124,16 +133,20 @@ pkgs_map = {} # Reverse dependencies pkg_depends_on = {} -PACKAGES_DIR = 'packages' +PACKAGES_DIRS = ['packages'] def populate(): + all_packages = [] + for package_dir in PACKAGES_DIRS: + for pkgdir_name in sorted(os.listdir(package_dir)): + dir_path = package_dir + '/' + pkgdir_name + if os.path.isfile(dir_path + '/build.sh'): + all_packages.append(TermuxPackage(package_dir + '/' + pkgdir_name)) - for pkgdir_name in sorted(os.listdir(PACKAGES_DIR)): - - pkg = TermuxPackage(pkgdir_name) - - pkgs_map[pkg.name] = pkg + for pkg in all_packages: + if pkg.name in pkgs_map: die('Duplicated package: ' + pkg.name) + else: pkgs_map[pkg.name] = pkg for subpkg in pkg.subpkgs: pkgs_map[subpkg.name] = pkg @@ -163,9 +176,8 @@ def generate_full_buildorder(): if not leaf_pkgs: die('No package without dependencies - where to start?') - # Sort alphabetically, but with libandroid-support first (since dependency on libandroid-support - # does not need to be declared explicitly, so anything might in theory depend on it to build): - pkg_queue = sorted(leaf_pkgs, key=lambda p: '' if p.name == 'libandroid-support' else p.name) + # Sort alphabetically: + pkg_queue = sorted(leaf_pkgs, key=lambda p: p.name) # Topological sorting visited = set() @@ -200,32 +212,50 @@ def generate_full_buildorder(): return build_order +def deps(pkg): + l = [] + for dep in sorted(pkg.deps): + l += deps_then_me(pkgs_map[dep]) + return l def deps_then_me(pkg): l = [] - for dep in sorted(pkg.deps): l += deps_then_me(pkgs_map[dep]) l += [pkg] - return l -def generate_targets_buildorder(targetnames): +def generate_targets_buildorder(target_paths): buildorder = [] - for pkgname in targetnames: - buildorder += deps_then_me(pkgs_map[pkgname]) + for target_path in target_paths: + if target_path.endswith('/'): target_path = target_path[:-1] + pkgname = os.path.basename(target_path) + if not pkgname in pkgs_map: + die('Dependencies for ' + pkgname + ' could not be calculated (skip dependency check with -s)') + buildorder += deps(pkgs_map[pkgname]) return unique_everseen(buildorder) if __name__ == '__main__': + full_buildorder = len(sys.argv) == 1 + if not full_buildorder: + packages_real_path = os.path.realpath('packages') + for path in sys.argv[1:]: + if not os.path.isdir(path): + die('Not a directory: ' + path) + if path.endswith('/'): path = path[:-1] + parent_path = os.path.dirname(path) + if packages_real_path != os.path.realpath(parent_path): + PACKAGES_DIRS.append(parent_path) + populate() - if len(sys.argv) == 1: + if full_buildorder: bo = generate_full_buildorder() else: bo = generate_targets_buildorder(sys.argv[1:]) for pkg in bo: - print(pkg.name) + print(pkg.dir)