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