options.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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. #include "options.h"
  17. #include "logging.h"
  18. #include "os.h"
  19. #include <getopt.h>
  20. #include <stdlib.h>
  21. #include <unistd.h>
  22. #include <algorithm>
  23. #include <iostream>
  24. #include <sstream>
  25. #include <string>
  26. #include <android-base/strings.h>
  27. using android::base::Split;
  28. using android::base::Trim;
  29. using std::endl;
  30. using std::string;
  31. namespace android {
  32. namespace aidl {
  33. string Options::GetUsage() const {
  34. std::ostringstream sstr;
  35. sstr << "usage:" << endl
  36. << myname_ << " --lang={java|cpp} [OPTION]... INPUT..." << endl
  37. << " Generate Java or C++ files for AIDL file(s)." << endl
  38. << endl
  39. << myname_ << " --preprocess OUTPUT INPUT..." << endl
  40. << " Create an AIDL file having declarations of AIDL file(s)." << endl
  41. << endl
  42. #ifndef _WIN32
  43. << myname_ << " --dumpapi --out=DIR INPUT..." << endl
  44. << " Dump API signature of AIDL file(s) to DIR." << endl
  45. << endl
  46. << myname_ << " --checkapi OLD_DIR NEW_DIR" << endl
  47. << " Checkes whether API dump NEW_DIR is backwards compatible extension " << endl
  48. << " of the API dump OLD_DIR." << endl
  49. #endif
  50. << endl;
  51. // Legacy option formats
  52. if (language_ == Options::Language::JAVA) {
  53. sstr << myname_ << " [OPTION]... INPUT [OUTPUT]" << endl
  54. << " Generate a Java file for an AIDL file." << endl
  55. << endl;
  56. } else if (language_ == Options::Language::CPP) {
  57. sstr << myname_ << " [OPTION]... INPUT HEADER_DIR OUTPUT" << endl
  58. << " Generate C++ headers and source for an AIDL file." << endl
  59. << endl;
  60. }
  61. sstr << "OPTION:" << endl
  62. << " -I DIR, --include=DIR" << endl
  63. << " Use DIR as a search path for import statements." << endl
  64. << " -m FILE, --import=FILE" << endl
  65. << " Import FILE directly without searching in the search paths." << endl
  66. << " -p FILE, --preprocessed=FILE" << endl
  67. << " Include FILE which is created by --preprocess." << endl
  68. << " -d FILE, --dep=FILE" << endl
  69. << " Generate dependency file as FILE. Don't use this when" << endl
  70. << " there are multiple input files. Use -a then." << endl
  71. << " -o DIR, --out=DIR" << endl
  72. << " Use DIR as the base output directory for generated files." << endl
  73. << " -h DIR, --header_out=DIR" << endl
  74. << " Generate C++ headers under DIR." << endl
  75. << " -a" << endl
  76. << " Generate dependency file next to the output file with the" << endl
  77. << " name based on the input file." << endl
  78. << " -b" << endl
  79. << " Trigger fail when trying to compile a parcelable." << endl
  80. << " --ninja" << endl
  81. << " Generate dependency file in a format ninja understands." << endl
  82. << " --structured" << endl
  83. << " Whether this interface is defined exclusively in AIDL." << endl
  84. << " It is therefore a candidate for stabilization." << endl
  85. << " -t, --trace" << endl
  86. << " Include tracing code for systrace. Note that if either" << endl
  87. << " the client or service code is not auto-generated by this" << endl
  88. << " tool, that part will not be traced." << endl
  89. << " --transaction_names" << endl
  90. << " Generate transaction names." << endl
  91. << " --apimapping" << endl
  92. << " Generates a mapping of declared aidl method signatures to" << endl
  93. << " the original line number. e.g.: " << endl
  94. << " If line 39 of foo/bar/IFoo.aidl contains:"
  95. << " void doFoo(int bar, String baz);" << endl
  96. << " Then the result would be:" << endl
  97. << " foo.bar.Baz|doFoo|int,String,|void" << endl
  98. << " foo/bar/IFoo.aidl:39" << endl
  99. << " -v VER, --version=VER" << endl
  100. << " Set the version of the interface and parcelable to VER." << endl
  101. << " VER must be an interger greater than 0." << endl
  102. << " --log" << endl
  103. << " Information about the transaction, e.g., method name, argument" << endl
  104. << " values, execution time, etc., is provided via callback." << endl
  105. << " --help" << endl
  106. << " Show this help." << endl
  107. << endl
  108. << "INPUT:" << endl
  109. << " An AIDL file." << endl
  110. << endl
  111. << "OUTPUT:" << endl
  112. << " Path to the generated Java or C++ source file. This is ignored when" << endl
  113. << " -o or --out is specified or the number of the input files are" << endl
  114. << " more than one." << endl
  115. << " For Java, if omitted, Java source file is generated at the same" << endl
  116. << " place as the input AIDL file," << endl
  117. << endl
  118. << "HEADER_DIR:" << endl
  119. << " Path to where C++ headers are generated." << endl;
  120. return sstr.str();
  121. }
  122. Options Options::From(const string& cmdline) {
  123. vector<string> args = Split(cmdline, " ");
  124. return From(args);
  125. }
  126. Options Options::From(const vector<string>& args) {
  127. Options::Language lang = Options::Language::JAVA;
  128. int argc = args.size();
  129. if (argc >= 1 && args.at(0) == "aidl-cpp") {
  130. lang = Options::Language::CPP;
  131. }
  132. const char* argv[argc + 1];
  133. for (int i = 0; i < argc; i++) {
  134. argv[i] = args.at(i).c_str();
  135. }
  136. argv[argc] = nullptr;
  137. return Options(argc, argv, lang);
  138. }
  139. Options::Options(int argc, const char* const argv[], Options::Language default_lang)
  140. : myname_(argv[0]), language_(default_lang) {
  141. bool lang_option_found = false;
  142. optind = 0;
  143. while (true) {
  144. static struct option long_options[] = {
  145. {"lang", required_argument, 0, 'l'},
  146. {"preprocess", no_argument, 0, 's'},
  147. #ifndef _WIN32
  148. {"dumpapi", no_argument, 0, 'u'},
  149. {"checkapi", no_argument, 0, 'A'},
  150. #endif
  151. {"apimapping", required_argument, 0, 'i'},
  152. {"include", required_argument, 0, 'I'},
  153. {"import", required_argument, 0, 'm'},
  154. {"preprocessed", required_argument, 0, 'p'},
  155. {"dep", required_argument, 0, 'd'},
  156. {"out", required_argument, 0, 'o'},
  157. {"header_out", required_argument, 0, 'h'},
  158. {"ninja", no_argument, 0, 'n'},
  159. {"structured", no_argument, 0, 'S'},
  160. {"trace", no_argument, 0, 't'},
  161. {"transaction_names", no_argument, 0, 'c'},
  162. {"version", required_argument, 0, 'v'},
  163. {"log", no_argument, 0, 'L'},
  164. {"help", no_argument, 0, 'e'},
  165. {0, 0, 0, 0},
  166. };
  167. const int c = getopt_long(argc, const_cast<char* const*>(argv),
  168. "I:m:p:d:o:h:abtv:", long_options, nullptr);
  169. if (c == -1) {
  170. // no more options
  171. break;
  172. }
  173. switch (c) {
  174. case 'l':
  175. if (language_ == Options::Language::CPP) {
  176. // aidl-cpp can't set language. aidl-cpp exists only for backwards
  177. // compatibility.
  178. error_message_ << "aidl-cpp does not support --lang." << endl;
  179. return;
  180. } else {
  181. lang_option_found = true;
  182. string lang = Trim(optarg);
  183. if (lang == "java") {
  184. language_ = Options::Language::JAVA;
  185. task_ = Options::Task::COMPILE;
  186. } else if (lang == "cpp") {
  187. language_ = Options::Language::CPP;
  188. task_ = Options::Task::COMPILE;
  189. } else if (lang == "ndk") {
  190. language_ = Options::Language::NDK;
  191. task_ = Options::Task::COMPILE;
  192. } else {
  193. error_message_ << "Unsupported language: '" << lang << "'" << endl;
  194. return;
  195. }
  196. }
  197. break;
  198. case 's':
  199. if (task_ != Options::Task::UNSPECIFIED) {
  200. task_ = Options::Task::PREPROCESS;
  201. }
  202. break;
  203. #ifndef _WIN32
  204. case 'u':
  205. if (task_ != Options::Task::UNSPECIFIED) {
  206. task_ = Options::Task::DUMP_API;
  207. }
  208. break;
  209. case 'A':
  210. if (task_ != Options::Task::UNSPECIFIED) {
  211. task_ = Options::Task::CHECK_API;
  212. // to ensure that all parcelables in the api dumpes are structured
  213. structured_ = true;
  214. }
  215. break;
  216. #endif
  217. case 'I': {
  218. import_dirs_.emplace(Trim(optarg));
  219. break;
  220. }
  221. case 'm': {
  222. import_files_.emplace(Trim(optarg));
  223. break;
  224. }
  225. case 'p':
  226. preprocessed_files_.emplace_back(Trim(optarg));
  227. break;
  228. case 'd':
  229. dependency_file_ = Trim(optarg);
  230. break;
  231. case 'o':
  232. output_dir_ = Trim(optarg);
  233. if (output_dir_.back() != OS_PATH_SEPARATOR) {
  234. output_dir_.push_back(OS_PATH_SEPARATOR);
  235. }
  236. break;
  237. case 'h':
  238. output_header_dir_ = Trim(optarg);
  239. if (output_header_dir_.back() != OS_PATH_SEPARATOR) {
  240. output_header_dir_.push_back(OS_PATH_SEPARATOR);
  241. }
  242. break;
  243. case 'n':
  244. dependency_file_ninja_ = true;
  245. break;
  246. case 'S':
  247. structured_ = true;
  248. break;
  249. case 't':
  250. gen_traces_ = true;
  251. break;
  252. case 'a':
  253. auto_dep_file_ = true;
  254. break;
  255. case 'b':
  256. fail_on_parcelable_ = true;
  257. break;
  258. case 'c':
  259. gen_transaction_names_ = true;
  260. break;
  261. case 'v': {
  262. const string ver_str = Trim(optarg);
  263. int ver = atoi(ver_str.c_str());
  264. if (ver > 0) {
  265. version_ = ver;
  266. } else {
  267. error_message_ << "Invalid version number: '" << ver_str << "'. "
  268. << "Version must be a positive natural number." << endl;
  269. return;
  270. }
  271. break;
  272. }
  273. case 'L':
  274. gen_log_ = true;
  275. break;
  276. case 'e':
  277. std::cerr << GetUsage();
  278. exit(0);
  279. case 'i':
  280. output_file_ = Trim(optarg);
  281. task_ = Task::DUMP_MAPPINGS;
  282. break;
  283. default:
  284. std::cerr << GetUsage();
  285. exit(1);
  286. }
  287. } // while
  288. // Positional arguments
  289. if (!lang_option_found && task_ == Options::Task::COMPILE) {
  290. // the legacy arguments format
  291. if (argc - optind <= 0) {
  292. error_message_ << "No input file" << endl;
  293. return;
  294. }
  295. if (language_ == Options::Language::JAVA) {
  296. input_files_.emplace_back(argv[optind++]);
  297. if (argc - optind >= 1) {
  298. output_file_ = argv[optind++];
  299. } else if (output_dir_.empty()) {
  300. // when output is omitted and -o option isn't set, the output is by
  301. // default set to the input file path with .aidl is replaced to .java.
  302. // If -o option is set, the output path is calculated by
  303. // generate_outputFileName which returns "<output_dir>/<package/name>/
  304. // <typename>.java"
  305. output_file_ = input_files_.front();
  306. if (android::base::EndsWith(output_file_, ".aidl")) {
  307. output_file_ = output_file_.substr(0, output_file_.length() - strlen(".aidl"));
  308. }
  309. output_file_ += ".java";
  310. }
  311. } else if (IsCppOutput()) {
  312. input_files_.emplace_back(argv[optind++]);
  313. if (argc - optind < 2) {
  314. error_message_ << "No HEADER_DIR or OUTPUT." << endl;
  315. return;
  316. }
  317. output_header_dir_ = argv[optind++];
  318. if (output_header_dir_.back() != OS_PATH_SEPARATOR) {
  319. output_header_dir_.push_back(OS_PATH_SEPARATOR);
  320. }
  321. output_file_ = argv[optind++];
  322. }
  323. if (argc - optind > 0) {
  324. error_message_ << "Too many arguments: ";
  325. for (int i = optind; i < argc; i++) {
  326. error_message_ << " " << argv[i];
  327. }
  328. error_message_ << endl;
  329. }
  330. } else {
  331. // the new arguments format
  332. if (task_ == Options::Task::COMPILE || task_ == Options::Task::DUMP_API) {
  333. if (argc - optind < 1) {
  334. error_message_ << "No input file." << endl;
  335. return;
  336. }
  337. } else {
  338. if (argc - optind < 2) {
  339. error_message_ << "Insufficient arguments. At least 2 required, but "
  340. << "got " << (argc - optind) << "." << endl;
  341. return;
  342. }
  343. if (task_ != Options::Task::CHECK_API && task_ != Options::Task::DUMP_MAPPINGS) {
  344. output_file_ = argv[optind++];
  345. }
  346. }
  347. while (optind < argc) {
  348. input_files_.emplace_back(argv[optind++]);
  349. }
  350. }
  351. // filter out invalid combinations
  352. if (lang_option_found) {
  353. if (IsCppOutput() && task_ == Options::Task::COMPILE) {
  354. if (output_dir_.empty()) {
  355. error_message_ << "Output directory is not set. Set with --out." << endl;
  356. return;
  357. }
  358. if (output_header_dir_.empty()) {
  359. error_message_ << "Header output directory is not set. Set with "
  360. << "--header_out." << endl;
  361. return;
  362. }
  363. }
  364. if (language_ == Options::Language::JAVA && task_ == Options::Task::COMPILE) {
  365. if (output_dir_.empty()) {
  366. error_message_ << "Output directory is not set. Set with --out." << endl;
  367. return;
  368. }
  369. if (!output_header_dir_.empty()) {
  370. error_message_ << "Header output directory is set, which does not make "
  371. << "sense for Java." << endl;
  372. return;
  373. }
  374. }
  375. }
  376. if (task_ == Options::Task::COMPILE) {
  377. for (const string& input : input_files_) {
  378. if (!android::base::EndsWith(input, ".aidl")) {
  379. error_message_ << "Expected .aidl file for input but got '" << input << "'" << endl;
  380. return;
  381. }
  382. }
  383. if (!output_file_.empty() && input_files_.size() > 1) {
  384. error_message_ << "Multiple AIDL files can't be compiled to a single "
  385. << "output file '" << output_file_ << "'. "
  386. << "Use --out=DIR instead for output files." << endl;
  387. return;
  388. }
  389. if (!dependency_file_.empty() && input_files_.size() > 1) {
  390. error_message_ << "-d or --dep doesn't work when compiling multiple AIDL "
  391. << "files. Use '-a' to generate dependency file next to "
  392. << "the output file with the name based on the input "
  393. << "file." << endl;
  394. return;
  395. }
  396. if (gen_log_ && (language_ != Options::Language::CPP && language_ != Options::Language::NDK)) {
  397. error_message_ << "--log is currently supported for either --lang=cpp or --lang=ndk" << endl;
  398. return;
  399. }
  400. }
  401. if (task_ == Options::Task::PREPROCESS) {
  402. if (version_ > 0) {
  403. error_message_ << "--version should not be used with '--preprocess'." << endl;
  404. return;
  405. }
  406. }
  407. if (task_ == Options::Task::CHECK_API) {
  408. if (input_files_.size() != 2) {
  409. error_message_ << "--checkapi requires two inputs for comparing, "
  410. << "but got " << input_files_.size() << "." << endl;
  411. return;
  412. }
  413. }
  414. if (task_ == Options::Task::DUMP_API) {
  415. if (output_dir_.empty()) {
  416. error_message_ << "--dump_api requires output directory. Use --out." << endl;
  417. return;
  418. }
  419. }
  420. CHECK(output_dir_.empty() || output_dir_.back() == OS_PATH_SEPARATOR);
  421. CHECK(output_header_dir_.empty() || output_header_dir_.back() == OS_PATH_SEPARATOR);
  422. }
  423. } // namespace android
  424. } // namespace aidl