aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--derive_classpath/derive_classpath.cpp83
-rw-r--r--derive_classpath/derive_classpath.h4
-rw-r--r--derive_classpath/derive_classpath_test.cpp34
-rw-r--r--derive_classpath/main.cpp59
4 files changed, 137 insertions, 43 deletions
diff --git a/derive_classpath/derive_classpath.cpp b/derive_classpath/derive_classpath.cpp
index 0ff246c..0c9c361 100644
--- a/derive_classpath/derive_classpath.cpp
+++ b/derive_classpath/derive_classpath.cpp
@@ -34,34 +34,51 @@ using Classpaths = std::unordered_map<Classpath, Filepaths>;
static const std::regex kBindMountedApex("^/apex/[^/]+@[0-9]+/");
static const std::regex kApexPathRegex("(/apex/[^/]+)/");
-// Defines the order of individual fragments to be merged for BOOTCLASSPATH:
-// 1. Jars in ART module always come first;
-// 2. Jars defined as part of /system/etc/classpaths;
-// 3. Jars defined in all non-ART apexes that expose /apex/*/etc/classpaths fragments.
-//
-// Notes:
-// - Relative order in the individual fragment files is not changed when merging.
-// - If a fragment file is matched by multiple globs, the first one is used; i.e. ART module
-// fragment is only parsed once, even if there is a "/apex/*/" pattern later.
-// - If there are multiple files matched for a glob pattern with wildcards, the results are sorted
-// by pathname (default glob behaviour); i.e. all fragment files are sorted within a single
-// "pattern block".
-static const std::vector<std::string> kBootclasspathFragmentGlobPatterns = {
- // ART module is a special case and must come first before any other classpath entries.
- "/apex/com.android.art/etc/classpaths/bootclasspath.pb",
- "/system/etc/classpaths/bootclasspath.pb",
- "/apex/*/etc/classpaths/bootclasspath.pb",
-};
+std::vector<std::string> getBootclasspathFragmentGlobPatterns(const Args& args) {
+ // Defines the order of individual fragments to be merged for BOOTCLASSPATH:
+ // 1. Jars in ART module always come first;
+ // 2. Jars defined as part of /system/etc/classpaths;
+ // 3. Jars defined in all non-ART apexes that expose /apex/*/etc/classpaths fragments.
+ //
+ // Notes:
+ // - Relative order in the individual fragment files is not changed when merging.
+ // - If a fragment file is matched by multiple globs, the first one is used; i.e. ART module
+ // fragment is only parsed once, even if there is a "/apex/*/" pattern later.
+ // - If there are multiple files matched for a glob pattern with wildcards, the results are sorted
+ // by pathname (default glob behaviour); i.e. all fragment files are sorted within a single
+ // "pattern block".
+ std::vector<std::string> patterns = {
+ // ART module is a special case and must come first before any other classpath entries.
+ "/apex/com.android.art/etc/classpaths/bootclasspath.pb",
+ };
+ if (args.system_bootclasspath_fragment.empty()) {
+ patterns.emplace_back("/system/etc/classpaths/bootclasspath.pb");
+ } else {
+ // TODO: Avoid applying glob(3) expansion later to this path. Although the caller should not
+ // provide a path that contains '*', it can technically happen. Instead of checking the string
+ // format, we should just avoid the glob(3) for this string.
+ patterns.emplace_back(args.system_bootclasspath_fragment);
+ }
+ patterns.emplace_back("/apex/*/etc/classpaths/bootclasspath.pb");
+ return patterns;
+}
-// Defines the order of individual fragments to be merged for SYSTEMSERVERCLASSPATH.
-//
-// ART system server jars are not special in this case, and are considered to be part of all the
-// other apexes that may expose system server jars.
-//
-// All notes from kBootclasspathFragmentGlobPatterns apply here.
-static const std::vector<std::string> kSystemserverclasspathFragmentGlobPatterns = {
- "/system/etc/classpaths/systemserverclasspath.pb",
- "/apex/*/etc/classpaths/systemserverclasspath.pb",
+std::vector<std::string> getSystemserverclasspathFragmentGlobPatterns(const Args& args) {
+ // Defines the order of individual fragments to be merged for SYSTEMSERVERCLASSPATH.
+ //
+ // ART system server jars are not special in this case, and are considered to be part of all the
+ // other apexes that may expose system server jars.
+ //
+ // All notes from getBootclasspathFragmentGlobPatterns apply here.
+ std::vector<std::string> patterns;
+ if (args.system_systemserverclasspath_fragment.empty()) {
+ patterns.emplace_back("/system/etc/classpaths/systemserverclasspath.pb");
+ } else {
+ // TODO: Avoid applying glob(3) expansion later to this path. See above.
+ patterns.emplace_back(args.system_systemserverclasspath_fragment);
+ }
+ patterns.emplace_back("/apex/*/etc/classpaths/systemserverclasspath.pb");
+ return patterns;
};
// Finds all classpath fragment files that match the glob pattern and appends them to `fragments`.
@@ -149,15 +166,15 @@ std::string GetAllowedJarPathPrefix(const std::string& fragment_path) {
}
// Finds and parses all classpath fragments on device matching given glob patterns.
-bool ParseFragments(const std::string& globPatternPrefix, Classpaths& classpaths, bool boot_jars) {
+bool ParseFragments(const Args& args, Classpaths& classpaths, bool boot_jars) {
LOG(INFO) << "ParseFragments for " << (boot_jars ? "bootclasspath" : "systemserverclasspath");
- auto glob_patterns =
- boot_jars ? kBootclasspathFragmentGlobPatterns : kSystemserverclasspathFragmentGlobPatterns;
+ auto glob_patterns = boot_jars ? getBootclasspathFragmentGlobPatterns(args)
+ : getSystemserverclasspathFragmentGlobPatterns(args);
Filepaths fragments;
for (const auto& pattern : glob_patterns) {
- if (!GlobClasspathFragments(&fragments, globPatternPrefix + pattern)) {
+ if (!GlobClasspathFragments(&fragments, args.glob_pattern_prefix + pattern)) {
return false;
}
}
@@ -194,11 +211,11 @@ bool GenerateClasspathExports(const Args& args) {
<< "derive_classpath must only be run on Android 12 or above";
Classpaths classpaths;
- if (!ParseFragments(args.glob_pattern_prefix, classpaths, /*boot_jars=*/true)) {
+ if (!ParseFragments(args, classpaths, /*boot_jars=*/true)) {
LOG(ERROR) << "Failed to parse BOOTCLASSPATH fragments";
return false;
}
- if (!ParseFragments(args.glob_pattern_prefix, classpaths, /*boot_jars=*/false)) {
+ if (!ParseFragments(args, classpaths, /*boot_jars=*/false)) {
LOG(ERROR) << "Failed to parse SYSTEMSERVERCLASSPATH fragments";
return false;
}
diff --git a/derive_classpath/derive_classpath.h b/derive_classpath/derive_classpath.h
index 9b53254..b0716f3 100644
--- a/derive_classpath/derive_classpath.h
+++ b/derive_classpath/derive_classpath.h
@@ -27,6 +27,10 @@ constexpr std::string_view kGeneratedClasspathExportsFilepath = "/data/system/en
struct Args {
std::string_view output_path;
+ // Alternative *classpath.pb files if provided.
+ std::string system_bootclasspath_fragment;
+ std::string system_systemserverclasspath_fragment;
+
// Test only. glob_pattern_prefix is appended to each glob pattern to allow adding mock configs in
// /data/local/tmp for example.
std::string glob_pattern_prefix;
diff --git a/derive_classpath/derive_classpath_test.cpp b/derive_classpath/derive_classpath_test.cpp
index 277a452..6e503f3 100644
--- a/derive_classpath/derive_classpath_test.cpp
+++ b/derive_classpath/derive_classpath_test.cpp
@@ -216,6 +216,40 @@ TEST_F(DeriveClasspathTest, CustomOutputLocation) {
EXPECT_EQ(expectedJars, exportValue);
}
+// Test alternative .pb for bootclasspath and systemclasspath.
+TEST_F(DeriveClasspathTest, CustomInputLocation) {
+ AddJarToClasspath("/other", "/other/bcp-jar", BOOTCLASSPATH);
+ AddJarToClasspath("/other", "/other/systemserver-jar", SYSTEMSERVERCLASSPATH);
+ AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
+ AddJarToClasspath("/apex/com.android.foo", "/apex/com.android.foo/javalib/foo", BOOTCLASSPATH);
+ AddJarToClasspath("/apex/com.android.baz", "/apex/com.android.baz/javalib/baz",
+ SYSTEMSERVERCLASSPATH);
+
+ Args args = default_args_with_test_dir_;
+ args.system_bootclasspath_fragment = "/other/etc/classpaths/bootclasspath.pb";
+ args.system_systemserverclasspath_fragment = "/other/etc/classpaths/systemserverclasspath.pb";
+
+ ASSERT_TRUE(GenerateClasspathExports(args));
+
+ const std::vector<std::string> exportLines = ParseExportsFile();
+
+ std::vector<std::string> splitExportLine;
+ splitExportLine = SplitClasspathExportLine(exportLines[0]);
+ EXPECT_EQ("BOOTCLASSPATH", splitExportLine[1]);
+ const std::string expectedBcpJars(
+ "/apex/com.android.art/javalib/art"
+ ":/other/bcp-jar"
+ ":/apex/com.android.foo/javalib/foo");
+ EXPECT_EQ(expectedBcpJars, splitExportLine[2]);
+
+ splitExportLine = SplitClasspathExportLine(exportLines[2]);
+ EXPECT_EQ("SYSTEMSERVERCLASSPATH", splitExportLine[1]);
+ const std::string expectedSystemServerJars(
+ "/other/systemserver-jar"
+ ":/apex/com.android.baz/javalib/baz");
+ EXPECT_EQ(expectedSystemServerJars, splitExportLine[2]);
+}
+
// Test output location that can't be written to.
TEST_F(DeriveClasspathTest, NonWriteableOutputLocation) {
AddJarToClasspath("/apex/com.android.art", "/apex/com.android.art/javalib/art", BOOTCLASSPATH);
diff --git a/derive_classpath/main.cpp b/derive_classpath/main.cpp
index d589680..8807cdb 100644
--- a/derive_classpath/main.cpp
+++ b/derive_classpath/main.cpp
@@ -21,18 +21,57 @@
#include "derive_classpath.h"
-int main(int argc, char** argv) {
- // Default args
- android::derive_classpath::Args args = {
- .output_path = android::derive_classpath::kGeneratedClasspathExportsFilepath,
- .glob_pattern_prefix = "",
- };
- if (argc == 1) {
+bool ArgumentMatches(std::string_view argument, std::string_view prefix, std::string_view* value) {
+ if (android::base::StartsWith(argument, prefix)) {
+ *value = argument.substr(prefix.size());
+ return true;
+ }
+ return false;
+}
+
+// Command line flags need to be considered as a de facto API since there may be callers outside
+// of the SdkExtensions APEX, which needs to run on older Android versions. For example, otapreopt
+// currently executes derive_classpath with a single output file. When changing the flags, make sure
+// it won't break on older Android.
+bool ParseArgs(android::derive_classpath::Args& args, int argc, char** argv) {
+ // Parse flags
+ std::vector<std::string_view> positional_args;
+ for (int i = 1; i < argc; ++i) {
+ const std::string_view arg = argv[i];
+ std::string_view value;
+ if (ArgumentMatches(arg, "--bootclasspath-fragment=", &value)) {
+ if (!args.system_bootclasspath_fragment.empty()) {
+ LOG(ERROR) << "Duplicated flag --bootclasspath-fragment is specified";
+ return false;
+ }
+ args.system_bootclasspath_fragment = value;
+ } else if (ArgumentMatches(arg, "--systemserverclasspath-fragment=", &value)) {
+ if (!args.system_bootclasspath_fragment.empty()) {
+ LOG(ERROR) << "Duplicated flag --systemserverclasspath-fragment is specified";
+ return false;
+ }
+ args.system_systemserverclasspath_fragment = value;
+ } else {
+ positional_args.emplace_back(arg);
+ }
+ }
+
+ // Handle positional args
+ if (positional_args.size() == 0) {
args.output_path = android::derive_classpath::kGeneratedClasspathExportsFilepath;
- } else if (argc == 2) {
- args.output_path = argv[1];
+ } else if (positional_args.size() == 1) {
+ args.output_path = positional_args[0];
} else {
- LOG(ERROR) << "too many arguments " << argc;
+ LOG(ERROR) << "Unrecognized positional arguments: "
+ << android::base::Join(positional_args, ' ');
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char** argv) {
+ android::derive_classpath::Args args;
+ if (!ParseArgs(args, argc, argv)) {
return EXIT_FAILURE;
}
if (!android::derive_classpath::GenerateClasspathExports(args)) {