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