aboutsummaryrefslogtreecommitdiff
path: root/utils/seeded-builds/manifest-union
blob: 4a2392a2769539745063adf54cfa6a59c8b26875 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/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("""\
<?xml version="1.0" encoding="UTF-8"?>
<manifest>

""")
    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("""\

</manifest>
""")

optparser = optparse.OptionParser(usage="%prog -o <union.xml> <manifest.xml>...")
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)