From 51097aa5ba7eee5ee71f2ec88ddfefc955f38237 Mon Sep 17 00:00:00 2001 From: Max Rees Date: Mon, 13 Apr 2020 22:49:36 -0500 Subject: depver_test: support multiple arches correctly --- depver_test.py | 194 ++++++++++++++++++++++++++++++++++++--------------------- output.py | 8 +++ 2 files changed, 132 insertions(+), 70 deletions(-) diff --git a/depver_test.py b/depver_test.py index 5e25c39..014e296 100644 --- a/depver_test.py +++ b/depver_test.py @@ -14,7 +14,7 @@ import sys from apkkit.base.index import Index from version import is_older, is_same, verkey, ver_is, APK_OPS -from output import FORMATTERS, No, Partial +from output import FORMATTERS, Yes, No, Partial, PARTIAL_MISSING def atomize(spec): if not any(i in spec for i in APK_OPS): @@ -31,79 +31,134 @@ def atomize(spec): # Not reached assert False -def analyze(opts, arch): - pkgs = collections.defaultdict(dict) - newest = {} - providers = collections.defaultdict(list) - - print("Loading " + arch + "...", file=sys.stderr) - - index = [] - for repo in opts.repos: - url = f"{opts.url}/{repo}/{arch}/APKINDEX.tar.gz" - index.extend(Index(url=url).packages) - - for pkg in index: - new = pkg.version - pkgs[pkg.name][new] = pkg - - curr = newest.get(pkg.name, None) - if curr is None: - newest[pkg.name] = new - elif is_older(curr, new): - newest[pkg.name] = new - - providers[pkg.name].append((pkg.name, new)) - for i in pkg.provides: - i = i.split("=", maxsplit=1) - if len(i) == 2: - i, pver = i - else: - i, pver = i[0], new +def analyze(opts): + pkgs = {i: collections.defaultdict(dict) for i in opts.arches} + newest = {i: dict() for i in opts.arches} + providers = {i: collections.defaultdict(list) for i in opts.arches} + + for arch in opts.arches: + print("Loading " + arch + "...", file=sys.stderr) + + index = [] + for repo in opts.repos: + url = f"{opts.url}/{repo}/{arch}/APKINDEX.tar.gz" + index.extend(Index(url=url).packages) - curr = newest.get(i, None) + for pkg in index: + new = pkg.version + pkgs[arch][pkg.name][new] = pkg + + curr = newest[arch].get(pkg.name, None) if curr is None: - newest[i] = pver - elif is_older(curr, pver): - newest[i] = pver + newest[arch][pkg.name] = new + elif is_older(curr, new): + newest[arch][pkg.name] = new + + providers[arch][pkg.name].append((pkg.name, new)) + for i in pkg.provides: + i = i.split("=", maxsplit=1) + if len(i) == 2: + i, pver = i + else: + i, pver = i[0], new + + curr = newest[arch].get(i, None) + if curr is None: + newest[arch][i] = pver + elif is_older(curr, pver): + newest[arch][i] = pver + + providers[arch][i].append((pkg.name, pver)) + + return pkgs, newest, providers + +def trace_deps(providers, newest, pkg): + for dep in pkg.depends: + if dep.startswith("!"): + continue + + spec = dep + dep, constraint = atomize(dep) + + if dep in pkg.provides: + continue + if dep not in providers: + yield No(f"missing {spec}") + continue + + if constraint: + for provider, pver in providers[dep]: + if constraint(pver): + break + else: + yield No(f"missing {spec}") - providers[i].append((pkg.name, pver)) + for _, pver in providers[dep]: + if is_same(pver, newest[dep]): + break + else: + yield No(f"old {spec}") + +def order_arch(opts): + pkgs, newest, providers = analyze(opts) yield ["arch", "package", "version", "issue"] - for name in sorted(pkgs.keys()): - # DON'T use newest[] here. It is possible that a package - # provides= another package's name with a newer version - ver = sorted(pkgs[name].keys(), key=verkey)[-1] - pkg = pkgs[name][ver] - - for dep in pkg.depends: - if dep.startswith("!"): + for arch in opts.arches: + for name in sorted(pkgs[arch].keys()): + # DON'T use newest[] here. It is possible that a package + # provides= another package's name with a newer version + ver = sorted(pkgs[arch][name].keys(), key=verkey)[-1] + pkg = pkgs[arch][name][ver] + + for problem in trace_deps(providers[arch], newest[arch], pkg): + yield [arch, name, ver, problem] + +def order_pkg(opts): + pkgs, newest, providers = analyze(opts) + + all_pkgs = set() + for arch in pkgs: + all_pkgs.update(pkgs[arch].keys()) + + yield ["package", "problem", *opts.arches] + for name in sorted(all_pkgs): + all_problems = set() + problems = {} + + for arch in opts.arches: + vers = pkgs[arch].get(name, {}) + if not vers: + problems[arch] = [PARTIAL_MISSING] + # Don't add PARTIAL_MISSING to all_problems: we don't + # care if "name" is missing, use pkgver_test.py for that continue + ver = sorted(vers.keys(), key=verkey)[-1] + pkg = vers[ver] - spec = dep - dep, constraint = atomize(dep) + problems[arch] = list( + trace_deps(providers[arch], newest[arch], pkg) + ) + all_problems.update(problems[arch]) - if dep in pkg.provides: - continue - if dep not in providers: - yield [arch, name, ver, No(f"Missing {spec}")] - continue + if not all_problems: + continue - if constraint: - for provider, pver in providers[dep]: - if constraint(pver): - break + for problem in sorted(all_problems): + row = [name, problem.content] + for arch in opts.arches: + if problem in problems[arch]: + row.append(type(problem)("yes")) + elif PARTIAL_MISSING in problems[arch]: + row.append(Yes("n/a")) else: - yield [arch, name, ver, No(f"Missing {spec}")] + row.append(Yes("no")) - if opts.only_missing: - continue + yield row - for _, pver in providers[dep]: - if is_same(pver, newest[dep]): - break - else: - yield [arch, name, ver, Partial(f"Old {spec}")] +ORDERS = { + "arch": order_arch, + "pkg": order_pkg, +} if __name__ == "__main__": opts = argparse.ArgumentParser( @@ -122,8 +177,9 @@ if __name__ == "__main__": help="display format", ) opts.add_argument( - "-m", "--only-missing", action="store_true", - help="show only missing dependencies", + "-o", "--order", choices=ORDERS.keys(), + default="pkg", + help="display order", ) opts.add_argument( "url", metavar="URL", @@ -141,9 +197,7 @@ if __name__ == "__main__": opts.repos = opts.repos.split(",") opts.arches = opts.arches.split(",") - for arch in opts.arches: - # FIXME: with html, wrapping will repeat for > 1 arch - FORMATTERS[opts.format]( - opts, - analyze(opts, arch), - ) + FORMATTERS[opts.format]( + opts, + ORDERS[opts.order](opts), + ) diff --git a/output.py b/output.py index 870d618..c8bb542 100644 --- a/output.py +++ b/output.py @@ -3,11 +3,19 @@ # Copyright © 2019-2020 Adélie Linux team. All rights reserved. # NCSA license. # +import functools import subprocess +@functools.total_ordering class _Cell: def __init__(self, content): self.content = content + def __eq__(self, other): + return self.content == other.content + def __lt__(self, other): + return self.content < other.content + def __hash__(self): + return hash(self.content) _ANSI_CLEAR = "\033[0;00m" -- cgit v1.2.3-70-g09d2