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