1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <cstdlib>
18 #include <string_view>
19
20 #include <android-base/logging.h>
21 #include <android-base/parseint.h>
22 #include <android-base/strings.h>
23
24 #include "derive_classpath.h"
25
ArgumentMatches(std::string_view argument,std::string_view prefix,std::string_view * value)26 bool ArgumentMatches(std::string_view argument, std::string_view prefix, std::string_view* value) {
27 if (android::base::StartsWith(argument, prefix)) {
28 *value = argument.substr(prefix.size());
29 return true;
30 }
31 return false;
32 }
33
34 // Command line flags need to be considered as a de facto API since there may be callers outside
35 // of the SdkExtensions APEX, which needs to run on older Android versions. For example, otapreopt
36 // currently executes derive_classpath with a single output file. When changing the flags, make sure
37 // it won't break on older Android.
ParseArgs(android::derive_classpath::Args & args,int argc,char ** argv)38 bool ParseArgs(android::derive_classpath::Args& args, int argc, char** argv) {
39 // Parse flags
40 std::vector<std::string_view> positional_args;
41 for (int i = 1; i < argc; ++i) {
42 const std::string_view arg = argv[i];
43 std::string_view value;
44 if (ArgumentMatches(arg, "--bootclasspath-fragment=", &value)) {
45 if (!args.system_bootclasspath_fragment.empty()) {
46 LOG(ERROR) << "Duplicated flag --bootclasspath-fragment is specified";
47 return false;
48 }
49 args.system_bootclasspath_fragment = value;
50 } else if (ArgumentMatches(arg, "--systemserverclasspath-fragment=", &value)) {
51 if (!args.system_systemserverclasspath_fragment.empty()) {
52 LOG(ERROR) << "Duplicated flag --systemserverclasspath-fragment is specified";
53 return false;
54 }
55 args.system_systemserverclasspath_fragment = value;
56 } else if (ArgumentMatches(arg, "--scan-dirs=", &value)) {
57 if (!args.scan_dirs.empty()) {
58 LOG(ERROR) << "Duplicated flag --scan-dirs is specified";
59 return false;
60 }
61 args.scan_dirs = android::base::Split(std::string(value), ",");
62 } else if (ArgumentMatches(arg, "--glob-pattern-prefix=", &value)) {
63 if (!args.glob_pattern_prefix.empty()) {
64 LOG(ERROR) << "Duplicated flag --glob-pattern-prefix is specified";
65 return false;
66 }
67 args.glob_pattern_prefix = value;
68 } else if (ArgumentMatches(arg, "--override-device-sdk-version=", &value)) {
69 if (args.override_device_sdk_version != 0) {
70 LOG(ERROR) << "Duplicated flag --override-device-sdk-version is specified";
71 return false;
72 }
73 if (!android::base::ParseInt(std::string(value), &args.override_device_sdk_version, /*min=*/1,
74 /*max=*/INT_MAX)) {
75 PLOG(ERROR) << "Invalid value for --override-device-sdk-version \"" << value << "\"";
76 return false;
77 }
78 } else if (ArgumentMatches(arg, "--override-device-codename=", &value)) {
79 if (!args.override_device_codename.empty()) {
80 LOG(ERROR) << "Duplicated flag --override-device-codename is specified";
81 return false;
82 }
83 args.override_device_codename = value;
84 } else if (ArgumentMatches(arg, "--override-device-known-codenames=", &value)) {
85 if (!args.override_device_known_codenames.empty()) {
86 LOG(ERROR) << "Duplicated flag --override-device-known-codenames is specified";
87 return false;
88 }
89 std::vector<std::string> known_codenames = android::base::Split(std::string(value), ",");
90 std::move(known_codenames.begin(), known_codenames.end(),
91 std::inserter(args.override_device_known_codenames,
92 args.override_device_known_codenames.end()));
93 } else {
94 positional_args.emplace_back(arg);
95 }
96 }
97
98 // Validate flag combinations
99 if (!args.scan_dirs.empty() && (!args.system_bootclasspath_fragment.empty() ||
100 !args.system_systemserverclasspath_fragment.empty())) {
101 LOG(ERROR) << "--scan-dirs should not be accompanied by --bootclasspath-fragment or "
102 "--systemserverclasspath-fragment";
103 return false;
104 }
105
106 if (!args.glob_pattern_prefix.empty() &&
107 (!args.scan_dirs.empty() || !args.system_bootclasspath_fragment.empty() ||
108 !args.system_systemserverclasspath_fragment.empty())) {
109 LOG(ERROR) << "--glob-pattern-prefix should not be accompanied by --scan-dirs, "
110 "--bootclasspath-fragment or --systemserverclasspath-fragment";
111 return false;
112 }
113
114 if (args.override_device_sdk_version != 0 && args.override_device_codename.empty()) {
115 LOG(ERROR)
116 << "--override-device-sdk-version should be accompanied by --override-device-codename";
117 return false;
118 }
119
120 if (!args.override_device_codename.empty() && args.override_device_codename != "REL" &&
121 args.override_device_known_codenames.empty()) {
122 LOG(ERROR) << "--override-device-codename should be accompanied by "
123 "--override-device-known-codenames, unless it is set to \"REL\"";
124 return false;
125 }
126
127 if (args.override_device_sdk_version == 0 &&
128 (!args.override_device_codename.empty() || !args.override_device_known_codenames.empty())) {
129 LOG(ERROR) << "--override-device-codename and --override-device-known-codenames should not "
130 "be specified without --override-device-sdk-version";
131 return false;
132 }
133
134 #ifndef SDKEXT_ANDROID
135 if (args.glob_pattern_prefix.empty() && args.scan_dirs.empty()) {
136 LOG(ERROR) << "Either --glob-pattern-prefix or --scan-dirs must be specified on host";
137 return false;
138 }
139
140 if (args.override_device_sdk_version == 0) {
141 LOG(ERROR)
142 << "--override-device-sdk-version and --override-device-codename must be specified on host";
143 return false;
144 }
145 #endif
146
147 // Handle positional args
148 if (positional_args.size() == 0) {
149 #ifndef SDKEXT_ANDROID
150 LOG(ERROR) << "Output path must be specified on host";
151 return false;
152 #endif
153 args.output_path = android::derive_classpath::kGeneratedClasspathExportsFilepath;
154 } else if (positional_args.size() == 1) {
155 args.output_path = positional_args[0];
156 } else {
157 LOG(ERROR) << "Unrecognized positional arguments: "
158 << android::base::Join(positional_args, ' ');
159 return false;
160 }
161
162 return true;
163 }
164
main(int argc,char ** argv)165 int main(int argc, char** argv) {
166 android::derive_classpath::Args args;
167 if (!ParseArgs(args, argc, argv)) {
168 return EXIT_FAILURE;
169 }
170 if (!android::derive_classpath::GenerateClasspathExports(args)) {
171 return EXIT_FAILURE;
172 }
173 return EXIT_SUCCESS;
174 }
175