#!/usr/bin/env python """ Produce set-theorhetic union of several Android manifests. Each project appearing in any manifest gets output once, and at most once, to the resulting manifest. Differences in checkout path/branch are ignored - values from a random occurance are output in result. The output is mostly intended for usage with "repo init --mirror", to produce mirror which contains all repositories used by a number of manifests. """ import sys import optparse from xml.dom import minidom class Manifest: def __init__(self): self.remote_map = {} self.project_map = {} self.default_node = None @staticmethod def compare_attr_wise(node1, node2): if node1.attributes.length != node2.attributes.length: return False for i in xrange(node1.attributes.length): a1 = node1.attributes.item(i) if a1.value != node2.attributes[a1.name].value: return False return True def add_default(self, default_node): if self.default_node: if not self.compare_attr_wise(default_node, self.default_node): raise ValueError("default collision: %s vs %s" % (default_node.toxml(), self.default_node.toxml())) return self.default_node = default_node def add_remote(self, remote_node): name = remote_node.getAttribute("name") if name in self.remote_map: if not self.compare_attr_wise(remote_node, self.remote_map[name]): raise ValueError("remote collision: %s vs %s" % (remote_node.toxml(), self.remote_map[name].toxml())) return False self.remote_map[name] = remote_node return True def add_project(self, project_node): remote = project_node.getAttribute("remote") name = project_node.getAttribute("name") revision = project_node.getAttribute("revision") path = project_node.getAttribute("path") # key = (remote, name, revision, path) # key = (remote, name) # Turned out can't have dup project names per manifest, not per remote key = name if key in self.project_map: return False self.project_map[key] = project_node return True def get_default(self): return self.default_node def get_remotes(self): return self.remote_map.values() def get_projects(self): return self.project_map.values() def union(manifest_files): manifest = Manifest() first = True for f in manifest_files: print >>sys.stderr, "Processing: %s" % f dom = minidom.parse(f) default = dom.getElementsByTagName("default") assert len(default) == 1 manifest.add_default(default[0]) for r in dom.getElementsByTagName("remote"): manifest.add_remote(r) for p in dom.getElementsByTagName("project"): added = manifest.add_project(p) if not first and added: print >>sys.stderr, "Added new project: %s" % p.toxml() first = False return manifest def dump(manifest, out): def sort_by_name(n1, n2): return cmp(n1.getAttribute("name"), n2.getAttribute("name")) out.write("""\ """) for r in sorted(manifest.get_remotes(), sort_by_name): out.write(r.toxml()) out.write("\n") out.write("\n") out.write(manifest.get_default().toxml()) out.write("\n\n") for p in sorted(manifest.get_projects(), sort_by_name): out.write(p.toxml()) out.write("\n") out.write("""\ """) optparser = optparse.OptionParser(usage="%prog -o ...") optparser.add_option("-o", metavar="FILE", help="Output file") options, args = optparser.parse_args(sys.argv[1:]) if len(args) < 1: optparser.error("Wrong number of arguments") m = union(args) out = sys.stdout if options.o: out = open(options.o, "w") dump(m, out)