/* * Copyright (C) 2022 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. */ #include #include #include #include #include #include #include #include #include "android-base/parsebool.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "arch/instruction_set.h" #include "base/file_utils.h" #include "base/globals.h" #include "base/macros.h" #include "base/os.h" #include "base/testing.h" namespace art { namespace gbi { namespace { using ::android::base::ConsumePrefix; using ::android::base::ConsumeSuffix; using ::android::base::Join; using ::android::base::ParseBool; using ::android::base::ParseBoolResult; using ::android::base::StringPrintf; using ::art::testing::GetLibCoreDexFileNames; using ::art::testing::GetLibCoreDexLocations; constexpr const char* kUsage = R"( A commandline tool to generate a primary boot image for testing. Usage: generate-boot-image --output-dir=OUTPUT_DIR [OPTIONS]... [-- [DEX2OAT_OPTIONS]...] Supported options: --help: Print this text. --output-dir=OUTPUT_DIR: The directory to output the boot image. Required. --compiler-filter=COMPILER_FILTER: The compiler filter option to pass to dex2oat. Default: verify --use-profile=true|false: If true, use a profile. Default: true --dex2oat-bin=DEX2OAT_BIN: The path to the dex2oat binary. Required when running on host. Default on target: /apex/com.android.art/bin/dex2oat{32,64,32d,64d} --android-root=ANDROID_ROOT: The root directory to search for bootclasspath jars. The file structure under the root must be in the form of: /apex /com.android.art /javalib /core-oj.jar ... /com.android.i18n /javalib ... /com.android.conscrypt /javalib ... Required when running on host. Default on target: / --profile-file=PROFILE_FILE: The path to the profile file. Required when running on host and --use-profile is true. Default on target: /apex/com.android.art/etc/boot-image.prof --instruction-set=ISA: The instruction set option to pass to dex2oat. Required when running on host. The default on target is based on the ISA of this binary. --core-only=true|false: If true, only compile ART jars. Otherwise, also compile core-icu4j and conscrypt. Default: false --: Arguments following '--' are directly passed to dex2oat. )"; struct Options { std::string output_dir = ""; // Set the compiler filter to `verify` by default to make test preparation // faster. std::string compiler_filter = "verify"; bool use_profile = true; std::string dex2oat_bin = ""; std::string android_root = ""; std::string profile_file = ""; std::string instruction_set = ""; bool core_only = false; std::vector dex2oat_options; }; [[noreturn]] void Usage(const std::string& message) { LOG(ERROR) << message << '\n'; std::cerr << message << "\n" << kUsage << "\n"; exit(EX_USAGE); } std::string GetCompilerExecutable() { std::string compiler_executable = GetArtBinDir() + "/dex2oat"; if (kIsDebugBuild) { compiler_executable += 'd'; } compiler_executable += Is64BitInstructionSet(kRuntimeISA) ? "64" : "32"; return compiler_executable; } // Joins a list of commandline args into a single string, where each part is quoted with double // quotes. Note that this is a naive implementation that does NOT escape existing double quotes, // which is fine since we don't have existing double quotes in the args in this particular use case // and this code is never used in production. std::string BuildCommand(const std::vector& args) { std::string command = ""; for (const std::string& arg : args) { if (!command.empty()) { command += " "; } command += '"' + arg + '"'; } return command; } int GenerateBootImage(const Options& options) { std::vector args; args.push_back(options.dex2oat_bin); std::vector dex_files = GetLibCoreDexFileNames(options.android_root, options.core_only); std::vector dex_locations = GetLibCoreDexLocations(options.core_only); args.push_back("--runtime-arg"); args.push_back("-Xbootclasspath:" + Join(dex_files, ":")); args.push_back("--runtime-arg"); args.push_back("-Xbootclasspath-locations:" + Join(dex_locations, ":")); for (const std::string& file : dex_files) { args.push_back("--dex-file=" + file); } for (const std::string& location : dex_locations) { args.push_back("--dex-location=" + location); } args.push_back("--instruction-set=" + options.instruction_set); args.push_back(StringPrintf("--base=0x%08x", ART_BASE_ADDRESS)); args.push_back("--compiler-filter=" + options.compiler_filter); if (options.use_profile) { args.push_back("--profile-file=" + options.profile_file); } args.push_back("--avoid-storing-invocation"); args.push_back("--generate-debug-info"); args.push_back("--generate-build-id"); args.push_back("--image-format=lz4hc"); args.push_back("--strip"); args.push_back("--android-root=out/empty"); std::string path = ART_FORMAT("{}/{}", options.output_dir, options.instruction_set); if (!OS::DirectoryExists(path.c_str())) { CHECK_EQ(mkdir(path.c_str(), S_IRWXU), 0); } args.push_back(StringPrintf("--image=%s/boot.art", path.c_str())); args.push_back(StringPrintf("--oat-file=%s/boot.oat", path.c_str())); std::move( options.dex2oat_options.begin(), options.dex2oat_options.end(), std::back_inserter(args)); int exit_code = system(BuildCommand(args).c_str()); if (exit_code != 0) { LOG(ERROR) << "dex2oat invocation failed. Exit code: " << exit_code; } return exit_code; } int Main(int argc, char** argv) { android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM)); Options options; for (int i = 1; i < argc; i++) { std::string_view arg{argv[i]}; if (arg == "--help") { std::cerr << kUsage << "\n"; exit(0); } else if (ConsumePrefix(&arg, "--output-dir=")) { options.output_dir = arg; } else if (ConsumePrefix(&arg, "--compiler-filter=")) { options.compiler_filter = arg; } else if (ConsumePrefix(&arg, "--use-profile=")) { ParseBoolResult result = ParseBool(arg); if (result == ParseBoolResult::kError) { Usage(ART_FORMAT("Unrecognized --use-profile value: '{}'", arg)); } options.use_profile = result == ParseBoolResult::kTrue; } else if (ConsumePrefix(&arg, "--dex2oat-bin=")) { options.dex2oat_bin = arg; } else if (ConsumePrefix(&arg, "--android-root=")) { ConsumeSuffix(&arg, "/"); options.android_root = arg; } else if (ConsumePrefix(&arg, "--profile-file=")) { options.profile_file = arg; } else if (ConsumePrefix(&arg, "--instruction-set=")) { options.instruction_set = arg; } else if (ConsumePrefix(&arg, "--core-only=")) { ParseBoolResult result = ParseBool(arg); if (result == ParseBoolResult::kError) { Usage(ART_FORMAT("Unrecognized --core-only value: '{}'", arg)); } options.core_only = result == ParseBoolResult::kTrue; } else if (arg == "--") { for (i++; i < argc; i++) { options.dex2oat_options.push_back(argv[i]); } } else { Usage(ART_FORMAT("Unrecognized argument: '{}'", argv[i])); } } if (options.output_dir.empty()) { Usage("--output-dir must be specified"); } if (options.dex2oat_bin.empty()) { if (kIsTargetBuild) { options.dex2oat_bin = GetCompilerExecutable(); } else { Usage("--dex2oat-bin must be specified when running on host"); } } if (options.android_root.empty()) { if (!kIsTargetBuild) { Usage("--android-root must be specified when running on host"); } } if (options.use_profile && options.profile_file.empty()) { if (kIsTargetBuild) { options.profile_file = ART_FORMAT("{}/etc/boot-image.prof", GetArtRoot()); } else { Usage("--profile-file must be specified when running on host and --use-profile is true"); } } if (options.instruction_set.empty()) { if (kIsTargetBuild) { options.instruction_set = GetInstructionSetString(kRuntimeISA); } else { Usage("--instruction-set must be specified when running on host"); } } return GenerateBootImage(options); } } // namespace } // namespace gbi } // namespace art int main(int argc, char** argv) { return art::gbi::Main(argc, argv); }