blob: 56a07d5a61306deeba13d4379308e2a2e84d5245 [file] [log] [blame]
Alex Light4fa48352020-12-29 13:45:04 -08001#!/usr/bin/python3
2#
3# Copyright (C) 2021 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
17#
18# This script runs dex2oat on the host to compile a provided JAR or APK.
19#
20
21import argparse
Alex Light34aebb62021-03-30 13:33:46 -070022import itertools
Alex Light4fa48352020-12-29 13:45:04 -080023import shlex
24import subprocess
25import os
26import os.path
27
Alex Light4fa48352020-12-29 13:45:04 -080028def run_print(lst):
29 return " ".join(map(shlex.quote, lst))
30
31
32def parse_args():
33 parser = argparse.ArgumentParser(
34 description="compile dex or jar files",
35 epilog="Unrecognized options are passed on to dex2oat unmodified.")
36 parser.add_argument(
37 "--dex2oat",
38 action="store",
39 default=os.path.expandvars("$ANDROID_HOST_OUT/bin/dex2oatd64"),
40 help="selects the dex2oat to use.")
41 parser.add_argument(
42 "--debug",
43 action="store_true",
44 default=False,
45 help="launches dex2oatd with lldb-server g :5039. Connect using vscode or remote lldb"
46 )
47 parser.add_argument(
48 "--profman",
49 action="store",
50 default=os.path.expandvars("$ANDROID_HOST_OUT/bin/profmand"),
51 help="selects the profman to use.")
52 parser.add_argument(
53 "--debug-profman",
54 action="store_true",
55 default=False,
56 help="launches profman with lldb-server g :5039. Connect using vscode or remote lldb"
57 )
58 profs = parser.add_mutually_exclusive_group()
59 profs.add_argument(
60 "--profile-file",
61 action="store",
62 help="Use this profile file. Probably want to pass --compiler-filter=speed-profile with this."
63 )
64 profs.add_argument(
65 "--profile-line",
66 action="append",
67 default=[],
68 help="functions to add to a profile. Probably want to pass --compiler-filter=speed-profile with this. All functions are marked as 'hot'. Use --profile-file for more control."
69 )
70 parser.add_argument(
Alex Light34aebb62021-03-30 13:33:46 -070071 "--add-bcp",
72 action="append",
73 default=[],
74 nargs=2,
75 metavar=("BCP_FILE", "BCP_LOCATION"),
76 help="File and location to add to the boot-class-path. Note no deduplication is attempted."
77 )
78 parser.add_argument(
Alex Light4fa48352020-12-29 13:45:04 -080079 "--arch",
80 action="store",
81 choices=["arm", "arm64", "x86", "x86_64", "host64", "host32"],
82 default="host64",
83 help="architecture to compile for. Defaults to host64")
84 parser.add_argument(
85 "--odex-file",
86 action="store",
87 help="odex file to write. File discarded if not set",
88 default=None)
89 parser.add_argument(
90 "--save-profile",
91 action="store",
92 type=argparse.FileType("w"),
93 default=None,
94 help="File path to store the profile to")
95 parser.add_argument(
96 "dex_files", help="dex/jar files", nargs="+", metavar="DEX")
97 return parser.parse_known_args()
98
99
Alex Light34aebb62021-03-30 13:33:46 -0700100def get_bcp_runtime_args(additions, image, arch):
101 add_files = map(lambda a: a[0], additions)
102 add_locs = map(lambda a: a[1], additions)
Alex Light4fa48352020-12-29 13:45:04 -0800103 if arch != "host32" and arch != "host64":
104 args = [
105 "art/tools/host_bcp.sh",
106 os.path.expandvars(
107 "${{OUT}}/system/framework/oat/{}/services.odex".format(arch)),
108 "--use-first-dir"
109 ]
110 print("Running: {}".format(run_print(args)))
111 print("=START=======================================")
112 res = subprocess.run(args, capture_output=True, text=True)
113 print("=END=========================================")
Alex Light34aebb62021-03-30 13:33:46 -0700114 if res.returncode != 0:
115 print("Falling back to com.android.art BCP")
116 args = [
117 "art/tools/host_bcp.sh",
118 os.path.expandvars(
119 "${{OUT}}/apex/com.android.art.debug/javalib/{}/boot.oat".format(arch)),
120 "--use-first-dir"
121 ]
122 print("Running: {}".format(run_print(args)))
123 print("=START=======================================")
124 res = subprocess.run(args, capture_output=True, text=True)
125 print("=END=========================================")
126 res.check_returncode()
127 segments = res.stdout.split()
128 def extend_bcp(segment: str):
129 # TODO We should make the bcp have absolute paths.
130 if segment.startswith("-Xbootclasspath:"):
131 return ":".join(itertools.chain((segment,), add_files))
132 elif segment.startswith("-Xbootclasspath-locations:"):
133 return ":".join(itertools.chain((segment,), add_locs))
134 else:
135 return segment
136 return list(map(extend_bcp, segments))
Alex Light4fa48352020-12-29 13:45:04 -0800137 else:
138 # Host we just use the bcp locations for both.
139 res = open(
140 os.path.expandvars(
141 "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/{}/boot.oat".format(
142 "x86" if arch == "host32" else "x86_64")), "rb").read()
143 bcp_tag = b"bootclasspath\0"
144 bcp_start = res.find(bcp_tag) + len(bcp_tag)
145 bcp = res[bcp_start:bcp_start + res[bcp_start:].find(b"\0")]
Alex Light34aebb62021-03-30 13:33:46 -0700146 img_bcp = bcp.decode()
147 # TODO We should make the str_bcp have absolute paths.
148 str_bcp = ":".join(itertools.chain((img_bcp,), add_files))
149 str_bcp_loc = ":".join(itertools.chain((img_bcp,), add_locs))
Alex Light4fa48352020-12-29 13:45:04 -0800150 return [
Alex Light34aebb62021-03-30 13:33:46 -0700151 "--runtime-arg", "-Xbootclasspath:{}".format(str_bcp),
152 "--runtime-arg", "-Xbootclasspath-locations:{}".format(str_bcp_loc)
Alex Light4fa48352020-12-29 13:45:04 -0800153 ]
154
155
156def fdfile(fd):
157 return "/proc/{}/fd/{}".format(os.getpid(), fd)
158
159
160def get_profile_args(args, location_base):
161 """Handle all the profile file options."""
162 if args.profile_file is None and len(args.profile_line) == 0:
163 return []
Alex Light34aebb62021-03-30 13:33:46 -0700164 if args.profile_file:
165 with open(args.profile_file, "rb") as prof:
166 prof_magic = prof.read(4)
167 if prof_magic == b'pro\0':
168 # Looks like the profile-file is a binary profile. Just use it directly
169 return ['--profile-file={}'.format(args.profile_file)]
Alex Light4fa48352020-12-29 13:45:04 -0800170 if args.debug_profman:
171 profman_args = ["lldb-server", "g", ":5039", "--", args.profman]
172 else:
173 profman_args = [args.profman]
174 if args.save_profile:
175 prof_out_fd = args.save_profile.fileno()
176 os.set_inheritable(prof_out_fd, True)
177 else:
178 prof_out_fd = os.memfd_create("reference_prof", flags=0)
179 if args.debug_profman:
180 profman_args.append("--reference-profile-file={}".format(
181 fdfile(prof_out_fd)))
182 else:
183 profman_args.append("--reference-profile-file-fd={}".format(prof_out_fd))
184 if args.profile_file:
185 profman_args.append("--create-profile-from={}".format(args.profile_file))
186 else:
187 prof_in_fd = os.memfd_create("input_prof", flags=0)
188 # Why on earth does fdopen take control of the fd and not mention it in the docs.
189 with os.fdopen(os.dup(prof_in_fd), "w") as prof_in:
190 for l in args.profile_line:
191 print(l, file=prof_in)
192 profman_args.append("--create-profile-from={}".format(fdfile(prof_in_fd)))
193 for f in args.dex_files:
194 profman_args.append("--apk={}".format(f))
195 profman_args.append("--dex-location={}".format(
196 os.path.join(location_base, os.path.basename(f))))
197 print("Running: {}".format(run_print(profman_args)))
198 print("=START=======================================")
199 subprocess.run(profman_args, close_fds=False).check_returncode()
200 print("=END=========================================")
201 if args.debug:
202 return ["--profile-file={}".format(fdfile(prof_out_fd))]
203 else:
204 return ["--profile-file={}".format(fdfile(prof_out_fd))]
205
206
207def main():
208 args, extra = parse_args()
209 if args.arch == "host32" or args.arch == "host64":
210 location_base = os.path.expandvars("${ANDROID_HOST_OUT}/framework/")
211 real_arch = "x86" if args.arch == "host32" else "x86_64"
212 boot_image = os.path.expandvars(
213 "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/boot.art")
214 android_root = os.path.expandvars("$ANDROID_HOST_OUT")
215 for f in args.dex_files:
216 extra.append("--dex-location={}".format(
217 os.path.join(location_base, os.path.basename(f))))
218 extra.append("--dex-file={}".format(f))
219 else:
220 location_base = "/system/framework"
221 real_arch = args.arch
222 boot_image = os.path.expandvars(":".join([
Alex Light34aebb62021-03-30 13:33:46 -0700223 "${OUT}/apex/art_boot_images/javalib/boot.art",
Alex Light4fa48352020-12-29 13:45:04 -0800224 "${OUT}/system/framework/boot-framework.art"
225 ]))
226 android_root = os.path.expandvars("$OUT/system")
227 for f in args.dex_files:
228 extra.append("--dex-location={}".format(
229 os.path.join(location_base, os.path.basename(f))))
230 extra.append("--dex-file={}".format(f))
Alex Light34aebb62021-03-30 13:33:46 -0700231 extra += get_bcp_runtime_args(args.add_bcp, boot_image, args.arch)
Alex Light4fa48352020-12-29 13:45:04 -0800232 extra += get_profile_args(args, location_base)
233 extra.append("--instruction-set={}".format(real_arch))
234 extra.append("--boot-image={}".format(boot_image))
235 extra.append("--android-root={}".format(android_root))
236 extra += ["--runtime-arg", "-Xms64m", "--runtime-arg", "-Xmx512m"]
237 if args.odex_file is not None:
238 extra.append("--oat-file={}".format(args.odex_file))
239 else:
240 if args.debug:
241 raise Exception("Debug requires a real output file. :(")
242 extra.append("--oat-fd={}".format(os.memfd_create("odex_fd", flags=0)))
243 extra.append("--oat-location={}".format("/tmp/odex_fd.odex"))
244 extra.append("--output-vdex-fd={}".format(
245 os.memfd_create("vdex_fd", flags=0)))
246 pre_args = []
247 if args.debug:
248 pre_args = ["lldb-server", "g", ":5039", "--"]
249 pre_args.append(args.dex2oat)
250 print("Running: {}".format(run_print(pre_args + extra)))
251 print("=START=======================================")
252 subprocess.run(pre_args + extra, close_fds=False).check_returncode()
253 print("=END=========================================")
254
255
256if __name__ == "__main__":
257 main()