blob: 0cebc2aa0d110a7e3cb6a8b43d5b7e455d56f4fd [file] [log] [blame]
Liz Kammer767fad42023-06-09 11:23:15 -04001#!/usr/bin/env python3
2
3# Copyright (C) 2023 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18import collections
19import json
Cole Faustbbe2cc62023-07-12 17:55:28 -070020import os
Liz Kammer767fad42023-06-09 11:23:15 -040021import subprocess
Cole Faustbbe2cc62023-07-12 17:55:28 -070022import sys
Liz Kammer767fad42023-06-09 11:23:15 -040023import tempfile
24
Cole Faustbbe2cc62023-07-12 17:55:28 -070025def get_top() -> str:
26 path = '.'
27 while not os.path.isfile(os.path.join(path, 'build/soong/tests/genrule_sandbox_test.py')):
28 if os.path.abspath(path) == '/':
29 sys.exit('Could not find android source tree root.')
30 path = os.path.join(path, '..')
31 return os.path.abspath(path)
Liz Kammer767fad42023-06-09 11:23:15 -040032
Cole Faustbbe2cc62023-07-12 17:55:28 -070033def _build_with_soong(targets, target_product, *, keep_going = False, extra_env={}):
Liz Kammer767fad42023-06-09 11:23:15 -040034 env = {
Cole Faustbbe2cc62023-07-12 17:55:28 -070035 **os.environ,
Liz Kammer767fad42023-06-09 11:23:15 -040036 "TARGET_PRODUCT": target_product,
37 "TARGET_BUILD_VARIANT": "userdebug",
38 }
Liz Kammer767fad42023-06-09 11:23:15 -040039 env.update(extra_env)
40 args = [
41 "build/soong/soong_ui.bash",
42 "--make-mode",
43 "--skip-soong-tests",
44 ]
Cole Faustbbe2cc62023-07-12 17:55:28 -070045 if keep_going:
46 args.append("-k")
Liz Kammer767fad42023-06-09 11:23:15 -040047 args.extend(targets)
48 try:
Cole Faustbbe2cc62023-07-12 17:55:28 -070049 subprocess.check_output(
Liz Kammer767fad42023-06-09 11:23:15 -040050 args,
Liz Kammer767fad42023-06-09 11:23:15 -040051 env=env,
52 )
53 except subprocess.CalledProcessError as e:
54 print(e)
55 print(e.stdout)
56 print(e.stderr)
57 exit(1)
58
59
60def _find_outputs_for_modules(modules, out_dir, target_product):
Cole Faustbbe2cc62023-07-12 17:55:28 -070061 module_path = os.path.join(out_dir, "soong", "module-actions.json")
Liz Kammer767fad42023-06-09 11:23:15 -040062
63 if not os.path.exists(module_path):
Cole Faustbbe2cc62023-07-12 17:55:28 -070064 _build_with_soong(["json-module-graph"], target_product)
Liz Kammer767fad42023-06-09 11:23:15 -040065
Cole Faustbbe2cc62023-07-12 17:55:28 -070066 with open(module_path) as f:
67 action_graph = json.load(f)
Liz Kammer767fad42023-06-09 11:23:15 -040068
69 module_to_outs = collections.defaultdict(set)
70 for mod in action_graph:
71 name = mod["Name"]
72 if name in modules:
73 for act in mod["Module"]["Actions"]:
Jason Macnak394f1b72023-06-15 09:28:26 -070074 if "}generate" in act["Desc"]:
Liz Kammer767fad42023-06-09 11:23:15 -040075 module_to_outs[name].update(act["Outputs"])
76 return module_to_outs
77
78
Cole Faustbbe2cc62023-07-12 17:55:28 -070079def _compare_outputs(module_to_outs, tempdir) -> dict[str, list[str]]:
Liz Kammer767fad42023-06-09 11:23:15 -040080 different_modules = collections.defaultdict(list)
81 for module, outs in module_to_outs.items():
82 for out in outs:
Cole Faustbbe2cc62023-07-12 17:55:28 -070083 try:
84 subprocess.check_output(["diff", os.path.join(tempdir, out), out])
85 except subprocess.CalledProcessError as e:
86 different_modules[module].append(e.stdout)
Liz Kammer767fad42023-06-09 11:23:15 -040087
Liz Kammer767fad42023-06-09 11:23:15 -040088 return different_modules
89
90
91def main():
92 parser = argparse.ArgumentParser()
93 parser.add_argument(
94 "--target_product",
95 "-t",
96 default="aosp_cf_arm64_phone",
97 help="optional, target product, always runs as eng",
98 )
99 parser.add_argument(
100 "modules",
101 nargs="+",
102 help="modules to compare builds with genrule sandboxing enabled/not",
103 )
104 parser.add_argument(
105 "--show-diff",
106 "-d",
107 action="store_true",
Liz Kammer767fad42023-06-09 11:23:15 -0400108 help="whether to display differing files",
109 )
Liz Kammer2fb361c2023-06-09 11:29:48 -0400110 parser.add_argument(
111 "--output-paths-only",
112 "-o",
113 action="store_true",
Liz Kammer2fb361c2023-06-09 11:29:48 -0400114 help="Whether to only return the output paths per module",
115 )
Liz Kammer767fad42023-06-09 11:23:15 -0400116 args = parser.parse_args()
Cole Faustbbe2cc62023-07-12 17:55:28 -0700117 os.chdir(get_top())
Liz Kammer767fad42023-06-09 11:23:15 -0400118
119 out_dir = os.environ.get("OUT_DIR", "out")
Liz Kammer767fad42023-06-09 11:23:15 -0400120
Cole Faustbbe2cc62023-07-12 17:55:28 -0700121 print("finding output files for the modules...")
122 module_to_outs = _find_outputs_for_modules(set(args.modules), out_dir, args.target_product)
Jason Macnak394f1b72023-06-15 09:28:26 -0700123 if not module_to_outs:
Cole Faustbbe2cc62023-07-12 17:55:28 -0700124 sys.exit("No outputs found")
Jason Macnak394f1b72023-06-15 09:28:26 -0700125
Liz Kammer2fb361c2023-06-09 11:29:48 -0400126 if args.output_paths_only:
127 for m, o in module_to_outs.items():
128 print(f"{m} outputs: {o}")
Cole Faustbbe2cc62023-07-12 17:55:28 -0700129 sys.exit(0)
Liz Kammer2fb361c2023-06-09 11:29:48 -0400130
Cole Faustbbe2cc62023-07-12 17:55:28 -0700131 all_outs = list(set.union(*module_to_outs.values()))
132
133 print("building without sandboxing...")
134 _build_with_soong(all_outs, args.target_product)
135 with tempfile.TemporaryDirectory() as tempdir:
136 for f in all_outs:
137 subprocess.check_call(["cp", "--parents", f, tempdir])
138
139 print("building with sandboxing...")
140 _build_with_soong(
141 all_outs,
142 args.target_product,
143 # We've verified these build without sandboxing already, so do the sandboxing build
144 # with keep_going = True so that we can find all the genrules that fail to build with
145 # sandboxing.
146 keep_going = True,
147 extra_env={"GENRULE_SANDBOXING": "true"},
148 )
149
150 diffs = _compare_outputs(module_to_outs, tempdir)
151 if len(diffs) == 0:
152 print("All modules are correct")
153 elif args.show_diff:
154 for m, d in diffs.items():
155 print(f"Module {m} has diffs {d}")
156 else:
157 print(f"Modules {list(diffs.keys())} have diffs")
Liz Kammer767fad42023-06-09 11:23:15 -0400158
159
160if __name__ == "__main__":
161 main()