#!/usr/bin/env python3 import argparse import os import subprocess import sys def get_build_var(var): return subprocess.run(["build/soong/soong_ui.bash","--dumpvar-mode", var], check=True, capture_output=True, text=True).stdout.strip() def get_sources(modules): result = subprocess.run(["./prebuilts/build-tools/linux-x86/bin/ninja", "-f", "out/combined-" + os.environ["TARGET_PRODUCT"] + ".ninja", "-t", "inputs", "-d", ] + modules, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, check=False, text=True) if result.returncode != 0: sys.stderr.write(result.stdout) sys.exit(1) return set([f for f in result.stdout.split("\n") if not f.startswith("out/")]) def m_nothing(): result = subprocess.run(["build/soong/soong_ui.bash", "--build-mode", "--all-modules", "--dir=" + os.getcwd(), "nothing"], check=False, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, text=True) if result.returncode != 0: sys.stderr.write(result.stdout) sys.exit(1) def get_git_dirs(): text = subprocess.run(["repo","list"], check=True, capture_output=True, text=True).stdout return [line.split(" : ")[0] + "/" for line in text.split("\n")] def get_referenced_projects(git_dirs, files): # files must be sorted referenced_dirs = set() prev_dir = None for f in files: # Optimization is ~5x speedup for large sets of files if prev_dir: if f.startswith(prev_dir): referenced_dirs.add(d) continue for d in git_dirs: if f.startswith(d): referenced_dirs.add(d) prev_dir = d break return [d[0:-1] for d in referenced_dirs] def main(argv): # Argument parsing ap = argparse.ArgumentParser(description="List the required git projects for the given modules") ap.add_argument("--products", nargs="*", help="The TARGET_PRODUCT to check. If not provided just uses whatever has" + " already been built") ap.add_argument("--variants", nargs="*", help="The TARGET_BUILD_VARIANTS to check. If not provided just uses whatever has" + " already been built, or eng if --products is supplied") ap.add_argument("--modules", nargs="*", help="The build modules to check, or droid it not supplied") ap.add_argument("--why", nargs="*", help="Also print the input files used in these projects, or \"*\" for all") args = ap.parse_args(argv[1:]) modules = args.modules if args.modules else ["droid"] # Get the list of sources for all of the requested build combos if not args.products and not args.variants: sources = get_sources(modules) else: if not args.products: sys.stderr.write("Error: --products must be supplied if --variants is supplied") sys.exit(1) sources = set() build_num = 1 for product in args.products: os.environ["TARGET_PRODUCT"] = product variants = args.variants if args.variants else ["user", "userdebug", "eng"] for variant in variants: sys.stderr.write(f"Analyzing build {build_num} of {len(args.products)*len(variants)}\r") os.environ["TARGET_BUILD_VARIANT"] = variant m_nothing() sources.update(get_sources(modules)) build_num += 1 sys.stderr.write("\n\n") sources = sorted(sources) # Print the list of git directories that has one or more of the sources in it for project in sorted(get_referenced_projects(get_git_dirs(), sources)): print(project) if args.why: if "*" in args.why or project in args.why: prefix = project + "/" for f in sources: if f.startswith(prefix): print(" " + f) if __name__ == "__main__": sys.exit(main(sys.argv)) # vim: set ts=2 sw=2 sts=2 expandtab nocindent tw=100: