aboutsummaryrefslogtreecommitdiff
path: root/tools/whichgit
blob: b0bf2e42f8b159bc7a7acd624d053128dbbe53e8 (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
#!/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: