#!/usr/bin/python import argparse import sqlite3 from dedup.utils import fetchiter def bipartitions(edges): """ @type edges: {T: set(T)} @rtype: [(set(T), set(T))] """ todo = set(edges.keys()) result = [] colors = {} while todo: node = todo.pop() colors[node] = False partition = (set((node,)), set()) todo2 = set((node,)) while todo2: partnode = todo2.pop() for nextnode in edges[partnode]: try: if colors[nextnode] == colors[partnode]: raise ValueError("not bipartite") except KeyError: colors[nextnode] = not colors[partnode] partition[colors[nextnode]].add(nextnode) todo2.add(nextnode) todo.remove(nextnode) result.append(partition) return result def show_archset(archs, limit=3): """ @type archs: set(str) @rtype: str """ archs = set(archs) if len(archs) > limit: return "%d archs" % len(archs) return ", ".join(sorted(archs)) def show_combinations(combinations): edges = {} for left, right in combinations: edges.setdefault(left, set()).add(right) edges.setdefault(right, set()).add(left) # safety if len(edges) == 2: return "%s <-> %s" % (min(left, right), max(left, right)) try: partitions = bipartitions(edges) except ValueError: pass else: if len(partitions) == 1: return " <-> ".join(show_archset(archs, 4) for archs in sorted(partitions[0], key=len)) else: return "%d bipartitions of %s" % (len(partitions), show_archset(edges.keys())) if all(len(outedges) == len(edges) - 1 for outedges in edges.values()): return "any two of " + show_archset(edges.keys(), limit=4) return "%s with %d combinations" % (show_archset(edges.keys()), len(combinations)) def show_files(filenames): if len(filenames) == 1: return next(iter(filenames)) return "%d files" % len(filenames) def main(): parser = argparse.ArgumentParser() parser.add_argument("-d", "--database", action="store", default="test.sqlite3", help="path to the sqlite3 database file") args = parser.parse_args() db = sqlite3.connect(args.database) cur = db.cursor() cur.execute("SELECT name1, architecture1, architecture2, filename FROM masame_conflict;") same_conflicts = {} for package, arch1, arch2, filename in fetchiter(cur): conflicts = same_conflicts.setdefault(package, {}) conflicts.setdefault(filename, set()).add((arch1, arch2)) for package, conflicts in sorted(same_conflicts.items()): all_combinations = set() for combinations in conflicts.values(): all_combinations.update(combinations) print("%s conflicts on %s on %s" % (package, show_files(conflicts.keys()), show_combinations(all_combinations))) cur.execute("SELECT name FROM maforeign_candidate ORDER BY name;") for name, in fetchiter(cur): print("%s could be Multi-Arch: foreign" % name) cur.execute("SELECT name FROM archall_candidate ORDER BY name;") archall_suggests = set() for name, in fetchiter(cur): archall_suggests.add(name) print("%s could be converted to Architecture: all + Multi-Arch: foreign" % name) cur.execute("SELECT name FROM masame_candidate ORDER BY name;") for name, in fetchiter(cur): if name not in archall_suggests: print("%s could be Multi-Arch: same" % name) cur.execute("SELECT depender, dependee FROM colonany_candidate ORDER BY depender;") for depender, dependee in fetchiter(cur): print("%s could have its %s dependency annotated with :any" % (depender, dependee)) if __name__ == "__main__": main()