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