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 <cstring>
20 #include <iostream>
21 #include <stdio.h>
22 
23 #include "logging.h"
24 #include "os.h"
25 
26 using std::cerr;
27 using std::endl;
28 using std::string;
29 using std::unique_ptr;
30 using std::vector;
31 
32 namespace android {
33 namespace aidl {
34 namespace {
35 
java_usage()36 unique_ptr<JavaOptions> java_usage() {
37   fprintf(stderr,
38           "usage: aidl OPTIONS INPUT [OUTPUT]\n"
39           "       aidl --preprocess OUTPUT INPUT...\n"
40           "\n"
41           "OPTIONS:\n"
42           "   -I<DIR>    search path for import statements.\n"
43           "   -d<FILE>   generate dependency file.\n"
44           "   -a         generate dependency file next to the output file with "
45           "the name based on the input file.\n"
46           "   -p<FILE>   file created by --preprocess to import.\n"
47           "   -o<FOLDER> base output folder for generated files.\n"
48           "   -b         fail when trying to compile a parcelable.\n"
49           "\n"
50           "INPUT:\n"
51           "   An aidl interface file.\n"
52           "\n"
53           "OUTPUT:\n"
54           "   The generated interface files.\n"
55           "   If omitted and the -o option is not used, the input filename is "
56           "used, with the .aidl extension changed to a .java extension.\n"
57           "   If the -o option is used, the generated files will be placed in "
58           "the base output folder, under their package folder\n");
59   return unique_ptr<JavaOptions>(nullptr);
60 }
61 
62 }  // namespace
63 
Parse(int argc,const char * const * argv)64 unique_ptr<JavaOptions> JavaOptions::Parse(int argc, const char* const* argv) {
65   unique_ptr<JavaOptions> options(new JavaOptions());
66   int i = 1;
67 
68   if (argc >= 2 && 0 == strcmp(argv[1], "--preprocess")) {
69     if (argc < 4) {
70       return java_usage();
71     }
72     options->output_file_name_ = argv[2];
73     for (int i = 3; i < argc; i++) {
74       options->files_to_preprocess_.push_back(argv[i]);
75     }
76     options->task = PREPROCESS_AIDL;
77     return options;
78   }
79 
80   options->task = COMPILE_AIDL_TO_JAVA;
81   // OPTIONS
82   while (i < argc) {
83     const char* s = argv[i];
84     const size_t len = strlen(s);
85     if (s[0] != '-') {
86       break;
87     }
88     if (len <= 1) {
89       fprintf(stderr, "unknown option (%d): %s\n", i, s);
90       return java_usage();
91     }
92     // -I<system-import-path>
93     if (s[1] == 'I') {
94       if (len > 2) {
95         options->import_paths_.push_back(s + 2);
96       } else {
97         fprintf(stderr, "-I option (%d) requires a path.\n", i);
98         return java_usage();
99       }
100     } else if (s[1] == 'd') {
101       if (len > 2) {
102         options->dep_file_name_ = s + 2;
103       } else {
104         fprintf(stderr, "-d option (%d) requires a file.\n", i);
105         return java_usage();
106       }
107     } else if (strcmp(s, "-a") == 0) {
108       options->auto_dep_file_ = true;
109     } else if (s[1] == 'p') {
110       if (len > 2) {
111         options->preprocessed_files_.push_back(s + 2);
112       } else {
113         fprintf(stderr, "-p option (%d) requires a file.\n", i);
114         return java_usage();
115       }
116     } else if (s[1] == 'o') {
117       if (len > 2) {
118         options->output_base_folder_= s + 2;
119       } else {
120         fprintf(stderr, "-o option (%d) requires a path.\n", i);
121         return java_usage();
122       }
123     } else if (strcmp(s, "-b") == 0) {
124       options->fail_on_parcelable_ = true;
125     } else {
126       // s[1] is not known
127       fprintf(stderr, "unknown option (%d): %s\n", i, s);
128       return java_usage();
129     }
130     i++;
131   }
132   // INPUT
133   if (i < argc) {
134     options->input_file_name_ = argv[i];
135     i++;
136   } else {
137     fprintf(stderr, "INPUT required\n");
138     return java_usage();
139   }
140   if (!EndsWith(options->input_file_name_, ".aidl")) {
141     cerr << "Expected .aidl file for input but got "
142          << options->input_file_name_ << endl;
143     return java_usage();
144   }
145 
146   // OUTPUT
147   if (i < argc) {
148     options->output_file_name_ = argv[i];
149     i++;
150   } else if (options->output_base_folder_.empty()) {
151     // copy input into output and change the extension from .aidl to .java
152     options->output_file_name_= options->input_file_name_;
153     if (!ReplaceSuffix(".aidl", ".java", &options->output_file_name_)) {
154       // we should never get here since we validated the suffix.
155       LOG(FATAL) << "Internal aidl error.";
156       return java_usage();
157     }
158   }
159 
160   // anything remaining?
161   if (i != argc) {
162     fprintf(stderr, "unknown option%s:",
163             (i == argc - 1 ? (const char*)"" : (const char*)"s"));
164     for (; i < argc - 1; i++) {
165       fprintf(stderr, " %s", argv[i]);
166     }
167     fprintf(stderr, "\n");
168     return java_usage();
169   }
170 
171   return options;
172 }
173 
DependencyFilePath() const174 string JavaOptions::DependencyFilePath() const {
175   if (auto_dep_file_) {
176     return output_file_name_ + ".d";
177   }
178   return dep_file_name_;
179 }
180 
181 namespace {
182 
cpp_usage()183 unique_ptr<CppOptions> cpp_usage() {
184   cerr << "usage: aidl-cpp INPUT_FILE HEADER_DIR OUTPUT_FILE" << endl
185        << endl
186        << "OPTIONS:" << endl
187        << "   -I<DIR>   search path for import statements" << endl
188        << "   -d<FILE>  generate dependency file" << endl
189        << endl
190        << "INPUT_FILE:" << endl
191        << "   an aidl interface file" << endl
192        << "HEADER_DIR:" << endl
193        << "   empty directory to put generated headers" << endl
194        << "OUTPUT_FILE:" << endl
195        << "   path to write generated .cpp code" << endl;
196   return unique_ptr<CppOptions>(nullptr);
197 }
198 
199 }  // namespace
200 
Parse(int argc,const char * const * argv)201 unique_ptr<CppOptions> CppOptions::Parse(int argc, const char* const* argv) {
202   unique_ptr<CppOptions> options(new CppOptions());
203   int i = 1;
204 
205   // Parse flags, all of which start with '-'
206   for ( ; i < argc; ++i) {
207     const size_t len = strlen(argv[i]);
208     const char *s = argv[i];
209     if (s[0] != '-') {
210       break;  // On to the positional arguments.
211     }
212     if (len < 2) {
213       cerr << "Invalid argument '" << s << "'." << endl;
214       return cpp_usage();
215     }
216     const string the_rest = s + 2;
217     if (s[1] == 'I') {
218       options->import_paths_.push_back(the_rest);
219     } else if (s[1] == 'd') {
220       options->dep_file_name_ = the_rest;
221     } else {
222       cerr << "Invalid argument '" << s << "'." << endl;
223       return cpp_usage();
224     }
225   }
226 
227   // There are exactly three positional arguments.
228   const int remaining_args = argc - i;
229   if (remaining_args != 3) {
230     cerr << "Expected 3 positional arguments but got " << remaining_args << "." << endl;
231     return cpp_usage();
232   }
233 
234   options->input_file_name_ = argv[i];
235   options->output_header_dir_ = argv[i + 1];
236   options->output_file_name_ = argv[i + 2];
237 
238   if (!EndsWith(options->input_file_name_, ".aidl")) {
239     cerr << "Expected .aidl file for input but got " << options->input_file_name_ << endl;
240     return cpp_usage();
241   }
242 
243   return options;
244 }
245 
EndsWith(const string & str,const string & suffix)246 bool EndsWith(const string& str, const string& suffix) {
247   if (str.length() < suffix.length()) {
248     return false;
249   }
250   return std::equal(str.crbegin(), str.crbegin() + suffix.length(),
251                     suffix.crbegin());
252 }
253 
ReplaceSuffix(const string & old_suffix,const string & new_suffix,string * str)254 bool ReplaceSuffix(const string& old_suffix,
255                    const string& new_suffix,
256                    string* str) {
257   if (!EndsWith(*str, old_suffix)) return false;
258   str->replace(str->length() - old_suffix.length(),
259                old_suffix.length(),
260                new_suffix);
261   return true;
262 }
263 
264 
265 
266 }  // namespace android
267 }  // namespace aidl
268