xref: /aosp_15_r20/build/soong/bin/soongdbg (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*333d2b36SAndroid Build Coastguard Worker
3*333d2b36SAndroid Build Coastguard Workerimport argparse
4*333d2b36SAndroid Build Coastguard Workerimport fnmatch
5*333d2b36SAndroid Build Coastguard Workerimport html
6*333d2b36SAndroid Build Coastguard Workerimport io
7*333d2b36SAndroid Build Coastguard Workerimport json
8*333d2b36SAndroid Build Coastguard Workerimport os
9*333d2b36SAndroid Build Coastguard Workerimport pathlib
10*333d2b36SAndroid Build Coastguard Workerimport subprocess
11*333d2b36SAndroid Build Coastguard Workerimport types
12*333d2b36SAndroid Build Coastguard Workerimport sys
13*333d2b36SAndroid Build Coastguard Worker
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerclass Graph:
16*333d2b36SAndroid Build Coastguard Worker    def __init__(self, modules):
17*333d2b36SAndroid Build Coastguard Worker        def get_or_make_node(dictionary, id, module):
18*333d2b36SAndroid Build Coastguard Worker            node = dictionary.get(id)
19*333d2b36SAndroid Build Coastguard Worker            if node:
20*333d2b36SAndroid Build Coastguard Worker                if module and not node.module:
21*333d2b36SAndroid Build Coastguard Worker                    node.module = module
22*333d2b36SAndroid Build Coastguard Worker                return node
23*333d2b36SAndroid Build Coastguard Worker            node = Node(id, module)
24*333d2b36SAndroid Build Coastguard Worker            dictionary[id] = node
25*333d2b36SAndroid Build Coastguard Worker            return node
26*333d2b36SAndroid Build Coastguard Worker        self.nodes = dict()
27*333d2b36SAndroid Build Coastguard Worker        for module in modules.values():
28*333d2b36SAndroid Build Coastguard Worker            node = get_or_make_node(self.nodes, module.id, module)
29*333d2b36SAndroid Build Coastguard Worker            for d in module.deps:
30*333d2b36SAndroid Build Coastguard Worker                dep = get_or_make_node(self.nodes, d.id, None)
31*333d2b36SAndroid Build Coastguard Worker                node.deps.add(dep)
32*333d2b36SAndroid Build Coastguard Worker                dep.rdeps.add(node)
33*333d2b36SAndroid Build Coastguard Worker                node.dep_tags.setdefault(dep, list()).append(d)
34*333d2b36SAndroid Build Coastguard Worker
35*333d2b36SAndroid Build Coastguard Worker    def find_paths(self, id1, id2, tag_filter):
36*333d2b36SAndroid Build Coastguard Worker        # Throws KeyError if one of the names isn't found
37*333d2b36SAndroid Build Coastguard Worker        def recurse(node1, node2, visited):
38*333d2b36SAndroid Build Coastguard Worker            result = set()
39*333d2b36SAndroid Build Coastguard Worker            for dep in node1.rdeps:
40*333d2b36SAndroid Build Coastguard Worker                if not matches_tag(dep, node1, tag_filter):
41*333d2b36SAndroid Build Coastguard Worker                    continue
42*333d2b36SAndroid Build Coastguard Worker                if dep == node2:
43*333d2b36SAndroid Build Coastguard Worker                    result.add(node2)
44*333d2b36SAndroid Build Coastguard Worker                if dep not in visited:
45*333d2b36SAndroid Build Coastguard Worker                    visited.add(dep)
46*333d2b36SAndroid Build Coastguard Worker                    found = recurse(dep, node2, visited)
47*333d2b36SAndroid Build Coastguard Worker                    if found:
48*333d2b36SAndroid Build Coastguard Worker                        result |= found
49*333d2b36SAndroid Build Coastguard Worker                        result.add(dep)
50*333d2b36SAndroid Build Coastguard Worker            return result
51*333d2b36SAndroid Build Coastguard Worker        node1 = self.nodes[id1]
52*333d2b36SAndroid Build Coastguard Worker        node2 = self.nodes[id2]
53*333d2b36SAndroid Build Coastguard Worker        # Take either direction
54*333d2b36SAndroid Build Coastguard Worker        p = recurse(node1, node2, set())
55*333d2b36SAndroid Build Coastguard Worker        if p:
56*333d2b36SAndroid Build Coastguard Worker            p.add(node1)
57*333d2b36SAndroid Build Coastguard Worker            return p
58*333d2b36SAndroid Build Coastguard Worker        p = recurse(node2, node1, set())
59*333d2b36SAndroid Build Coastguard Worker        p.add(node2)
60*333d2b36SAndroid Build Coastguard Worker        return p
61*333d2b36SAndroid Build Coastguard Worker
62*333d2b36SAndroid Build Coastguard Worker
63*333d2b36SAndroid Build Coastguard Workerclass Node:
64*333d2b36SAndroid Build Coastguard Worker    def __init__(self, id, module):
65*333d2b36SAndroid Build Coastguard Worker        self.id = id
66*333d2b36SAndroid Build Coastguard Worker        self.module = module
67*333d2b36SAndroid Build Coastguard Worker        self.deps = set()
68*333d2b36SAndroid Build Coastguard Worker        self.rdeps = set()
69*333d2b36SAndroid Build Coastguard Worker        self.dep_tags = {}
70*333d2b36SAndroid Build Coastguard Worker
71*333d2b36SAndroid Build Coastguard Worker
72*333d2b36SAndroid Build Coastguard WorkerPROVIDERS = [
73*333d2b36SAndroid Build Coastguard Worker    "android/soong/java.JarJarProviderData",
74*333d2b36SAndroid Build Coastguard Worker    "android/soong/java.BaseJarJarProviderData",
75*333d2b36SAndroid Build Coastguard Worker]
76*333d2b36SAndroid Build Coastguard Worker
77*333d2b36SAndroid Build Coastguard Worker
78*333d2b36SAndroid Build Coastguard Workerdef format_dep_label(node, dep):
79*333d2b36SAndroid Build Coastguard Worker    tags = node.dep_tags.get(dep)
80*333d2b36SAndroid Build Coastguard Worker    labels = []
81*333d2b36SAndroid Build Coastguard Worker    if tags:
82*333d2b36SAndroid Build Coastguard Worker        labels = [tag.tag_type.split("/")[-1] for tag in tags]
83*333d2b36SAndroid Build Coastguard Worker        labels = sorted(set(labels))
84*333d2b36SAndroid Build Coastguard Worker    if labels:
85*333d2b36SAndroid Build Coastguard Worker        result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
86*333d2b36SAndroid Build Coastguard Worker        for label in labels:
87*333d2b36SAndroid Build Coastguard Worker            result += f"<tr><td>{label}</td></tr>"
88*333d2b36SAndroid Build Coastguard Worker        result += "</table>>"
89*333d2b36SAndroid Build Coastguard Worker        return result
90*333d2b36SAndroid Build Coastguard Worker
91*333d2b36SAndroid Build Coastguard Worker
92*333d2b36SAndroid Build Coastguard Workerdef format_node_label(node, module_formatter):
93*333d2b36SAndroid Build Coastguard Worker    result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
94*333d2b36SAndroid Build Coastguard Worker
95*333d2b36SAndroid Build Coastguard Worker    # node name
96*333d2b36SAndroid Build Coastguard Worker    result += f"<tr><td><b>{node.module.name if node.module else node.id}</b></td></tr>"
97*333d2b36SAndroid Build Coastguard Worker
98*333d2b36SAndroid Build Coastguard Worker    if node.module:
99*333d2b36SAndroid Build Coastguard Worker        # node_type
100*333d2b36SAndroid Build Coastguard Worker        result += f"<tr><td>{node.module.type}</td></tr>"
101*333d2b36SAndroid Build Coastguard Worker
102*333d2b36SAndroid Build Coastguard Worker        # module_formatter will return a list of rows
103*333d2b36SAndroid Build Coastguard Worker        for row in module_formatter(node.module):
104*333d2b36SAndroid Build Coastguard Worker            row = html.escape(row)
105*333d2b36SAndroid Build Coastguard Worker            result += f"<tr><td><font color=\"#666666\">{row}</font></td></tr>"
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Worker    result += "</table>>"
108*333d2b36SAndroid Build Coastguard Worker    return result
109*333d2b36SAndroid Build Coastguard Worker
110*333d2b36SAndroid Build Coastguard Worker
111*333d2b36SAndroid Build Coastguard Workerdef format_source_pos(file, lineno):
112*333d2b36SAndroid Build Coastguard Worker    result = file
113*333d2b36SAndroid Build Coastguard Worker    if lineno:
114*333d2b36SAndroid Build Coastguard Worker        result += f":{lineno}"
115*333d2b36SAndroid Build Coastguard Worker    return result
116*333d2b36SAndroid Build Coastguard Worker
117*333d2b36SAndroid Build Coastguard Worker
118*333d2b36SAndroid Build Coastguard WorkerSTRIP_TYPE_PREFIXES = [
119*333d2b36SAndroid Build Coastguard Worker    "android/soong/",
120*333d2b36SAndroid Build Coastguard Worker    "github.com/google/",
121*333d2b36SAndroid Build Coastguard Worker]
122*333d2b36SAndroid Build Coastguard Worker
123*333d2b36SAndroid Build Coastguard Worker
124*333d2b36SAndroid Build Coastguard Workerdef format_provider(provider):
125*333d2b36SAndroid Build Coastguard Worker    result = ""
126*333d2b36SAndroid Build Coastguard Worker    for prefix in STRIP_TYPE_PREFIXES:
127*333d2b36SAndroid Build Coastguard Worker        if provider.type.startswith(prefix):
128*333d2b36SAndroid Build Coastguard Worker            result = provider.type[len(prefix):]
129*333d2b36SAndroid Build Coastguard Worker            break
130*333d2b36SAndroid Build Coastguard Worker    if not result:
131*333d2b36SAndroid Build Coastguard Worker        result = provider.type
132*333d2b36SAndroid Build Coastguard Worker    if True and provider.debug:
133*333d2b36SAndroid Build Coastguard Worker        result += " (" + provider.debug + ")"
134*333d2b36SAndroid Build Coastguard Worker    return result
135*333d2b36SAndroid Build Coastguard Worker
136*333d2b36SAndroid Build Coastguard Worker
137*333d2b36SAndroid Build Coastguard Workerdef load_soong_debug():
138*333d2b36SAndroid Build Coastguard Worker    # Read the json
139*333d2b36SAndroid Build Coastguard Worker    try:
140*333d2b36SAndroid Build Coastguard Worker        with open(SOONG_DEBUG_DATA_FILENAME) as f:
141*333d2b36SAndroid Build Coastguard Worker            info = json.load(f, object_hook=lambda d: types.SimpleNamespace(**d))
142*333d2b36SAndroid Build Coastguard Worker    except IOError:
143*333d2b36SAndroid Build Coastguard Worker        sys.stderr.write(f"error: Unable to open {SOONG_DEBUG_DATA_FILENAME}. Make sure you have"
144*333d2b36SAndroid Build Coastguard Worker                         + " built with GENERATE_SOONG_DEBUG.\n")
145*333d2b36SAndroid Build Coastguard Worker        sys.exit(1)
146*333d2b36SAndroid Build Coastguard Worker
147*333d2b36SAndroid Build Coastguard Worker    # Construct IDs, which are name + variant if the
148*333d2b36SAndroid Build Coastguard Worker    name_counts = dict()
149*333d2b36SAndroid Build Coastguard Worker    for m in info.modules:
150*333d2b36SAndroid Build Coastguard Worker        name_counts[m.name] = name_counts.get(m.name, 0) + 1
151*333d2b36SAndroid Build Coastguard Worker    def get_id(m):
152*333d2b36SAndroid Build Coastguard Worker        result = m.name
153*333d2b36SAndroid Build Coastguard Worker        if name_counts[m.name] > 1 and m.variant:
154*333d2b36SAndroid Build Coastguard Worker            result += "@@" + m.variant
155*333d2b36SAndroid Build Coastguard Worker        return result
156*333d2b36SAndroid Build Coastguard Worker    for m in info.modules:
157*333d2b36SAndroid Build Coastguard Worker        m.id = get_id(m)
158*333d2b36SAndroid Build Coastguard Worker        for dep in m.deps:
159*333d2b36SAndroid Build Coastguard Worker            dep.id = get_id(dep)
160*333d2b36SAndroid Build Coastguard Worker
161*333d2b36SAndroid Build Coastguard Worker    return info
162*333d2b36SAndroid Build Coastguard Worker
163*333d2b36SAndroid Build Coastguard Worker
164*333d2b36SAndroid Build Coastguard Workerdef load_modules():
165*333d2b36SAndroid Build Coastguard Worker    info = load_soong_debug()
166*333d2b36SAndroid Build Coastguard Worker
167*333d2b36SAndroid Build Coastguard Worker    # Filter out unnamed modules
168*333d2b36SAndroid Build Coastguard Worker    modules = dict()
169*333d2b36SAndroid Build Coastguard Worker    for m in info.modules:
170*333d2b36SAndroid Build Coastguard Worker        if not m.name:
171*333d2b36SAndroid Build Coastguard Worker            continue
172*333d2b36SAndroid Build Coastguard Worker        modules[m.id] = m
173*333d2b36SAndroid Build Coastguard Worker
174*333d2b36SAndroid Build Coastguard Worker    return modules
175*333d2b36SAndroid Build Coastguard Worker
176*333d2b36SAndroid Build Coastguard Worker
177*333d2b36SAndroid Build Coastguard Workerdef load_graph():
178*333d2b36SAndroid Build Coastguard Worker    modules=load_modules()
179*333d2b36SAndroid Build Coastguard Worker    return Graph(modules)
180*333d2b36SAndroid Build Coastguard Worker
181*333d2b36SAndroid Build Coastguard Worker
182*333d2b36SAndroid Build Coastguard Workerdef module_selection_args(parser):
183*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("modules", nargs="*",
184*333d2b36SAndroid Build Coastguard Worker                        help="Modules to match. Can be glob-style wildcards.")
185*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("--provider", nargs="+",
186*333d2b36SAndroid Build Coastguard Worker                        help="Match the given providers.")
187*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("--dep", nargs="+",
188*333d2b36SAndroid Build Coastguard Worker                        help="Match the given providers.")
189*333d2b36SAndroid Build Coastguard Worker
190*333d2b36SAndroid Build Coastguard Worker
191*333d2b36SAndroid Build Coastguard Workerdef load_and_filter_modules(args):
192*333d2b36SAndroid Build Coastguard Worker    # Which modules are printed
193*333d2b36SAndroid Build Coastguard Worker    matchers = []
194*333d2b36SAndroid Build Coastguard Worker    if args.modules:
195*333d2b36SAndroid Build Coastguard Worker        matchers.append(lambda m: [True for pattern in args.modules
196*333d2b36SAndroid Build Coastguard Worker                                   if fnmatch.fnmatchcase(m.name, pattern)])
197*333d2b36SAndroid Build Coastguard Worker    if args.provider:
198*333d2b36SAndroid Build Coastguard Worker        matchers.append(lambda m: [True for pattern in args.provider
199*333d2b36SAndroid Build Coastguard Worker                                   if [True for p in m.providers if p.type.endswith(pattern)]])
200*333d2b36SAndroid Build Coastguard Worker    if args.dep:
201*333d2b36SAndroid Build Coastguard Worker        matchers.append(lambda m: [True for pattern in args.dep
202*333d2b36SAndroid Build Coastguard Worker                                   if [True for d in m.deps if d.id == pattern]])
203*333d2b36SAndroid Build Coastguard Worker
204*333d2b36SAndroid Build Coastguard Worker    if not matchers:
205*333d2b36SAndroid Build Coastguard Worker        sys.stderr.write("error: At least one module matcher must be supplied\n")
206*333d2b36SAndroid Build Coastguard Worker        sys.exit(1)
207*333d2b36SAndroid Build Coastguard Worker
208*333d2b36SAndroid Build Coastguard Worker    info = load_soong_debug()
209*333d2b36SAndroid Build Coastguard Worker    for m in sorted(info.modules, key=lambda m: (m.name, m.variant)):
210*333d2b36SAndroid Build Coastguard Worker        if len([matcher for matcher in matchers if matcher(m)]) == len(matchers):
211*333d2b36SAndroid Build Coastguard Worker            yield m
212*333d2b36SAndroid Build Coastguard Worker
213*333d2b36SAndroid Build Coastguard Worker
214*333d2b36SAndroid Build Coastguard Workerdef print_args(parser):
215*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("--label", action="append", metavar="JQ_FILTER",
216*333d2b36SAndroid Build Coastguard Worker                        help="jq query for each module metadata")
217*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("--deptags", action="store_true",
218*333d2b36SAndroid Build Coastguard Worker                        help="show dependency tags (makes the graph much more complex)")
219*333d2b36SAndroid Build Coastguard Worker    parser.add_argument("--tag", action="append", default=[],
220*333d2b36SAndroid Build Coastguard Worker                        help="Limit output to these dependency tags.")
221*333d2b36SAndroid Build Coastguard Worker
222*333d2b36SAndroid Build Coastguard Worker    group = parser.add_argument_group("output formats",
223*333d2b36SAndroid Build Coastguard Worker                                      "If no format is provided, a dot file will be written to"
224*333d2b36SAndroid Build Coastguard Worker                                      + " stdout.")
225*333d2b36SAndroid Build Coastguard Worker    output = group.add_mutually_exclusive_group()
226*333d2b36SAndroid Build Coastguard Worker    output.add_argument("--dot", type=str, metavar="FILENAME",
227*333d2b36SAndroid Build Coastguard Worker                        help="Write the graph to this file as dot (graphviz format)")
228*333d2b36SAndroid Build Coastguard Worker    output.add_argument("--svg", type=str, metavar="FILENAME",
229*333d2b36SAndroid Build Coastguard Worker                        help="Write the graph to this file as svg")
230*333d2b36SAndroid Build Coastguard Worker
231*333d2b36SAndroid Build Coastguard Worker
232*333d2b36SAndroid Build Coastguard Workerdef print_nodes(args, nodes, module_formatter):
233*333d2b36SAndroid Build Coastguard Worker    # Generate the graphviz
234*333d2b36SAndroid Build Coastguard Worker    dep_tag_id = 0
235*333d2b36SAndroid Build Coastguard Worker    dot = io.StringIO()
236*333d2b36SAndroid Build Coastguard Worker    dot.write("digraph {\n")
237*333d2b36SAndroid Build Coastguard Worker    dot.write("node [shape=box];")
238*333d2b36SAndroid Build Coastguard Worker
239*333d2b36SAndroid Build Coastguard Worker    for node in nodes:
240*333d2b36SAndroid Build Coastguard Worker        dot.write(f"\"{node.id}\" [label={format_node_label(node, module_formatter)}];\n")
241*333d2b36SAndroid Build Coastguard Worker        for dep in node.deps:
242*333d2b36SAndroid Build Coastguard Worker            if dep in nodes:
243*333d2b36SAndroid Build Coastguard Worker                if args.deptags:
244*333d2b36SAndroid Build Coastguard Worker                    dot.write(f"\"{node.id}\" -> \"__dep_tag_{dep_tag_id}\" [ arrowhead=none ];\n")
245*333d2b36SAndroid Build Coastguard Worker                    dot.write(f"\"__dep_tag_{dep_tag_id}\" -> \"{dep.id}\";\n")
246*333d2b36SAndroid Build Coastguard Worker                    dot.write(f"\"__dep_tag_{dep_tag_id}\""
247*333d2b36SAndroid Build Coastguard Worker                                  + f"[label={format_dep_label(node, dep)} shape=ellipse"
248*333d2b36SAndroid Build Coastguard Worker                                  + " color=\"#666666\" fontcolor=\"#666666\"];\n")
249*333d2b36SAndroid Build Coastguard Worker                else:
250*333d2b36SAndroid Build Coastguard Worker                    dot.write(f"\"{node.id}\" -> \"{dep.id}\";\n")
251*333d2b36SAndroid Build Coastguard Worker                dep_tag_id += 1
252*333d2b36SAndroid Build Coastguard Worker    dot.write("}\n")
253*333d2b36SAndroid Build Coastguard Worker    text = dot.getvalue()
254*333d2b36SAndroid Build Coastguard Worker
255*333d2b36SAndroid Build Coastguard Worker    # Write it somewhere
256*333d2b36SAndroid Build Coastguard Worker    if args.dot:
257*333d2b36SAndroid Build Coastguard Worker        with open(args.dot, "w") as f:
258*333d2b36SAndroid Build Coastguard Worker            f.write(text)
259*333d2b36SAndroid Build Coastguard Worker    elif args.svg:
260*333d2b36SAndroid Build Coastguard Worker        subprocess.run(["dot", "-Tsvg", "-o", args.svg],
261*333d2b36SAndroid Build Coastguard Worker                              input=text, text=True, check=True)
262*333d2b36SAndroid Build Coastguard Worker    else:
263*333d2b36SAndroid Build Coastguard Worker        sys.stdout.write(text)
264*333d2b36SAndroid Build Coastguard Worker
265*333d2b36SAndroid Build Coastguard Worker
266*333d2b36SAndroid Build Coastguard Workerdef matches_tag(node, dep, tag_filter):
267*333d2b36SAndroid Build Coastguard Worker    if not tag_filter:
268*333d2b36SAndroid Build Coastguard Worker        return True
269*333d2b36SAndroid Build Coastguard Worker    return not tag_filter.isdisjoint([t.tag_type for t in node.dep_tags[dep]])
270*333d2b36SAndroid Build Coastguard Worker
271*333d2b36SAndroid Build Coastguard Worker
272*333d2b36SAndroid Build Coastguard Workerdef get_deps(nodes, root, maxdepth, reverse, tag_filter):
273*333d2b36SAndroid Build Coastguard Worker    if root in nodes:
274*333d2b36SAndroid Build Coastguard Worker        return
275*333d2b36SAndroid Build Coastguard Worker    nodes.add(root)
276*333d2b36SAndroid Build Coastguard Worker    if maxdepth != 0:
277*333d2b36SAndroid Build Coastguard Worker        for dep in (root.rdeps if reverse else root.deps):
278*333d2b36SAndroid Build Coastguard Worker            if not matches_tag(root, dep, tag_filter):
279*333d2b36SAndroid Build Coastguard Worker                continue
280*333d2b36SAndroid Build Coastguard Worker            get_deps(nodes, dep, maxdepth-1, reverse, tag_filter)
281*333d2b36SAndroid Build Coastguard Worker
282*333d2b36SAndroid Build Coastguard Worker
283*333d2b36SAndroid Build Coastguard Workerdef new_module_formatter(args):
284*333d2b36SAndroid Build Coastguard Worker    def module_formatter(module):
285*333d2b36SAndroid Build Coastguard Worker        if not args.label:
286*333d2b36SAndroid Build Coastguard Worker            return []
287*333d2b36SAndroid Build Coastguard Worker        result = []
288*333d2b36SAndroid Build Coastguard Worker        text = json.dumps(module, default=lambda o: o.__dict__)
289*333d2b36SAndroid Build Coastguard Worker        for jq_filter in args.label:
290*333d2b36SAndroid Build Coastguard Worker            proc = subprocess.run(["jq", jq_filter],
291*333d2b36SAndroid Build Coastguard Worker                                  input=text, text=True, check=True, stdout=subprocess.PIPE)
292*333d2b36SAndroid Build Coastguard Worker            if proc.stdout:
293*333d2b36SAndroid Build Coastguard Worker                o = json.loads(proc.stdout)
294*333d2b36SAndroid Build Coastguard Worker                if type(o) == list:
295*333d2b36SAndroid Build Coastguard Worker                    for row in o:
296*333d2b36SAndroid Build Coastguard Worker                        if row:
297*333d2b36SAndroid Build Coastguard Worker                            result.append(row)
298*333d2b36SAndroid Build Coastguard Worker                elif type(o) == dict:
299*333d2b36SAndroid Build Coastguard Worker                    result.append(str(proc.stdout).strip())
300*333d2b36SAndroid Build Coastguard Worker                else:
301*333d2b36SAndroid Build Coastguard Worker                    if o:
302*333d2b36SAndroid Build Coastguard Worker                        result.append(str(o).strip())
303*333d2b36SAndroid Build Coastguard Worker        return result
304*333d2b36SAndroid Build Coastguard Worker    return module_formatter
305*333d2b36SAndroid Build Coastguard Worker
306*333d2b36SAndroid Build Coastguard Worker
307*333d2b36SAndroid Build Coastguard Workerclass BetweenCommand:
308*333d2b36SAndroid Build Coastguard Worker    help = "Print the module graph between two nodes."
309*333d2b36SAndroid Build Coastguard Worker
310*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
311*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("module", nargs=2,
312*333d2b36SAndroid Build Coastguard Worker                            help="the two modules")
313*333d2b36SAndroid Build Coastguard Worker        print_args(parser)
314*333d2b36SAndroid Build Coastguard Worker
315*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
316*333d2b36SAndroid Build Coastguard Worker        graph = load_graph()
317*333d2b36SAndroid Build Coastguard Worker        print_nodes(args, graph.find_paths(args.module[0], args.module[1], set(args.tag)),
318*333d2b36SAndroid Build Coastguard Worker                    new_module_formatter(args))
319*333d2b36SAndroid Build Coastguard Worker
320*333d2b36SAndroid Build Coastguard Worker
321*333d2b36SAndroid Build Coastguard Workerclass DepsCommand:
322*333d2b36SAndroid Build Coastguard Worker    help = "Print the module graph of dependencies of one or more modules"
323*333d2b36SAndroid Build Coastguard Worker
324*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
325*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("module", nargs="+",
326*333d2b36SAndroid Build Coastguard Worker                            help="Module to print dependencies of")
327*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("--reverse", action="store_true",
328*333d2b36SAndroid Build Coastguard Worker                            help="traverse reverse dependencies")
329*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("--depth", type=int, default=-1,
330*333d2b36SAndroid Build Coastguard Worker                            help="max depth of dependencies (can keep the graph size reasonable)")
331*333d2b36SAndroid Build Coastguard Worker        print_args(parser)
332*333d2b36SAndroid Build Coastguard Worker
333*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
334*333d2b36SAndroid Build Coastguard Worker        graph = load_graph()
335*333d2b36SAndroid Build Coastguard Worker        nodes = set()
336*333d2b36SAndroid Build Coastguard Worker        err = False
337*333d2b36SAndroid Build Coastguard Worker        for id in args.module:
338*333d2b36SAndroid Build Coastguard Worker            root = graph.nodes.get(id)
339*333d2b36SAndroid Build Coastguard Worker            if not root:
340*333d2b36SAndroid Build Coastguard Worker                sys.stderr.write(f"error: Can't find root: {id}\n")
341*333d2b36SAndroid Build Coastguard Worker                err = True
342*333d2b36SAndroid Build Coastguard Worker                continue
343*333d2b36SAndroid Build Coastguard Worker            get_deps(nodes, root, args.depth, args.reverse, set(args.tag))
344*333d2b36SAndroid Build Coastguard Worker        if err:
345*333d2b36SAndroid Build Coastguard Worker            sys.exit(1)
346*333d2b36SAndroid Build Coastguard Worker        print_nodes(args, nodes, new_module_formatter(args))
347*333d2b36SAndroid Build Coastguard Worker
348*333d2b36SAndroid Build Coastguard Worker
349*333d2b36SAndroid Build Coastguard Workerclass IdCommand:
350*333d2b36SAndroid Build Coastguard Worker    help = "Print the id (name + variant) of matching modules"
351*333d2b36SAndroid Build Coastguard Worker
352*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
353*333d2b36SAndroid Build Coastguard Worker        module_selection_args(parser)
354*333d2b36SAndroid Build Coastguard Worker
355*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
356*333d2b36SAndroid Build Coastguard Worker        for m in load_and_filter_modules(args):
357*333d2b36SAndroid Build Coastguard Worker            print(m.id)
358*333d2b36SAndroid Build Coastguard Worker
359*333d2b36SAndroid Build Coastguard Worker
360*333d2b36SAndroid Build Coastguard Workerclass JsonCommand:
361*333d2b36SAndroid Build Coastguard Worker    help = "Print metadata about modules in json format"
362*333d2b36SAndroid Build Coastguard Worker
363*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
364*333d2b36SAndroid Build Coastguard Worker        module_selection_args(parser)
365*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("--list", action="store_true",
366*333d2b36SAndroid Build Coastguard Worker                            help="Print the results in a json list. If not set and multiple"
367*333d2b36SAndroid Build Coastguard Worker                            + " modules are matched, the output won't be valid json.")
368*333d2b36SAndroid Build Coastguard Worker
369*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
370*333d2b36SAndroid Build Coastguard Worker        modules = load_and_filter_modules(args)
371*333d2b36SAndroid Build Coastguard Worker        if args.list:
372*333d2b36SAndroid Build Coastguard Worker            json.dump([m for m in modules], sys.stdout, indent=4, default=lambda o: o.__dict__)
373*333d2b36SAndroid Build Coastguard Worker        else:
374*333d2b36SAndroid Build Coastguard Worker            for m in modules:
375*333d2b36SAndroid Build Coastguard Worker                json.dump(m, sys.stdout, indent=4, default=lambda o: o.__dict__)
376*333d2b36SAndroid Build Coastguard Worker                print()
377*333d2b36SAndroid Build Coastguard Worker
378*333d2b36SAndroid Build Coastguard Worker
379*333d2b36SAndroid Build Coastguard Workerclass QueryCommand:
380*333d2b36SAndroid Build Coastguard Worker    help = "Query details about modules"
381*333d2b36SAndroid Build Coastguard Worker
382*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
383*333d2b36SAndroid Build Coastguard Worker        module_selection_args(parser)
384*333d2b36SAndroid Build Coastguard Worker
385*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
386*333d2b36SAndroid Build Coastguard Worker        for m in load_and_filter_modules(args):
387*333d2b36SAndroid Build Coastguard Worker            print(m.id)
388*333d2b36SAndroid Build Coastguard Worker            print(f"    type:     {m.type}")
389*333d2b36SAndroid Build Coastguard Worker            print(f"    location: {format_source_pos(m.source_file, m.source_line)}")
390*333d2b36SAndroid Build Coastguard Worker            for p in m.providers:
391*333d2b36SAndroid Build Coastguard Worker                print(f"    provider: {format_provider(p)}")
392*333d2b36SAndroid Build Coastguard Worker            for d in m.deps:
393*333d2b36SAndroid Build Coastguard Worker                print(f"    dep:      {d.id}")
394*333d2b36SAndroid Build Coastguard Worker
395*333d2b36SAndroid Build Coastguard Worker
396*333d2b36SAndroid Build Coastguard Workerclass StarCommand:
397*333d2b36SAndroid Build Coastguard Worker    help = "Print the dependencies and reverse dependencies of a module"
398*333d2b36SAndroid Build Coastguard Worker
399*333d2b36SAndroid Build Coastguard Worker    def args(self, parser):
400*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("module", nargs="+",
401*333d2b36SAndroid Build Coastguard Worker                            help="Module to print dependencies of")
402*333d2b36SAndroid Build Coastguard Worker        parser.add_argument("--depth", type=int, required=True,
403*333d2b36SAndroid Build Coastguard Worker                            help="max depth of dependencies")
404*333d2b36SAndroid Build Coastguard Worker        print_args(parser)
405*333d2b36SAndroid Build Coastguard Worker
406*333d2b36SAndroid Build Coastguard Worker    def run(self, args):
407*333d2b36SAndroid Build Coastguard Worker        graph = load_graph()
408*333d2b36SAndroid Build Coastguard Worker        nodes = set()
409*333d2b36SAndroid Build Coastguard Worker        err = False
410*333d2b36SAndroid Build Coastguard Worker        for id in args.module:
411*333d2b36SAndroid Build Coastguard Worker            root = graph.nodes.get(id)
412*333d2b36SAndroid Build Coastguard Worker            if not root:
413*333d2b36SAndroid Build Coastguard Worker                sys.stderr.write(f"error: Can't find root: {id}\n")
414*333d2b36SAndroid Build Coastguard Worker                err = True
415*333d2b36SAndroid Build Coastguard Worker                continue
416*333d2b36SAndroid Build Coastguard Worker            get_deps(nodes, root, args.depth, False, set(args.tag))
417*333d2b36SAndroid Build Coastguard Worker            nodes.remove(root) # Remove it so get_deps doesn't bail out
418*333d2b36SAndroid Build Coastguard Worker            get_deps(nodes, root, args.depth, True, set(args.tag))
419*333d2b36SAndroid Build Coastguard Worker        if err:
420*333d2b36SAndroid Build Coastguard Worker            sys.exit(1)
421*333d2b36SAndroid Build Coastguard Worker        print_nodes(args, nodes, new_module_formatter(args))
422*333d2b36SAndroid Build Coastguard Worker
423*333d2b36SAndroid Build Coastguard Worker
424*333d2b36SAndroid Build Coastguard Worker
425*333d2b36SAndroid Build Coastguard WorkerCOMMANDS = {
426*333d2b36SAndroid Build Coastguard Worker    "between": BetweenCommand(),
427*333d2b36SAndroid Build Coastguard Worker    "deps": DepsCommand(),
428*333d2b36SAndroid Build Coastguard Worker    "id": IdCommand(),
429*333d2b36SAndroid Build Coastguard Worker    "json": JsonCommand(),
430*333d2b36SAndroid Build Coastguard Worker    "query": QueryCommand(),
431*333d2b36SAndroid Build Coastguard Worker    "star": StarCommand(),
432*333d2b36SAndroid Build Coastguard Worker}
433*333d2b36SAndroid Build Coastguard Worker
434*333d2b36SAndroid Build Coastguard Worker
435*333d2b36SAndroid Build Coastguard Workerdef assert_env(name):
436*333d2b36SAndroid Build Coastguard Worker    val = os.getenv(name)
437*333d2b36SAndroid Build Coastguard Worker    if not val:
438*333d2b36SAndroid Build Coastguard Worker        sys.stderr.write(f"{name} not set. please make sure you've run lunch.")
439*333d2b36SAndroid Build Coastguard Worker    return val
440*333d2b36SAndroid Build Coastguard Worker
441*333d2b36SAndroid Build Coastguard WorkerANDROID_BUILD_TOP = assert_env("ANDROID_BUILD_TOP")
442*333d2b36SAndroid Build Coastguard Worker
443*333d2b36SAndroid Build Coastguard WorkerTARGET_PRODUCT = assert_env("TARGET_PRODUCT")
444*333d2b36SAndroid Build Coastguard WorkerOUT_DIR = os.getenv("OUT_DIR")
445*333d2b36SAndroid Build Coastguard Workerif not OUT_DIR:
446*333d2b36SAndroid Build Coastguard Worker    OUT_DIR = "out"
447*333d2b36SAndroid Build Coastguard Workerif OUT_DIR[0] != "/":
448*333d2b36SAndroid Build Coastguard Worker    OUT_DIR = pathlib.Path(ANDROID_BUILD_TOP).joinpath(OUT_DIR)
449*333d2b36SAndroid Build Coastguard WorkerSOONG_DEBUG_DATA_FILENAME = pathlib.Path(OUT_DIR).joinpath("soong/soong-debug-info.json")
450*333d2b36SAndroid Build Coastguard Worker
451*333d2b36SAndroid Build Coastguard Worker
452*333d2b36SAndroid Build Coastguard Workerdef main():
453*333d2b36SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
454*333d2b36SAndroid Build Coastguard Worker    subparsers = parser.add_subparsers(required=True, dest="command")
455*333d2b36SAndroid Build Coastguard Worker    for name in sorted(COMMANDS.keys()):
456*333d2b36SAndroid Build Coastguard Worker        command = COMMANDS[name]
457*333d2b36SAndroid Build Coastguard Worker        subparser = subparsers.add_parser(name, help=command.help)
458*333d2b36SAndroid Build Coastguard Worker        command.args(subparser)
459*333d2b36SAndroid Build Coastguard Worker    args = parser.parse_args()
460*333d2b36SAndroid Build Coastguard Worker    COMMANDS[args.command].run(args)
461*333d2b36SAndroid Build Coastguard Worker    sys.exit(0)
462*333d2b36SAndroid Build Coastguard Worker
463*333d2b36SAndroid Build Coastguard Worker
464*333d2b36SAndroid Build Coastguard Workerif __name__ == "__main__":
465*333d2b36SAndroid Build Coastguard Worker    main()
466*333d2b36SAndroid Build Coastguard Worker
467