blob: 874859ad9168fc43f628a0e77423cf85d85e6c40 [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 Faust33d71692023-10-18 22:50:26 +000064 # Use GENRULE_SANDBOXING=false so that we don't cause re-analysis later when we do the no-sandboxing build
65 _build_with_soong(["json-module-graph"], target_product, extra_env={"GENRULE_SANDBOXING": "false"})
Liz Kammer767fad42023-06-09 11:23:15 -040066
Cole Faustbbe2cc62023-07-12 17:55:28 -070067 with open(module_path) as f:
68 action_graph = json.load(f)
Liz Kammer767fad42023-06-09 11:23:15 -040069
70 module_to_outs = collections.defaultdict(set)
71 for mod in action_graph:
72 name = mod["Name"]
73 if name in modules:
74 for act in mod["Module"]["Actions"]:
Jason Macnak394f1b72023-06-15 09:28:26 -070075 if "}generate" in act["Desc"]:
Liz Kammer767fad42023-06-09 11:23:15 -040076 module_to_outs[name].update(act["Outputs"])
77 return module_to_outs
78
79
Cole Faustbbe2cc62023-07-12 17:55:28 -070080def _compare_outputs(module_to_outs, tempdir) -> dict[str, list[str]]:
Liz Kammer767fad42023-06-09 11:23:15 -040081 different_modules = collections.defaultdict(list)
82 for module, outs in module_to_outs.items():
83 for out in outs:
Cole Faustbbe2cc62023-07-12 17:55:28 -070084 try:
85 subprocess.check_output(["diff", os.path.join(tempdir, out), out])
86 except subprocess.CalledProcessError as e:
87 different_modules[module].append(e.stdout)
Liz Kammer767fad42023-06-09 11:23:15 -040088
Liz Kammer767fad42023-06-09 11:23:15 -040089 return different_modules
90
91
92def main():
93 parser = argparse.ArgumentParser()
94 parser.add_argument(
95 "--target_product",
96 "-t",
97 default="aosp_cf_arm64_phone",
98 help="optional, target product, always runs as eng",
99 )
100 parser.add_argument(
101 "modules",
102 nargs="+",
103 help="modules to compare builds with genrule sandboxing enabled/not",
104 )
105 parser.add_argument(
106 "--show-diff",
107 "-d",
108 action="store_true",
Liz Kammer767fad42023-06-09 11:23:15 -0400109 help="whether to display differing files",
110 )
Liz Kammer2fb361c2023-06-09 11:29:48 -0400111 parser.add_argument(
112 "--output-paths-only",
113 "-o",
114 action="store_true",
Liz Kammer2fb361c2023-06-09 11:29:48 -0400115 help="Whether to only return the output paths per module",
116 )
Liz Kammer767fad42023-06-09 11:23:15 -0400117 args = parser.parse_args()
Cole Faustbbe2cc62023-07-12 17:55:28 -0700118 os.chdir(get_top())
Liz Kammer767fad42023-06-09 11:23:15 -0400119
120 out_dir = os.environ.get("OUT_DIR", "out")
Liz Kammer767fad42023-06-09 11:23:15 -0400121
Cole Faustbbe2cc62023-07-12 17:55:28 -0700122 print("finding output files for the modules...")
123 module_to_outs = _find_outputs_for_modules(set(args.modules), out_dir, args.target_product)
Jason Macnak394f1b72023-06-15 09:28:26 -0700124 if not module_to_outs:
Cole Faustbbe2cc62023-07-12 17:55:28 -0700125 sys.exit("No outputs found")
Jason Macnak394f1b72023-06-15 09:28:26 -0700126
Liz Kammer2fb361c2023-06-09 11:29:48 -0400127 if args.output_paths_only:
128 for m, o in module_to_outs.items():
129 print(f"{m} outputs: {o}")
Cole Faustbbe2cc62023-07-12 17:55:28 -0700130 sys.exit(0)
Liz Kammer2fb361c2023-06-09 11:29:48 -0400131
Cole Faustbbe2cc62023-07-12 17:55:28 -0700132 all_outs = list(set.union(*module_to_outs.values()))
133
134 print("building without sandboxing...")
Cole Faust33d71692023-10-18 22:50:26 +0000135 _build_with_soong(all_outs, args.target_product, extra_env={"GENRULE_SANDBOXING": "false"})
Cole Faustbbe2cc62023-07-12 17:55:28 -0700136 with tempfile.TemporaryDirectory() as tempdir:
137 for f in all_outs:
138 subprocess.check_call(["cp", "--parents", f, tempdir])
139
140 print("building with sandboxing...")
141 _build_with_soong(
142 all_outs,
143 args.target_product,
144 # We've verified these build without sandboxing already, so do the sandboxing build
145 # with keep_going = True so that we can find all the genrules that fail to build with
146 # sandboxing.
147 keep_going = True,
148 extra_env={"GENRULE_SANDBOXING": "true"},
149 )
150
151 diffs = _compare_outputs(module_to_outs, tempdir)
152 if len(diffs) == 0:
153 print("All modules are correct")
154 elif args.show_diff:
155 for m, d in diffs.items():
156 print(f"Module {m} has diffs {d}")
157 else:
158 print(f"Modules {list(diffs.keys())} have diffs")
Liz Kammer767fad42023-06-09 11:23:15 -0400159
160
161if __name__ == "__main__":
162 main()