1#!/usr/bin/env python3 2 3import argparse 4import os 5import subprocess 6import sys 7 8def get_build_var(var): 9 return subprocess.run(["build/soong/soong_ui.bash","--dumpvar-mode", var], 10 check=True, capture_output=True, text=True).stdout.strip() 11 12 13def get_sources(modules): 14 result = subprocess.run(["./prebuilts/build-tools/linux-x86/bin/ninja", "-f", 15 "out/combined-" + os.environ["TARGET_PRODUCT"] + ".ninja", 16 "-t", "inputs", "-d", ] + modules, 17 stderr=subprocess.STDOUT, stdout=subprocess.PIPE, check=False, text=True) 18 if result.returncode != 0: 19 sys.stderr.write(result.stdout) 20 sys.exit(1) 21 return set([f for f in result.stdout.split("\n") if not f.startswith("out/")]) 22 23 24def m_nothing(): 25 result = subprocess.run(["build/soong/soong_ui.bash", "--build-mode", "--all-modules", 26 "--dir=" + os.getcwd(), "nothing"], 27 check=False, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True) 28 if result.returncode != 0: 29 sys.stderr.write(result.stdout) 30 sys.exit(1) 31 32 33def get_git_dirs(): 34 text = subprocess.run(["repo","list"], check=True, capture_output=True, text=True).stdout 35 return [line.split(" : ")[0] + "/" for line in text.split("\n")] 36 37 38def get_referenced_projects(git_dirs, files): 39 # files must be sorted 40 referenced_dirs = set() 41 prev_dir = None 42 for f in files: 43 # Optimization is ~5x speedup for large sets of files 44 if prev_dir: 45 if f.startswith(prev_dir): 46 referenced_dirs.add(d) 47 continue 48 for d in git_dirs: 49 if f.startswith(d): 50 referenced_dirs.add(d) 51 prev_dir = d 52 break 53 return [d[0:-1] for d in referenced_dirs] 54 55 56def main(argv): 57 # Argument parsing 58 ap = argparse.ArgumentParser(description="List the required git projects for the given modules") 59 ap.add_argument("--products", nargs="*", 60 help="The TARGET_PRODUCT to check. If not provided just uses whatever has" 61 + " already been built") 62 ap.add_argument("--variants", nargs="*", 63 help="The TARGET_BUILD_VARIANTS to check. If not provided just uses whatever has" 64 + " already been built, or eng if --products is supplied") 65 ap.add_argument("--modules", nargs="*", 66 help="The build modules to check, or droid it not supplied") 67 ap.add_argument("--why", nargs="*", 68 help="Also print the input files used in these projects, or \"*\" for all") 69 args = ap.parse_args(argv[1:]) 70 71 modules = args.modules if args.modules else ["droid"] 72 73 # Get the list of sources for all of the requested build combos 74 if not args.products and not args.variants: 75 sources = get_sources(modules) 76 else: 77 if not args.products: 78 sys.stderr.write("Error: --products must be supplied if --variants is supplied") 79 sys.exit(1) 80 sources = set() 81 build_num = 1 82 for product in args.products: 83 os.environ["TARGET_PRODUCT"] = product 84 variants = args.variants if args.variants else ["user", "userdebug", "eng"] 85 for variant in variants: 86 sys.stderr.write(f"Analyzing build {build_num} of {len(args.products)*len(variants)}\r") 87 os.environ["TARGET_BUILD_VARIANT"] = variant 88 m_nothing() 89 sources.update(get_sources(modules)) 90 build_num += 1 91 sys.stderr.write("\n\n") 92 93 sources = sorted(sources) 94 95 # Print the list of git directories that has one or more of the sources in it 96 for project in sorted(get_referenced_projects(get_git_dirs(), sources)): 97 print(project) 98 if args.why: 99 if "*" in args.why or project in args.why: 100 prefix = project + "/" 101 for f in sources: 102 if f.startswith(prefix): 103 print(" " + f) 104 105 106if __name__ == "__main__": 107 sys.exit(main(sys.argv)) 108 109 110# vim: set ts=2 sw=2 sts=2 expandtab nocindent tw=100: 111