1 /*
2  * Copyright (C) 2015, 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 "options.h"
18 
19 #include <android-base/logging.h>
20 #include <android-base/parseint.h>
21 #include <android-base/result.h>
22 #include <android-base/strings.h>
23 #include <getopt.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 
27 #include <algorithm>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31 
32 #include "aidl_language.h"
33 #include "logging.h"
34 #include "os.h"
35 
36 using android::base::Join;
37 using android::base::Result;
38 using android::base::Split;
39 using android::base::StringReplace;
40 using android::base::Trim;
41 using std::endl;
42 using std::string;
43 
44 #ifndef PLATFORM_SDK_VERSION
45 #define PLATFORM_SDK_VERSION "<UNKNOWN>"
46 #endif
47 
48 namespace android {
49 namespace aidl {
50 
GetUsage() const51 string Options::GetUsage() const {
52   std::ostringstream sstr;
53   sstr << "AIDL Compiler: built for platform SDK version " << PLATFORM_SDK_VERSION << endl;
54   sstr << "usage:" << endl
55        << myname_ << " --lang={java|cpp|ndk|rust} [OPTION]... INPUT..." << endl
56        << "   Generate Java, C++ or Rust files for AIDL file(s)." << endl
57        << endl
58        << myname_ << " --preprocess OUTPUT INPUT..." << endl
59        << "   Create an AIDL file having declarations of AIDL file(s)." << endl
60        << endl
61        << myname_ << " --dumpapi --out=DIR INPUT..." << endl
62        << "   Dump API signature of AIDL file(s) to DIR." << endl
63        << endl
64        << myname_ << " --checkapi[={compatible|equal}] OLD_DIR NEW_DIR" << endl
65        << "   Check whether NEW_DIR API dump is {compatible|equal} extension " << endl
66        << "   of the API dump OLD_DIR. Default: compatible" << endl
67        << endl
68        << myname_ << " --apimapping OUTPUT INPUT..." << endl
69        << "   Generate a mapping of declared aidl method signatures to" << endl
70        << "   the original line number. e.g.: " << endl
71        << "       If line 39 of foo/bar/IFoo.aidl contains:"
72        << "         void doFoo(int bar, String baz);" << endl
73        << "       Then the result would be:" << endl
74        << "         foo.bar.Baz|doFoo|int,String,|void" << endl
75        << "         foo/bar/IFoo.aidl:39" << endl
76        << endl;
77 
78   // Legacy option formats
79   if (language_ == Options::Language::JAVA) {
80     sstr << myname_ << " [OPTION]... INPUT [OUTPUT]" << endl
81          << "   Generate a Java file for an AIDL file." << endl
82          << endl;
83   } else if (language_ == Options::Language::CPP) {
84     sstr << myname_ << " [OPTION]... INPUT HEADER_DIR OUTPUT" << endl
85          << "   Generate C++ headers and source for an AIDL file." << endl
86          << endl;
87   } else if (language_ == Options::Language::RUST) {
88     sstr << myname_ << " [OPTION]... INPUT [OUTPUT]" << endl
89          << "   Generate Rust file for an AIDL file." << endl
90          << endl;
91   }
92 
93   sstr << "OPTION:" << endl
94        << "  -I DIR, --include=DIR" << endl
95        << "          Use DIR as a search path for import statements of dependencies." << endl
96        << "  -N DIR, --next_include=DIR" << endl
97        << "          Use DIR as a search path for local import statements." << endl
98        << "  -p FILE, --preprocessed=FILE" << endl
99        << "          Include FILE which is created by --preprocess." << endl
100        << "  -d FILE, --dep=FILE" << endl
101        << "          Generate dependency file as FILE. Don't use this when" << endl
102        << "          there are multiple input files. Use -a then." << endl
103        << "  -o DIR, --out=DIR" << endl
104        << "          Use DIR as the base output directory for generated files." << endl
105        << "  -h DIR, --header_out=DIR" << endl
106        << "          Generate C++ headers under DIR." << endl
107        << "  --previous_api_dir=DIR" << endl
108        << "          The aidl_api directory of the previous version of this interface." << endl
109        << "  -a" << endl
110        << "          Generate dependency file next to the output file with the" << endl
111        << "          name based on the input file." << endl
112        << "  -b" << endl
113        << "          Trigger fail when trying to compile a parcelable declaration." << endl
114        << "  --ninja" << endl
115        << "          Generate dependency file in a format ninja understands." << endl
116        << "  --rpc" << endl
117        << "          (for Java) whether to generate support for RPC transactions." << endl
118        << "  --mockall" << endl
119        << "          (for Rust) whether to generate mockall mocks of AIDL interfaces." << endl
120        << "  --structured" << endl
121        << "          Whether this interface is defined exclusively in AIDL." << endl
122        << "          It is therefore a candidate for stabilization." << endl
123        << "  --stability=<level>" << endl
124        << "          The stability requirement of this interface." << endl
125        << "  --min_sdk_version=<version>" << endl
126        << "          Minimum SDK version that the generated code should support." << endl
127        << "          Defaults to " << DEFAULT_SDK_VERSION_JAVA << " for --lang=java, " << endl
128        << "            " << DEFAULT_SDK_VERSION_CPP << " for --lang=cpp, " << endl
129        << "            " << DEFAULT_SDK_VERSION_NDK << " for --lang=ndk, " << endl
130        << "            " << DEFAULT_SDK_VERSION_RUST << " for --lang=rust, " << endl
131        << "  --omit_invocation" << endl
132        << "          Do not print full commandline in output. This is required " << endl
133        << "          for certain build systems." << endl
134        << "  -t, --trace" << endl
135        << "          Include tracing code for systrace. Note that if either" << endl
136        << "          the client or service code is not auto-generated by this" << endl
137        << "          tool, that part will not be traced." << endl
138        << "  --transaction_names" << endl
139        << "          Generate transaction names." << endl
140        << "  -v VER, --version=VER" << endl
141        << "          Set the version of the interface and parcelable to VER." << endl
142        << "          VER must be an interger greater than 0." << endl
143        << "  --hash=HASH" << endl
144        << "          Set the interface hash to HASH." << endl
145        << "  --previous_hash=HASH" << endl
146        << "          Set the interface previous version's hash to HASH." << endl
147        << "  --log" << endl
148        << "          Information about the transaction, e.g., method name, argument" << endl
149        << "          values, execution time, etc., is provided via callback." << endl
150        << "  -Werror" << endl
151        << "          Turn warnings into errors." << endl
152        << "  -Wno-error=<warning>" << endl
153        << "          Turn the specified warning into a warning even if -Werror is specified."
154        << endl
155        << "  -W<warning>" << endl
156        << "          Enable the specified warning." << endl
157        << "  -Wno-<warning>" << endl
158        << "          Disable the specified warning." << endl
159        << "  -w" << endl
160        << "          Disable all diagnostics. -w wins -Weverything" << endl
161        << "  -Weverything" << endl
162        << "          Enable all diagnostics." << endl
163        << "  --help" << endl
164        << "          Show this help." << endl
165        << endl
166        << "INPUT:" << endl
167        << "  An AIDL file." << endl
168        << endl
169        << "OUTPUT:" << endl
170        << "  Path to the generated Java or C++ source file. This is ignored when" << endl
171        << "  -o or --out is specified or the number of the input files are" << endl
172        << "  more than one." << endl
173        << "  For Java, if omitted, Java source file is generated at the same" << endl
174        << "  place as the input AIDL file," << endl
175        << endl
176        << "HEADER_DIR:" << endl
177        << "  Path to where C++ headers are generated." << endl;
178   return sstr.str();
179 }
180 
to_string(Options::Language language)181 string to_string(Options::Language language) {
182   switch (language) {
183     case Options::Language::CPP:
184       return "cpp";
185     case Options::Language::JAVA:
186       return "java";
187     case Options::Language::NDK:
188       return "ndk";
189     case Options::Language::RUST:
190       return "rust";
191     case Options::Language::CPP_ANALYZER:
192       return "cpp-analyzer";
193     case Options::Language::UNSPECIFIED:
194       return "unspecified";
195     default:
196       AIDL_FATAL(AIDL_LOCATION_HERE)
197           << "Unexpected Options::Language enumerator: " << static_cast<size_t>(language);
198   }
199 }
200 
StabilityFromString(const std::string & stability,Stability * out_stability)201 bool Options::StabilityFromString(const std::string& stability, Stability* out_stability) {
202   if (stability == "vintf") {
203     *out_stability = Stability::VINTF;
204     return true;
205   }
206   return false;
207 }
208 
209 static const std::map<std::string, uint32_t> codeNameToVersion = {
210     {"S", 31},
211     {"Tiramisu", SDK_VERSION_Tiramisu},
212     {"UpsideDownCake", SDK_VERSION_UpsideDownCake},
213     {"VanillaIceCream", SDK_VERSION_VanillaIceCream},
214     {"Baklava", SDK_VERSION_current},
215     // this is an alias for the latest in-development platform version
216     {"current", SDK_VERSION_current},
217     // this is an alias for use of all APIs, including those not in any API surface
218     {"platform_apis", 10001},
219 };
220 
MinSdkVersionFromString(const std::string & str)221 Result<uint32_t> MinSdkVersionFromString(const std::string& str) {
222   uint32_t num;
223   if (!android::base::ParseUint(str, &num, 10000u /* max */)) {
224     if (auto found = codeNameToVersion.find(str); found != codeNameToVersion.end()) {
225       return found->second;
226     }
227     return Errorf("Invalid SDK version: {}", str);
228   }
229   return num;
230 }
231 
DefaultMinSdkVersionForLang(const Options::Language lang)232 static uint32_t DefaultMinSdkVersionForLang(const Options::Language lang) {
233   switch (lang) {
234     case Options::Language::CPP:
235       return DEFAULT_SDK_VERSION_CPP;
236     case Options::Language::JAVA:
237       return DEFAULT_SDK_VERSION_JAVA;
238     case Options::Language::NDK:
239       return DEFAULT_SDK_VERSION_NDK;
240     case Options::Language::RUST:
241       return DEFAULT_SDK_VERSION_RUST;
242     case Options::Language::CPP_ANALYZER:
243       return DEFAULT_SDK_VERSION_CPP;
244     case Options::Language::UNSPECIFIED:
245       return DEFAULT_SDK_VERSION_JAVA;  // The safest option
246     default:
247       AIDL_FATAL(AIDL_LOCATION_HERE)
248           << "Unexpected Options::Language enumerator: " << static_cast<size_t>(lang);
249   }
250 }
251 
From(const string & cmdline)252 Options Options::From(const string& cmdline) {
253   vector<string> args = Split(cmdline, " ");
254   return From(args);
255 }
256 
From(const vector<string> & args)257 Options Options::From(const vector<string>& args) {
258   Options::Language lang = Options::Language::JAVA;
259   int argc = args.size();
260   if (argc >= 1 && args.at(0) == "aidl-cpp") {
261     lang = Options::Language::CPP;
262   }
263   const char* argv[argc + 1];
264   for (int i = 0; i < argc; i++) {
265     argv[i] = args.at(i).c_str();
266   }
267   argv[argc] = nullptr;
268 
269   return Options(argc, argv, lang);
270 }
271 
ComputeRawArgs(int argc,const char * const raw_argv[])272 static std::string ComputeRawArgs(int argc, const char* const raw_argv[]) {
273   std::vector<std::string> args;
274   for (int i = 0; i < argc; i++) {
275     // First pass. This is mostly for devs to understand where files come from, and
276     // there may be more complicated rules, but we can at least do better than the
277     // typical paste that would break args with spaces in them.
278     args.push_back(StringReplace(raw_argv[i], " ", "\\ ", true));
279   }
280 
281   return Join(args, " ");
282 }
283 
ToCanonicalDirectory(const std::string & optarg)284 static std::string ToCanonicalDirectory(const std::string& optarg) {
285   std::string dir = Trim(optarg);
286   if (!dir.empty() && dir.back() != OS_PATH_SEPARATOR) {
287     dir.push_back(OS_PATH_SEPARATOR);
288   }
289   return dir;
290 }
291 
PlusImportDir(const std::string & import_dir) const292 Options Options::PlusImportDir(const std::string& import_dir) const {
293   Options copy(*this);
294   copy.import_dirs_.insert(ToCanonicalDirectory(import_dir));
295   return copy;
296 }
297 
Options(int argc,const char * const raw_argv[],Options::Language default_lang)298 Options::Options(int argc, const char* const raw_argv[], Options::Language default_lang)
299     : myname_(argc >= 1 ? raw_argv[0] : "aidl"),
300       raw_args_(ComputeRawArgs(argc, raw_argv)),
301       language_(default_lang) {
302   std::vector<const char*> argv = warning_options_.Parse(argc, raw_argv, error_message_);
303   if (!Ok()) return;
304 
305   argc = argv.size();
306   argv.push_back(nullptr);
307 
308   bool lang_option_found = false;
309   optind = 0;
310   while (true) {
311     static struct option long_options[] = {
312         {"lang", required_argument, 0, 'l'},
313         {"preprocess", no_argument, 0, 's'},
314         {"dumpapi", no_argument, 0, 'u'},
315         {"no_license", no_argument, 0, 'x'},
316         {"checkapi", optional_argument, 0, 'A'},
317         {"apimapping", required_argument, 0, 'i'},
318         {"include", required_argument, 0, 'I'},
319         {"next_include", required_argument, 0, 'N'},
320         {"preprocessed", required_argument, 0, 'p'},
321         {"dep", required_argument, 0, 'd'},
322         {"out", required_argument, 0, 'o'},
323         {"header_out", required_argument, 0, 'h'},
324         {"ninja", no_argument, 0, 'n'},
325         {"rpc", no_argument, 0, 'r'},
326         {"mockall", no_argument, 0, 'M'},
327         {"stability", required_argument, 0, 'Y'},
328         {"omit_invocation", no_argument, 0, 'O'},
329         {"min_sdk_version", required_argument, 0, 'm'},
330         {"structured", no_argument, 0, 'S'},
331         {"trace", no_argument, 0, 't'},
332         {"transaction_names", no_argument, 0, 'c'},
333         {"previous_api_dir", required_argument, 0, 'f'},
334         {"version", required_argument, 0, 'v'},
335         {"log", no_argument, 0, 'L'},
336         {"hash", required_argument, 0, 'H'},
337         {"previous_hash", required_argument, 0, 'P'},
338         {"help", no_argument, 0, 'e'},
339         {0, 0, 0, 0},
340     };
341     const int c = getopt_long(argc, const_cast<char* const*>(argv.data()),
342                               "I:N:p:d:o:h:abtv:i:", long_options, nullptr);
343     if (c == -1) {
344       // no more options
345       break;
346     }
347     switch (c) {
348       case 'l':
349         if (language_ == Options::Language::CPP) {
350           // aidl-cpp can't set language. aidl-cpp exists only for backwards
351           // compatibility.
352           error_message_ << "aidl-cpp does not support --lang." << endl;
353           return;
354         } else {
355           lang_option_found = true;
356           string lang = Trim(optarg);
357           if (lang == "java") {
358             language_ = Options::Language::JAVA;
359             task_ = Options::Task::COMPILE;
360           } else if (lang == "cpp") {
361             language_ = Options::Language::CPP;
362             task_ = Options::Task::COMPILE;
363           } else if (lang == "ndk") {
364             language_ = Options::Language::NDK;
365             task_ = Options::Task::COMPILE;
366           } else if (lang == "rust") {
367             language_ = Options::Language::RUST;
368             task_ = Options::Task::COMPILE;
369           } else if (lang == "cpp-analyzer") {
370             language_ = Options::Language::CPP_ANALYZER;
371             task_ = Options::Task::COMPILE;
372           } else {
373             error_message_ << "Unsupported language: '" << lang << "'" << endl;
374             return;
375           }
376         }
377         break;
378       case 's':
379         task_ = Options::Task::PREPROCESS;
380         break;
381       case 'u':
382         task_ = Options::Task::DUMP_API;
383         break;
384       case 'x':
385         dump_no_license_ = true;
386         break;
387       case 'A':
388         task_ = Options::Task::CHECK_API;
389         // to ensure that all parcelables in the api dumpes are structured
390         structured_ = true;
391         if (optarg) {
392           if (strcmp(optarg, "compatible") == 0)
393             check_api_level_ = CheckApiLevel::COMPATIBLE;
394           else if (strcmp(optarg, "equal") == 0)
395             check_api_level_ = CheckApiLevel::EQUAL;
396           else {
397             error_message_ << "Unsupported --checkapi level: '" << optarg << "'" << endl;
398             return;
399           }
400         }
401         break;
402       case 'I': {
403         // imports for dependencies
404         import_dirs_.emplace(ToCanonicalDirectory(optarg));
405         previous_import_dirs_.emplace(ToCanonicalDirectory(optarg));
406         break;
407       }
408       case 'p':
409         preprocessed_files_.emplace_back(Trim(optarg));
410         break;
411       case 'd':
412         dependency_file_ = Trim(optarg);
413         break;
414       case 'o':
415         output_dir_ = ToCanonicalDirectory(optarg);
416         break;
417       case 'N':
418         import_dirs_.emplace(ToCanonicalDirectory(optarg));
419         break;
420       case 'f':
421         previous_api_dir_ = ToCanonicalDirectory(optarg);
422         previous_import_dirs_.emplace(ToCanonicalDirectory(optarg));
423         break;
424       case 'O':
425         raw_args_ = "cmd not shown due to `--omit_invocation`";
426         break;
427       case 'h':
428         output_header_dir_ = ToCanonicalDirectory(optarg);
429         break;
430       case 'n':
431         dependency_file_ninja_ = true;
432         break;
433       case 'S':
434         structured_ = true;
435         break;
436       case 'Y': {
437         const string stability_str = Trim(optarg);
438         if (!StabilityFromString(stability_str, &stability_)) {
439           error_message_ << "Unrecognized stability level: '" << stability_str
440                          << "'. Must be vintf." << endl;
441           return;
442         }
443         break;
444       }
445       case 'm':
446         if (auto ret = MinSdkVersionFromString(Trim(optarg)); ret.ok()) {
447           min_sdk_version_ = *ret;
448         } else {
449           error_message_ << ret.error();
450           return;
451         }
452         break;
453       case 'r':
454         gen_rpc_ = true;
455         break;
456       case 'M':
457         gen_mockall_ = true;
458         break;
459       case 't':
460         gen_traces_ = true;
461         break;
462       case 'a':
463         auto_dep_file_ = true;
464         break;
465       case 'b':
466         fail_on_parcelable_ = true;
467         break;
468       case 'c':
469         gen_transaction_names_ = true;
470         break;
471       case 'v': {
472         const string ver_str = Trim(optarg);
473         int ver = atoi(ver_str.c_str());
474         if (ver > 0) {
475           version_ = ver;
476         } else {
477           error_message_ << "Invalid version number: '" << ver_str << "'. "
478                          << "Version must be a positive natural number." << endl;
479           return;
480         }
481         break;
482       }
483       case 'H':
484         hash_ = Trim(optarg);
485         break;
486       case 'P':
487         previous_hash_ = Trim(optarg);
488         break;
489       case 'L':
490         gen_log_ = true;
491         break;
492       case 'e':
493         std::cerr << GetUsage();
494         task_ = Task::HELP;
495         CHECK(Ok());
496         return;
497       case 'i':
498         output_file_ = Trim(optarg);
499         task_ = Task::DUMP_MAPPINGS;
500         break;
501       default:
502         error_message_ << GetUsage();
503         CHECK(!Ok());
504         return;
505     }
506   }  // while
507 
508   // Positional arguments
509   if (!lang_option_found && task_ == Options::Task::COMPILE) {
510     // the legacy arguments format
511     if (argc - optind <= 0) {
512       error_message_ << "No input file" << endl;
513       return;
514     }
515     if (language_ == Options::Language::JAVA || language_ == Options::Language::RUST) {
516       input_files_.emplace_back(argv[optind++]);
517       if (argc - optind >= 1) {
518         output_file_ = argv[optind++];
519       } else if (output_dir_.empty()) {
520         // when output is omitted and -o option isn't set, the output is by
521         // default set to the input file path with .aidl is replaced to .java.
522         // If -o option is set, the output path is calculated by
523         // GetOutputFilePath which returns "<output_dir>/<package/name>/
524         // <typename>.java"
525         output_file_ = input_files_.front();
526         if (android::base::EndsWith(output_file_, ".aidl")) {
527           output_file_ = output_file_.substr(0, output_file_.length() - strlen(".aidl"));
528         }
529         output_file_ += (language_ == Options::Language::JAVA) ? ".java" : ".rs";
530       }
531     } else if (IsCppOutput()) {
532       input_files_.emplace_back(argv[optind++]);
533       if (argc - optind < 2) {
534         error_message_ << "No HEADER_DIR or OUTPUT." << endl;
535         return;
536       }
537       output_header_dir_ = ToCanonicalDirectory(argv[optind++]);
538       output_file_ = argv[optind++];
539     }
540     if (argc - optind > 0) {
541       error_message_ << "Too many arguments: ";
542       for (int i = optind; i < argc; i++) {
543         error_message_ << " " << argv[i];
544       }
545       error_message_ << endl;
546     }
547   } else {
548     // the new arguments format
549     if (task_ == Options::Task::COMPILE || task_ == Options::Task::DUMP_API ||
550         task_ == Options::Task::DUMP_MAPPINGS) {
551       if (argc - optind < 1) {
552         error_message_ << "No input file." << endl;
553         return;
554       }
555     } else {
556       if (argc - optind < 2) {
557         error_message_ << "Insufficient arguments. At least 2 required, but "
558                        << "got " << (argc - optind) << "." << endl;
559         return;
560       }
561       if (task_ != Options::Task::CHECK_API) {
562         output_file_ = argv[optind++];
563       }
564     }
565     while (optind < argc) {
566       input_files_.emplace_back(argv[optind++]);
567     }
568   }
569 
570   // filter out invalid combinations
571   if (lang_option_found) {
572     if (IsCppOutput() && task_ == Options::Task::COMPILE) {
573       if (output_dir_.empty()) {
574         error_message_ << "Output directory is not set. Set with --out." << endl;
575         return;
576       }
577       if (output_header_dir_.empty()) {
578         error_message_ << "Header output directory is not set. Set with "
579                        << "--header_out." << endl;
580         return;
581       }
582     }
583     if (language_ == Options::Language::JAVA && task_ == Options::Task::COMPILE) {
584       if (output_dir_.empty()) {
585         error_message_ << "Output directory is not set. Set with --out." << endl;
586         return;
587       }
588       if (!output_header_dir_.empty()) {
589         error_message_ << "Header output directory is set, which does not make "
590                        << "sense for Java." << endl;
591         return;
592       }
593     }
594     if (language_ == Options::Language::RUST && task_ == Options::Task::COMPILE) {
595       if (output_dir_.empty()) {
596         error_message_ << "Output directory is not set. Set with --out." << endl;
597         return;
598       }
599       if (!output_header_dir_.empty()) {
600         error_message_ << "Header output directory is set, which does not make "
601                        << "sense for Rust." << endl;
602         return;
603       }
604     }
605   }
606   if (!previous_api_dir_.empty()) {
607     if (previous_hash_.empty()) {
608       error_message_ << "--previous_hash must be set if --previous_api_dir is set" << endl;
609       return;
610     }
611   } else {
612     if (!previous_hash_.empty()) {
613       error_message_ << "--previous_hash must not be set if --previous_api_dir is not set" << endl;
614       return;
615     }
616   }
617 
618   if (task_ == Options::Task::COMPILE) {
619     for (const string& input : input_files_) {
620       if (!android::base::EndsWith(input, ".aidl")) {
621         error_message_ << "Expected .aidl file for input but got '" << input << "'" << endl;
622         return;
623       }
624     }
625     if (!output_file_.empty() && input_files_.size() > 1) {
626       error_message_ << "Multiple AIDL files can't be compiled to a single "
627                      << "output file '" << output_file_ << "'. "
628                      << "Use --out=DIR instead for output files." << endl;
629       return;
630     }
631     if (!dependency_file_.empty() && input_files_.size() > 1) {
632       error_message_ << "-d or --dep doesn't work when compiling multiple AIDL "
633                      << "files. Use '-a' to generate dependency file next to "
634                      << "the output file with the name based on the input "
635                      << "file." << endl;
636       return;
637     }
638     if (gen_log_ && (language_ != Options::Language::CPP && language_ != Options::Language::NDK)) {
639       error_message_ << "--log is currently supported for either --lang=cpp or --lang=ndk" << endl;
640       return;
641     }
642   }
643   if (task_ == Options::Task::PREPROCESS) {
644     if (version_ > 0) {
645       error_message_ << "--version should not be used with '--preprocess'." << endl;
646       return;
647     }
648   }
649   if (task_ == Options::Task::CHECK_API) {
650     if (input_files_.size() != 2) {
651       error_message_ << "--checkapi requires two inputs for comparing, "
652                      << "but got " << input_files_.size() << "." << endl;
653       return;
654     }
655   }
656   if (task_ == Options::Task::DUMP_API) {
657     if (output_dir_.empty()) {
658       error_message_ << "--dumpapi requires output directory. Use --out." << endl;
659       return;
660     }
661   }
662   if (task_ != Options::Task::COMPILE) {
663     if (min_sdk_version_ != 0) {
664       error_message_ << "--min_sdk_version is available only for compilation." << endl;
665       return;
666     }
667     // For other tasks, use "current"
668     min_sdk_version_ = MinSdkVersionFromString("current").value();
669   }
670 
671   uint32_t default_ver = DefaultMinSdkVersionForLang(language_);
672   if (min_sdk_version_ == 0) {  // --min_sdk_version flag not specified
673     min_sdk_version_ = default_ver;
674   } else if (min_sdk_version_ < default_ver) {
675     error_message_ << "Min SDK version should at least be " << default_ver << "." << endl;
676     return;
677   }
678 
679   uint32_t rpc_version = MinSdkVersionFromString("Tiramisu").value();
680   // note: we would like to always generate (Java) code to support RPC out of
681   // the box, but doing so causes an unclear error for people trying to use RPC
682   // - now we require them to add the gen_rpc build rule and get this clear message.
683   if (gen_rpc_ && min_sdk_version_ < rpc_version) {
684     error_message_ << "RPC code requires minimum SDK version of at least " << rpc_version << endl;
685     return;
686   }
687 
688   if (min_sdk_version_ >= rpc_version) gen_rpc_ = true;
689 
690   AIDL_FATAL_IF(!output_dir_.empty() && output_dir_.back() != OS_PATH_SEPARATOR, output_dir_);
691   AIDL_FATAL_IF(!output_header_dir_.empty() && output_header_dir_.back() != OS_PATH_SEPARATOR,
692                 output_header_dir_);
693 }
694 
Parse(int argc,const char * const raw_argv[],ErrorMessage & error_message)695 std::vector<const char*> WarningOptions::Parse(int argc, const char* const raw_argv[],
696                                                ErrorMessage& error_message) {
697   std::vector<const char*> remains;
698   for (int i = 0; i < argc; i++) {
699     auto arg = raw_argv[i];
700     if (strcmp(arg, "-Weverything") == 0) {
701       enable_all_ = true;
702     } else if (strcmp(arg, "-Werror") == 0) {
703       as_errors_ = true;
704     } else if (strcmp(arg, "-w") == 0) {
705       disable_all_ = true;
706     } else if (base::StartsWith(arg, "-Wno-error=")) {
707       no_errors_.insert(arg + strlen("-Wno-error="));
708     } else if (base::StartsWith(arg, "-Wno-")) {
709       disabled_.insert(arg + strlen("-Wno-"));
710     } else if (base::StartsWith(arg, "-W")) {
711       enabled_.insert(arg + strlen("-W"));
712     } else {
713       remains.push_back(arg);
714     }
715   }
716 
717   for (const auto& names : {no_errors_, disabled_, enabled_}) {
718     for (const auto& name : names) {
719       if (kAllDiagnostics.count(name) == 0) {
720         error_message << "unknown warning: " << name << "\n";
721         return {};
722       }
723     }
724   }
725 
726   return remains;
727 }
728 
GetDiagnosticMapping() const729 DiagnosticMapping WarningOptions::GetDiagnosticMapping() const {
730   DiagnosticMapping mapping;
731   for (const auto& [_, d] : kAllDiagnostics) {
732     bool enabled = d.default_enabled;
733     if (enable_all_ || enabled_.find(d.name) != enabled_.end()) {
734       enabled = true;
735     }
736     if (disable_all_ || disabled_.find(d.name) != disabled_.end()) {
737       enabled = false;
738     }
739 
740     DiagnosticSeverity severity = DiagnosticSeverity::DISABLED;
741     if (enabled) {
742       severity = DiagnosticSeverity::WARNING;
743       if (as_errors_ && no_errors_.find(d.name) == no_errors_.end()) {
744         severity = DiagnosticSeverity::ERROR;
745       }
746     }
747     mapping.Severity(d.id, severity);
748   }
749   return mapping;
750 }
751 
752 }  // namespace aidl
753 }  // namespace android
754