• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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