xref: /aosp_15_r20/art/tools/compile-jar.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker#
18*795d594fSAndroid Build Coastguard Worker# This script runs dex2oat on the host to compile a provided JAR or APK.
19*795d594fSAndroid Build Coastguard Worker#
20*795d594fSAndroid Build Coastguard Worker
21*795d594fSAndroid Build Coastguard Workerimport argparse
22*795d594fSAndroid Build Coastguard Workerimport itertools
23*795d594fSAndroid Build Coastguard Workerimport shlex
24*795d594fSAndroid Build Coastguard Workerimport subprocess
25*795d594fSAndroid Build Coastguard Workerimport os
26*795d594fSAndroid Build Coastguard Workerimport os.path
27*795d594fSAndroid Build Coastguard Worker
28*795d594fSAndroid Build Coastguard Workerdef run_print(lst):
29*795d594fSAndroid Build Coastguard Worker  return " ".join(map(shlex.quote, lst))
30*795d594fSAndroid Build Coastguard Worker
31*795d594fSAndroid Build Coastguard Worker
32*795d594fSAndroid Build Coastguard Workerdef parse_args():
33*795d594fSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
34*795d594fSAndroid Build Coastguard Worker      description="compile dex or jar files",
35*795d594fSAndroid Build Coastguard Worker      epilog="Unrecognized options are passed on to dex2oat unmodified.")
36*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
37*795d594fSAndroid Build Coastguard Worker      "--dex2oat",
38*795d594fSAndroid Build Coastguard Worker      action="store",
39*795d594fSAndroid Build Coastguard Worker      default=os.path.expandvars("$ANDROID_HOST_OUT/bin/dex2oatd64"),
40*795d594fSAndroid Build Coastguard Worker      help="selects the dex2oat to use.")
41*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
42*795d594fSAndroid Build Coastguard Worker      "--debug",
43*795d594fSAndroid Build Coastguard Worker      action="store_true",
44*795d594fSAndroid Build Coastguard Worker      default=False,
45*795d594fSAndroid Build Coastguard Worker      help="launches dex2oatd with lldb-server g :5039. Connect using vscode or remote lldb"
46*795d594fSAndroid Build Coastguard Worker  )
47*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
48*795d594fSAndroid Build Coastguard Worker      "--profman",
49*795d594fSAndroid Build Coastguard Worker      action="store",
50*795d594fSAndroid Build Coastguard Worker      default=os.path.expandvars("$ANDROID_HOST_OUT/bin/profmand"),
51*795d594fSAndroid Build Coastguard Worker      help="selects the profman to use.")
52*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
53*795d594fSAndroid Build Coastguard Worker      "--debug-profman",
54*795d594fSAndroid Build Coastguard Worker      action="store_true",
55*795d594fSAndroid Build Coastguard Worker      default=False,
56*795d594fSAndroid Build Coastguard Worker      help="launches profman with lldb-server g :5039. Connect using vscode or remote lldb"
57*795d594fSAndroid Build Coastguard Worker  )
58*795d594fSAndroid Build Coastguard Worker  profs = parser.add_mutually_exclusive_group()
59*795d594fSAndroid Build Coastguard Worker  profs.add_argument(
60*795d594fSAndroid Build Coastguard Worker      "--profile-file",
61*795d594fSAndroid Build Coastguard Worker      action="store",
62*795d594fSAndroid Build Coastguard Worker      help="Use this profile file. Probably want to pass --compiler-filter=speed-profile with this."
63*795d594fSAndroid Build Coastguard Worker  )
64*795d594fSAndroid Build Coastguard Worker  profs.add_argument(
65*795d594fSAndroid Build Coastguard Worker      "--profile-line",
66*795d594fSAndroid Build Coastguard Worker      action="append",
67*795d594fSAndroid Build Coastguard Worker      default=[],
68*795d594fSAndroid Build Coastguard Worker      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*795d594fSAndroid Build Coastguard Worker  )
70*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
71*795d594fSAndroid Build Coastguard Worker      "--add-bcp",
72*795d594fSAndroid Build Coastguard Worker      action="append",
73*795d594fSAndroid Build Coastguard Worker      default=[],
74*795d594fSAndroid Build Coastguard Worker      nargs=2,
75*795d594fSAndroid Build Coastguard Worker      metavar=("BCP_FILE", "BCP_LOCATION"),
76*795d594fSAndroid Build Coastguard Worker      help="File and location to add to the boot-class-path. Note no deduplication is attempted."
77*795d594fSAndroid Build Coastguard Worker  )
78*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
79*795d594fSAndroid Build Coastguard Worker      "--arch",
80*795d594fSAndroid Build Coastguard Worker      action="store",
81*795d594fSAndroid Build Coastguard Worker      choices=["arm", "arm64", "x86", "x86_64", "host64", "host32"],
82*795d594fSAndroid Build Coastguard Worker      default="host64",
83*795d594fSAndroid Build Coastguard Worker      help="architecture to compile for. Defaults to host64")
84*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
85*795d594fSAndroid Build Coastguard Worker      "--odex-file",
86*795d594fSAndroid Build Coastguard Worker      action="store",
87*795d594fSAndroid Build Coastguard Worker      help="odex file to write. File discarded if not set",
88*795d594fSAndroid Build Coastguard Worker      default=None)
89*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
90*795d594fSAndroid Build Coastguard Worker      "--save-profile",
91*795d594fSAndroid Build Coastguard Worker      action="store",
92*795d594fSAndroid Build Coastguard Worker      type=argparse.FileType("w"),
93*795d594fSAndroid Build Coastguard Worker      default=None,
94*795d594fSAndroid Build Coastguard Worker      help="File path to store the profile to")
95*795d594fSAndroid Build Coastguard Worker  parser.add_argument(
96*795d594fSAndroid Build Coastguard Worker      "dex_files", help="dex/jar files", nargs="+", metavar="DEX")
97*795d594fSAndroid Build Coastguard Worker  return parser.parse_known_args()
98*795d594fSAndroid Build Coastguard Worker
99*795d594fSAndroid Build Coastguard Worker
100*795d594fSAndroid Build Coastguard Workerdef get_bcp_runtime_args(additions, image, arch):
101*795d594fSAndroid Build Coastguard Worker  add_files = map(lambda a: a[0], additions)
102*795d594fSAndroid Build Coastguard Worker  add_locs = map(lambda a: a[1], additions)
103*795d594fSAndroid Build Coastguard Worker  if arch != "host32" and arch != "host64":
104*795d594fSAndroid Build Coastguard Worker    args = [
105*795d594fSAndroid Build Coastguard Worker        "art/tools/host_bcp.sh",
106*795d594fSAndroid Build Coastguard Worker        os.path.expandvars(
107*795d594fSAndroid Build Coastguard Worker            "${{OUT}}/system/framework/oat/{}/services.odex".format(arch)),
108*795d594fSAndroid Build Coastguard Worker    ]
109*795d594fSAndroid Build Coastguard Worker    print("Running: {}".format(run_print(args)))
110*795d594fSAndroid Build Coastguard Worker    print("=START=======================================")
111*795d594fSAndroid Build Coastguard Worker    res = subprocess.run(args, capture_output=True, text=True)
112*795d594fSAndroid Build Coastguard Worker    print("=END=========================================")
113*795d594fSAndroid Build Coastguard Worker    if res.returncode != 0:
114*795d594fSAndroid Build Coastguard Worker      print("Falling back to ART boot image: {}".format(res))
115*795d594fSAndroid Build Coastguard Worker      args = [
116*795d594fSAndroid Build Coastguard Worker          "art/tools/host_bcp.sh",
117*795d594fSAndroid Build Coastguard Worker          os.path.expandvars(
118*795d594fSAndroid Build Coastguard Worker              "${{OUT}}/apex/art_boot_images/javalib/{}/boot.oat".format(arch)),
119*795d594fSAndroid Build Coastguard Worker      ]
120*795d594fSAndroid Build Coastguard Worker      print("Running: {}".format(run_print(args)))
121*795d594fSAndroid Build Coastguard Worker      print("=START=======================================")
122*795d594fSAndroid Build Coastguard Worker      res = subprocess.run(args, capture_output=True, text=True)
123*795d594fSAndroid Build Coastguard Worker      print("=END=========================================")
124*795d594fSAndroid Build Coastguard Worker      res.check_returncode()
125*795d594fSAndroid Build Coastguard Worker    segments = res.stdout.split()
126*795d594fSAndroid Build Coastguard Worker    def extend_bcp(segment: str):
127*795d594fSAndroid Build Coastguard Worker      # TODO We should make the bcp have absolute paths.
128*795d594fSAndroid Build Coastguard Worker      if segment.startswith("-Xbootclasspath:"):
129*795d594fSAndroid Build Coastguard Worker        return ":".join(itertools.chain((segment,), add_files))
130*795d594fSAndroid Build Coastguard Worker      elif segment.startswith("-Xbootclasspath-locations:"):
131*795d594fSAndroid Build Coastguard Worker        return ":".join(itertools.chain((segment,), add_locs))
132*795d594fSAndroid Build Coastguard Worker      else:
133*795d594fSAndroid Build Coastguard Worker        return segment
134*795d594fSAndroid Build Coastguard Worker    return list(map(extend_bcp, segments))
135*795d594fSAndroid Build Coastguard Worker  else:
136*795d594fSAndroid Build Coastguard Worker    # Host we just use the bcp locations for both.
137*795d594fSAndroid Build Coastguard Worker    res = open(
138*795d594fSAndroid Build Coastguard Worker        os.path.expandvars(
139*795d594fSAndroid Build Coastguard Worker            "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/{}/boot.oat".format(
140*795d594fSAndroid Build Coastguard Worker                "x86" if arch == "host32" else "x86_64")), "rb").read()
141*795d594fSAndroid Build Coastguard Worker    bcp_tag = b"bootclasspath\0"
142*795d594fSAndroid Build Coastguard Worker    bcp_start = res.find(bcp_tag) + len(bcp_tag)
143*795d594fSAndroid Build Coastguard Worker    bcp = res[bcp_start:bcp_start + res[bcp_start:].find(b"\0")]
144*795d594fSAndroid Build Coastguard Worker    img_bcp = bcp.decode()
145*795d594fSAndroid Build Coastguard Worker    # TODO We should make the str_bcp have absolute paths.
146*795d594fSAndroid Build Coastguard Worker    str_bcp = ":".join(itertools.chain((img_bcp,), add_files))
147*795d594fSAndroid Build Coastguard Worker    str_bcp_loc = ":".join(itertools.chain((img_bcp,), add_locs))
148*795d594fSAndroid Build Coastguard Worker    return [
149*795d594fSAndroid Build Coastguard Worker        "--runtime-arg", "-Xbootclasspath:{}".format(str_bcp),
150*795d594fSAndroid Build Coastguard Worker        "--runtime-arg", "-Xbootclasspath-locations:{}".format(str_bcp_loc)
151*795d594fSAndroid Build Coastguard Worker    ]
152*795d594fSAndroid Build Coastguard Worker
153*795d594fSAndroid Build Coastguard Worker
154*795d594fSAndroid Build Coastguard Workerdef fdfile(fd):
155*795d594fSAndroid Build Coastguard Worker  return "/proc/{}/fd/{}".format(os.getpid(), fd)
156*795d594fSAndroid Build Coastguard Worker
157*795d594fSAndroid Build Coastguard Worker
158*795d594fSAndroid Build Coastguard Workerdef get_profile_args(args, location_base):
159*795d594fSAndroid Build Coastguard Worker  """Handle all the profile file options."""
160*795d594fSAndroid Build Coastguard Worker  if args.profile_file is None and len(args.profile_line) == 0:
161*795d594fSAndroid Build Coastguard Worker    return []
162*795d594fSAndroid Build Coastguard Worker  if args.profile_file:
163*795d594fSAndroid Build Coastguard Worker    with open(args.profile_file, "rb") as prof:
164*795d594fSAndroid Build Coastguard Worker      prof_magic = prof.read(4)
165*795d594fSAndroid Build Coastguard Worker      if prof_magic == b'pro\0':
166*795d594fSAndroid Build Coastguard Worker        # Looks like the profile-file is a binary profile. Just use it directly
167*795d594fSAndroid Build Coastguard Worker        return ['--profile-file={}'.format(args.profile_file)]
168*795d594fSAndroid Build Coastguard Worker  if args.debug_profman:
169*795d594fSAndroid Build Coastguard Worker    profman_args = ["lldb-server", "g", ":5039", "--", args.profman]
170*795d594fSAndroid Build Coastguard Worker  else:
171*795d594fSAndroid Build Coastguard Worker    profman_args = [args.profman]
172*795d594fSAndroid Build Coastguard Worker  if args.save_profile:
173*795d594fSAndroid Build Coastguard Worker    prof_out_fd = args.save_profile.fileno()
174*795d594fSAndroid Build Coastguard Worker    os.set_inheritable(prof_out_fd, True)
175*795d594fSAndroid Build Coastguard Worker  else:
176*795d594fSAndroid Build Coastguard Worker    prof_out_fd = os.memfd_create("reference_prof", flags=0)
177*795d594fSAndroid Build Coastguard Worker  if args.debug_profman:
178*795d594fSAndroid Build Coastguard Worker    profman_args.append("--reference-profile-file={}".format(
179*795d594fSAndroid Build Coastguard Worker        fdfile(prof_out_fd)))
180*795d594fSAndroid Build Coastguard Worker  else:
181*795d594fSAndroid Build Coastguard Worker    profman_args.append("--reference-profile-file-fd={}".format(prof_out_fd))
182*795d594fSAndroid Build Coastguard Worker  if args.profile_file:
183*795d594fSAndroid Build Coastguard Worker    profman_args.append("--create-profile-from={}".format(args.profile_file))
184*795d594fSAndroid Build Coastguard Worker  else:
185*795d594fSAndroid Build Coastguard Worker    prof_in_fd = os.memfd_create("input_prof", flags=0)
186*795d594fSAndroid Build Coastguard Worker    # Why on earth does fdopen take control of the fd and not mention it in the docs.
187*795d594fSAndroid Build Coastguard Worker    with os.fdopen(os.dup(prof_in_fd), "w") as prof_in:
188*795d594fSAndroid Build Coastguard Worker      for l in args.profile_line:
189*795d594fSAndroid Build Coastguard Worker        print(l, file=prof_in)
190*795d594fSAndroid Build Coastguard Worker    profman_args.append("--create-profile-from={}".format(fdfile(prof_in_fd)))
191*795d594fSAndroid Build Coastguard Worker  for f in args.dex_files:
192*795d594fSAndroid Build Coastguard Worker    profman_args.append("--apk={}".format(f))
193*795d594fSAndroid Build Coastguard Worker    profman_args.append("--dex-location={}".format(
194*795d594fSAndroid Build Coastguard Worker        os.path.join(location_base, os.path.basename(f))))
195*795d594fSAndroid Build Coastguard Worker  print("Running: {}".format(run_print(profman_args)))
196*795d594fSAndroid Build Coastguard Worker  print("=START=======================================")
197*795d594fSAndroid Build Coastguard Worker  subprocess.run(profman_args, close_fds=False).check_returncode()
198*795d594fSAndroid Build Coastguard Worker  print("=END=========================================")
199*795d594fSAndroid Build Coastguard Worker  if args.debug:
200*795d594fSAndroid Build Coastguard Worker    return ["--profile-file={}".format(fdfile(prof_out_fd))]
201*795d594fSAndroid Build Coastguard Worker  else:
202*795d594fSAndroid Build Coastguard Worker    return ["--profile-file={}".format(fdfile(prof_out_fd))]
203*795d594fSAndroid Build Coastguard Worker
204*795d594fSAndroid Build Coastguard Worker
205*795d594fSAndroid Build Coastguard Workerdef main():
206*795d594fSAndroid Build Coastguard Worker  args, extra = parse_args()
207*795d594fSAndroid Build Coastguard Worker  if args.arch == "host32" or args.arch == "host64":
208*795d594fSAndroid Build Coastguard Worker    location_base = os.path.expandvars("${ANDROID_HOST_OUT}/framework/")
209*795d594fSAndroid Build Coastguard Worker    real_arch = "x86" if args.arch == "host32" else "x86_64"
210*795d594fSAndroid Build Coastguard Worker    boot_image = os.path.expandvars(
211*795d594fSAndroid Build Coastguard Worker        "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/boot.art")
212*795d594fSAndroid Build Coastguard Worker    android_root = os.path.expandvars("$ANDROID_HOST_OUT")
213*795d594fSAndroid Build Coastguard Worker    for f in args.dex_files:
214*795d594fSAndroid Build Coastguard Worker      extra.append("--dex-location={}".format(
215*795d594fSAndroid Build Coastguard Worker          os.path.join(location_base, os.path.basename(f))))
216*795d594fSAndroid Build Coastguard Worker      extra.append("--dex-file={}".format(f))
217*795d594fSAndroid Build Coastguard Worker  else:
218*795d594fSAndroid Build Coastguard Worker    location_base = "/system/framework"
219*795d594fSAndroid Build Coastguard Worker    real_arch = args.arch
220*795d594fSAndroid Build Coastguard Worker    boot_image = os.path.expandvars(":".join([
221*795d594fSAndroid Build Coastguard Worker        "${OUT}/apex/art_boot_images/javalib/boot.art",
222*795d594fSAndroid Build Coastguard Worker        "${OUT}/system/framework/boot-framework.art"
223*795d594fSAndroid Build Coastguard Worker    ]))
224*795d594fSAndroid Build Coastguard Worker    android_root = os.path.expandvars("$OUT/system")
225*795d594fSAndroid Build Coastguard Worker    for f in args.dex_files:
226*795d594fSAndroid Build Coastguard Worker      extra.append("--dex-location={}".format(
227*795d594fSAndroid Build Coastguard Worker          os.path.join(location_base, os.path.basename(f))))
228*795d594fSAndroid Build Coastguard Worker      extra.append("--dex-file={}".format(f))
229*795d594fSAndroid Build Coastguard Worker  extra += get_bcp_runtime_args(args.add_bcp, boot_image, args.arch)
230*795d594fSAndroid Build Coastguard Worker  extra += get_profile_args(args, location_base)
231*795d594fSAndroid Build Coastguard Worker  extra.append("--instruction-set={}".format(real_arch))
232*795d594fSAndroid Build Coastguard Worker  extra.append("--boot-image={}".format(boot_image))
233*795d594fSAndroid Build Coastguard Worker  extra.append("--android-root={}".format(android_root))
234*795d594fSAndroid Build Coastguard Worker  extra += ["--runtime-arg", "-Xms64m", "--runtime-arg", "-Xmx512m"]
235*795d594fSAndroid Build Coastguard Worker  if args.odex_file is not None:
236*795d594fSAndroid Build Coastguard Worker    extra.append("--oat-file={}".format(args.odex_file))
237*795d594fSAndroid Build Coastguard Worker  else:
238*795d594fSAndroid Build Coastguard Worker    if args.debug:
239*795d594fSAndroid Build Coastguard Worker      raise Exception("Debug requires a real output file. :(")
240*795d594fSAndroid Build Coastguard Worker    extra.append("--oat-fd={}".format(os.memfd_create("odex_fd", flags=0)))
241*795d594fSAndroid Build Coastguard Worker    extra.append("--oat-location={}".format("/tmp/odex_fd.odex"))
242*795d594fSAndroid Build Coastguard Worker    extra.append("--output-vdex-fd={}".format(
243*795d594fSAndroid Build Coastguard Worker        os.memfd_create("vdex_fd", flags=0)))
244*795d594fSAndroid Build Coastguard Worker  pre_args = []
245*795d594fSAndroid Build Coastguard Worker  if args.debug:
246*795d594fSAndroid Build Coastguard Worker    pre_args = ["lldb-server", "g", ":5039", "--"]
247*795d594fSAndroid Build Coastguard Worker  pre_args.append(args.dex2oat)
248*795d594fSAndroid Build Coastguard Worker  print("Running: {}".format(run_print(pre_args + extra)))
249*795d594fSAndroid Build Coastguard Worker  print("=START=======================================")
250*795d594fSAndroid Build Coastguard Worker  subprocess.run(pre_args + extra, close_fds=False).check_returncode()
251*795d594fSAndroid Build Coastguard Worker  print("=END=========================================")
252*795d594fSAndroid Build Coastguard Worker
253*795d594fSAndroid Build Coastguard Worker
254*795d594fSAndroid Build Coastguard Workerif __name__ == "__main__":
255*795d594fSAndroid Build Coastguard Worker  main()
256