You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
9.6 KiB
258 lines
9.6 KiB
4 months ago
|
#!/usr/bin/python3
|
||
|
#
|
||
|
# Copyright (C) 2021 The Android Open Source Project
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
#
|
||
|
# This script runs dex2oat on the host to compile a provided JAR or APK.
|
||
|
#
|
||
|
|
||
|
import argparse
|
||
|
import itertools
|
||
|
import shlex
|
||
|
import subprocess
|
||
|
import os
|
||
|
import os.path
|
||
|
|
||
|
def run_print(lst):
|
||
|
return " ".join(map(shlex.quote, lst))
|
||
|
|
||
|
|
||
|
def parse_args():
|
||
|
parser = argparse.ArgumentParser(
|
||
|
description="compile dex or jar files",
|
||
|
epilog="Unrecognized options are passed on to dex2oat unmodified.")
|
||
|
parser.add_argument(
|
||
|
"--dex2oat",
|
||
|
action="store",
|
||
|
default=os.path.expandvars("$ANDROID_HOST_OUT/bin/dex2oatd64"),
|
||
|
help="selects the dex2oat to use.")
|
||
|
parser.add_argument(
|
||
|
"--debug",
|
||
|
action="store_true",
|
||
|
default=False,
|
||
|
help="launches dex2oatd with lldb-server g :5039. Connect using vscode or remote lldb"
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"--profman",
|
||
|
action="store",
|
||
|
default=os.path.expandvars("$ANDROID_HOST_OUT/bin/profmand"),
|
||
|
help="selects the profman to use.")
|
||
|
parser.add_argument(
|
||
|
"--debug-profman",
|
||
|
action="store_true",
|
||
|
default=False,
|
||
|
help="launches profman with lldb-server g :5039. Connect using vscode or remote lldb"
|
||
|
)
|
||
|
profs = parser.add_mutually_exclusive_group()
|
||
|
profs.add_argument(
|
||
|
"--profile-file",
|
||
|
action="store",
|
||
|
help="Use this profile file. Probably want to pass --compiler-filter=speed-profile with this."
|
||
|
)
|
||
|
profs.add_argument(
|
||
|
"--profile-line",
|
||
|
action="append",
|
||
|
default=[],
|
||
|
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."
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"--add-bcp",
|
||
|
action="append",
|
||
|
default=[],
|
||
|
nargs=2,
|
||
|
metavar=("BCP_FILE", "BCP_LOCATION"),
|
||
|
help="File and location to add to the boot-class-path. Note no deduplication is attempted."
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"--arch",
|
||
|
action="store",
|
||
|
choices=["arm", "arm64", "x86", "x86_64", "host64", "host32"],
|
||
|
default="host64",
|
||
|
help="architecture to compile for. Defaults to host64")
|
||
|
parser.add_argument(
|
||
|
"--odex-file",
|
||
|
action="store",
|
||
|
help="odex file to write. File discarded if not set",
|
||
|
default=None)
|
||
|
parser.add_argument(
|
||
|
"--save-profile",
|
||
|
action="store",
|
||
|
type=argparse.FileType("w"),
|
||
|
default=None,
|
||
|
help="File path to store the profile to")
|
||
|
parser.add_argument(
|
||
|
"dex_files", help="dex/jar files", nargs="+", metavar="DEX")
|
||
|
return parser.parse_known_args()
|
||
|
|
||
|
|
||
|
def get_bcp_runtime_args(additions, image, arch):
|
||
|
add_files = map(lambda a: a[0], additions)
|
||
|
add_locs = map(lambda a: a[1], additions)
|
||
|
if arch != "host32" and arch != "host64":
|
||
|
args = [
|
||
|
"art/tools/host_bcp.sh",
|
||
|
os.path.expandvars(
|
||
|
"${{OUT}}/system/framework/oat/{}/services.odex".format(arch)),
|
||
|
"--use-first-dir"
|
||
|
]
|
||
|
print("Running: {}".format(run_print(args)))
|
||
|
print("=START=======================================")
|
||
|
res = subprocess.run(args, capture_output=True, text=True)
|
||
|
print("=END=========================================")
|
||
|
if res.returncode != 0:
|
||
|
print("Falling back to com.android.art BCP")
|
||
|
args = [
|
||
|
"art/tools/host_bcp.sh",
|
||
|
os.path.expandvars(
|
||
|
"${{OUT}}/apex/com.android.art.debug/javalib/{}/boot.oat".format(arch)),
|
||
|
"--use-first-dir"
|
||
|
]
|
||
|
print("Running: {}".format(run_print(args)))
|
||
|
print("=START=======================================")
|
||
|
res = subprocess.run(args, capture_output=True, text=True)
|
||
|
print("=END=========================================")
|
||
|
res.check_returncode()
|
||
|
segments = res.stdout.split()
|
||
|
def extend_bcp(segment: str):
|
||
|
# TODO We should make the bcp have absolute paths.
|
||
|
if segment.startswith("-Xbootclasspath:"):
|
||
|
return ":".join(itertools.chain((segment,), add_files))
|
||
|
elif segment.startswith("-Xbootclasspath-locations:"):
|
||
|
return ":".join(itertools.chain((segment,), add_locs))
|
||
|
else:
|
||
|
return segment
|
||
|
return list(map(extend_bcp, segments))
|
||
|
else:
|
||
|
# Host we just use the bcp locations for both.
|
||
|
res = open(
|
||
|
os.path.expandvars(
|
||
|
"$ANDROID_HOST_OUT/apex/art_boot_images/javalib/{}/boot.oat".format(
|
||
|
"x86" if arch == "host32" else "x86_64")), "rb").read()
|
||
|
bcp_tag = b"bootclasspath\0"
|
||
|
bcp_start = res.find(bcp_tag) + len(bcp_tag)
|
||
|
bcp = res[bcp_start:bcp_start + res[bcp_start:].find(b"\0")]
|
||
|
img_bcp = bcp.decode()
|
||
|
# TODO We should make the str_bcp have absolute paths.
|
||
|
str_bcp = ":".join(itertools.chain((img_bcp,), add_files))
|
||
|
str_bcp_loc = ":".join(itertools.chain((img_bcp,), add_locs))
|
||
|
return [
|
||
|
"--runtime-arg", "-Xbootclasspath:{}".format(str_bcp),
|
||
|
"--runtime-arg", "-Xbootclasspath-locations:{}".format(str_bcp_loc)
|
||
|
]
|
||
|
|
||
|
|
||
|
def fdfile(fd):
|
||
|
return "/proc/{}/fd/{}".format(os.getpid(), fd)
|
||
|
|
||
|
|
||
|
def get_profile_args(args, location_base):
|
||
|
"""Handle all the profile file options."""
|
||
|
if args.profile_file is None and len(args.profile_line) == 0:
|
||
|
return []
|
||
|
if args.profile_file:
|
||
|
with open(args.profile_file, "rb") as prof:
|
||
|
prof_magic = prof.read(4)
|
||
|
if prof_magic == b'pro\0':
|
||
|
# Looks like the profile-file is a binary profile. Just use it directly
|
||
|
return ['--profile-file={}'.format(args.profile_file)]
|
||
|
if args.debug_profman:
|
||
|
profman_args = ["lldb-server", "g", ":5039", "--", args.profman]
|
||
|
else:
|
||
|
profman_args = [args.profman]
|
||
|
if args.save_profile:
|
||
|
prof_out_fd = args.save_profile.fileno()
|
||
|
os.set_inheritable(prof_out_fd, True)
|
||
|
else:
|
||
|
prof_out_fd = os.memfd_create("reference_prof", flags=0)
|
||
|
if args.debug_profman:
|
||
|
profman_args.append("--reference-profile-file={}".format(
|
||
|
fdfile(prof_out_fd)))
|
||
|
else:
|
||
|
profman_args.append("--reference-profile-file-fd={}".format(prof_out_fd))
|
||
|
if args.profile_file:
|
||
|
profman_args.append("--create-profile-from={}".format(args.profile_file))
|
||
|
else:
|
||
|
prof_in_fd = os.memfd_create("input_prof", flags=0)
|
||
|
# Why on earth does fdopen take control of the fd and not mention it in the docs.
|
||
|
with os.fdopen(os.dup(prof_in_fd), "w") as prof_in:
|
||
|
for l in args.profile_line:
|
||
|
print(l, file=prof_in)
|
||
|
profman_args.append("--create-profile-from={}".format(fdfile(prof_in_fd)))
|
||
|
for f in args.dex_files:
|
||
|
profman_args.append("--apk={}".format(f))
|
||
|
profman_args.append("--dex-location={}".format(
|
||
|
os.path.join(location_base, os.path.basename(f))))
|
||
|
print("Running: {}".format(run_print(profman_args)))
|
||
|
print("=START=======================================")
|
||
|
subprocess.run(profman_args, close_fds=False).check_returncode()
|
||
|
print("=END=========================================")
|
||
|
if args.debug:
|
||
|
return ["--profile-file={}".format(fdfile(prof_out_fd))]
|
||
|
else:
|
||
|
return ["--profile-file={}".format(fdfile(prof_out_fd))]
|
||
|
|
||
|
|
||
|
def main():
|
||
|
args, extra = parse_args()
|
||
|
if args.arch == "host32" or args.arch == "host64":
|
||
|
location_base = os.path.expandvars("${ANDROID_HOST_OUT}/framework/")
|
||
|
real_arch = "x86" if args.arch == "host32" else "x86_64"
|
||
|
boot_image = os.path.expandvars(
|
||
|
"$ANDROID_HOST_OUT/apex/art_boot_images/javalib/boot.art")
|
||
|
android_root = os.path.expandvars("$ANDROID_HOST_OUT")
|
||
|
for f in args.dex_files:
|
||
|
extra.append("--dex-location={}".format(
|
||
|
os.path.join(location_base, os.path.basename(f))))
|
||
|
extra.append("--dex-file={}".format(f))
|
||
|
else:
|
||
|
location_base = "/system/framework"
|
||
|
real_arch = args.arch
|
||
|
boot_image = os.path.expandvars(":".join([
|
||
|
"${OUT}/apex/art_boot_images/javalib/boot.art",
|
||
|
"${OUT}/system/framework/boot-framework.art"
|
||
|
]))
|
||
|
android_root = os.path.expandvars("$OUT/system")
|
||
|
for f in args.dex_files:
|
||
|
extra.append("--dex-location={}".format(
|
||
|
os.path.join(location_base, os.path.basename(f))))
|
||
|
extra.append("--dex-file={}".format(f))
|
||
|
extra += get_bcp_runtime_args(args.add_bcp, boot_image, args.arch)
|
||
|
extra += get_profile_args(args, location_base)
|
||
|
extra.append("--instruction-set={}".format(real_arch))
|
||
|
extra.append("--boot-image={}".format(boot_image))
|
||
|
extra.append("--android-root={}".format(android_root))
|
||
|
extra += ["--runtime-arg", "-Xms64m", "--runtime-arg", "-Xmx512m"]
|
||
|
if args.odex_file is not None:
|
||
|
extra.append("--oat-file={}".format(args.odex_file))
|
||
|
else:
|
||
|
if args.debug:
|
||
|
raise Exception("Debug requires a real output file. :(")
|
||
|
extra.append("--oat-fd={}".format(os.memfd_create("odex_fd", flags=0)))
|
||
|
extra.append("--oat-location={}".format("/tmp/odex_fd.odex"))
|
||
|
extra.append("--output-vdex-fd={}".format(
|
||
|
os.memfd_create("vdex_fd", flags=0)))
|
||
|
pre_args = []
|
||
|
if args.debug:
|
||
|
pre_args = ["lldb-server", "g", ":5039", "--"]
|
||
|
pre_args.append(args.dex2oat)
|
||
|
print("Running: {}".format(run_print(pre_args + extra)))
|
||
|
print("=START=======================================")
|
||
|
subprocess.run(pre_args + extra, close_fds=False).check_returncode()
|
||
|
print("=END=========================================")
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|