Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2014, 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 "clang/Basic/DiagnosticOptions.h"
     18 #include "clang/Driver/DriverDiagnostic.h"
     19 #include "clang/Driver/Options.h"
     20 #include "clang/Frontend/Utils.h"
     21 
     22 #include "llvm/Option/Arg.h"
     23 #include "llvm/Option/ArgList.h"
     24 #include "llvm/Option/Option.h"
     25 #include "llvm/Option/OptTable.h"
     26 #include "llvm/Support/CommandLine.h"
     27 
     28 #include "rs_cc_options.h"
     29 #include "slang.h"
     30 #include "slang_assert.h"
     31 
     32 #include <cstdlib>
     33 #include <string>
     34 #include <utility>
     35 #include <vector>
     36 
     37 enum {
     38   OPT_INVALID = 0,  // This is not an option ID.
     39 #define PREFIX(NAME, VALUE)
     40 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
     41                HELPTEXT, METAVAR)                                             \
     42   OPT_##ID,
     43 #include "RSCCOptions.inc"
     44   LastOption
     45 #undef OPTION
     46 #undef PREFIX
     47 };
     48 
     49 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
     50 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
     51                HELPTEXT, METAVAR)
     52 #include "RSCCOptions.inc"
     53 #undef OPTION
     54 #undef PREFIX
     55 
     56 static const llvm::opt::OptTable::Info RSCCInfoTable[] = {
     57 #define PREFIX(NAME, VALUE)
     58 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
     59                HELPTEXT, METAVAR)                                              \
     60   {                                                                            \
     61     PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
     62         PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS                      \
     63   }                                                                            \
     64   ,
     65 #include "RSCCOptions.inc"
     66 #undef OPTION
     67 #undef PREFIX
     68 };
     69 
     70 namespace {
     71 
     72 class RSCCOptTable : public llvm::opt::OptTable {
     73  public:
     74   RSCCOptTable()
     75       : OptTable(llvm::makeArrayRef(RSCCInfoTable)) {}
     76 };
     77 }
     78 
     79 namespace slang {
     80 
     81 llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
     82 
     83 // This function is similar to
     84 // clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
     85 bool ParseArguments(const llvm::ArrayRef<const char *> &ArgsIn,
     86                     llvm::SmallVectorImpl<const char *> &Inputs,
     87                     RSCCOptions &Opts, clang::DiagnosticOptions &DiagOpts,
     88                     llvm::StringSaver &StringSaver) {
     89   // We use a different diagnostic engine for argument parsing from the rest of
     90   // the work.  This mimics what's done in clang.  I believe it is so the
     91   // argument parsing errors are well formatted while the full errors can be
     92   // influenced by command line arguments.
     93   llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> ArgumentParseDiagOpts(
     94       new clang::DiagnosticOptions());
     95   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
     96       new clang::DiagnosticIDs());
     97   DiagnosticBuffer DiagsBuffer;
     98   clang::DiagnosticsEngine DiagEngine(DiagIDs, &*ArgumentParseDiagOpts,
     99                                       &DiagsBuffer, false);
    100 
    101   // Populate a vector with the command line arguments, expanding command files
    102   // that have been included via the '@' argument.
    103   llvm::SmallVector<const char *, 256> ArgVector;
    104   // Skip over the command name, or we will mistakenly process it as a source file.
    105   ArgVector.append(ArgsIn.slice(1).begin(), ArgsIn.end());
    106   llvm::cl::ExpandResponseFiles(StringSaver, llvm::cl::TokenizeGNUCommandLine,
    107                                 ArgVector, false);
    108 
    109   std::unique_ptr<llvm::opt::OptTable> OptParser(createRSCCOptTable());
    110   unsigned MissingArgIndex = 0;
    111   unsigned MissingArgCount = 0;
    112   llvm::opt::InputArgList Args =
    113       OptParser->ParseArgs(ArgVector, MissingArgIndex, MissingArgCount);
    114 
    115   // Check for missing argument error.
    116   if (MissingArgCount) {
    117     DiagEngine.Report(clang::diag::err_drv_missing_argument)
    118         << Args.getArgString(MissingArgIndex) << MissingArgCount;
    119   }
    120 
    121   // Issue errors on unknown arguments.
    122   for (llvm::opt::arg_iterator it = Args.filtered_begin(OPT_UNKNOWN),
    123                                ie = Args.filtered_end();
    124        it != ie; ++it) {
    125     DiagEngine.Report(clang::diag::err_drv_unknown_argument)
    126         << (*it)->getAsString(Args);
    127   }
    128 
    129   DiagOpts.IgnoreWarnings = Args.hasArg(OPT_w);
    130   DiagOpts.Warnings = Args.getAllArgValues(OPT_W);
    131 
    132   // Always turn off warnings for empty initializers, since we really want to
    133   // employ/encourage this extension for zero-initialization of structures.
    134   DiagOpts.Warnings.push_back("no-gnu-empty-initializer");
    135 
    136   for (llvm::opt::ArgList::const_iterator it = Args.begin(), ie = Args.end();
    137        it != ie; ++it) {
    138     const llvm::opt::Arg *A = *it;
    139     if (A->getOption().getKind() == llvm::opt::Option::InputClass)
    140       Inputs.push_back(A->getValue());
    141   }
    142 
    143   Opts.mIncludePaths = Args.getAllArgValues(OPT_I);
    144 
    145   Opts.mBitcodeOutputDir = Args.getLastArgValue(OPT_o);
    146 
    147   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_M_Group)) {
    148     switch (A->getOption().getID()) {
    149     case OPT_M: {
    150       Opts.mEmitDependency = true;
    151       Opts.mOutputType = Slang::OT_Dependency;
    152       break;
    153     }
    154     case OPT_MD: {
    155       Opts.mEmitDependency = true;
    156       Opts.mOutputType = Slang::OT_Bitcode;
    157       break;
    158     }
    159     case OPT_MP: {
    160       Opts.mEmitDependency = true;
    161       Opts.mOutputType = Slang::OT_Bitcode;
    162       Opts.mEmitPhonyDependency = true;
    163       break;
    164     }
    165     default: { slangAssert(false && "Invalid option in M group!"); }
    166     }
    167   }
    168 
    169   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_Output_Type_Group)) {
    170     switch (A->getOption().getID()) {
    171     case OPT_emit_asm: {
    172       Opts.mOutputType = Slang::OT_Assembly;
    173       break;
    174     }
    175     case OPT_emit_llvm: {
    176       Opts.mOutputType = Slang::OT_LLVMAssembly;
    177       break;
    178     }
    179     case OPT_emit_bc: {
    180       Opts.mOutputType = Slang::OT_Bitcode;
    181       break;
    182     }
    183     case OPT_emit_nothing: {
    184       Opts.mOutputType = Slang::OT_Nothing;
    185       break;
    186     }
    187     default: { slangAssert(false && "Invalid option in output type group!"); }
    188     }
    189   }
    190 
    191   if (Opts.mEmitDependency && ((Opts.mOutputType != Slang::OT_Bitcode) &&
    192                                (Opts.mOutputType != Slang::OT_Dependency)))
    193     DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
    194         << Args.getLastArg(OPT_M_Group)->getAsString(Args)
    195         << Args.getLastArg(OPT_Output_Type_Group)->getAsString(Args);
    196 
    197   Opts.mAllowRSPrefix = Args.hasArg(OPT_allow_rs_prefix);
    198 
    199   Opts.mJavaReflectionPathBase =
    200       Args.getLastArgValue(OPT_java_reflection_path_base);
    201   Opts.mJavaReflectionPackageName =
    202       Args.getLastArgValue(OPT_java_reflection_package_name);
    203 
    204   Opts.mRSPackageName = Args.getLastArgValue(OPT_rs_package_name);
    205 
    206   llvm::StringRef BitcodeStorageValue =
    207       Args.getLastArgValue(OPT_bitcode_storage);
    208   if (BitcodeStorageValue == "ar")
    209     Opts.mBitcodeStorage = BCST_APK_RESOURCE;
    210   else if (BitcodeStorageValue == "jc")
    211     Opts.mBitcodeStorage = BCST_JAVA_CODE;
    212   else if (!BitcodeStorageValue.empty())
    213     DiagEngine.Report(clang::diag::err_drv_invalid_value)
    214         << OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue;
    215 
    216   llvm::opt::Arg *lastBitwidthArg = Args.getLastArg(OPT_m32, OPT_m64);
    217   if (Args.hasArg(OPT_reflect_cpp)) {
    218     Opts.mBitcodeStorage = BCST_CPP_CODE;
    219     // mJavaReflectionPathBase can be set for C++ reflected builds.
    220     // Set it to the standard mBitcodeOutputDir (via -o) by default.
    221     if (Opts.mJavaReflectionPathBase.empty()) {
    222       Opts.mJavaReflectionPathBase = Opts.mBitcodeOutputDir;
    223     }
    224 
    225     // Check for bitwidth arguments.
    226     if (lastBitwidthArg) {
    227       if (lastBitwidthArg->getOption().matches(OPT_m32)) {
    228         Opts.mBitWidth = 32;
    229       } else {
    230         Opts.mBitWidth = 64;
    231       }
    232     }
    233   } else if (lastBitwidthArg) {
    234       // -m32/-m64 are forbidden for non-C++ reflection paths for non-eng builds
    235       // (they would make it too easy for a developer to accidentally create and
    236       // release an APK that has 32-bit or 64-bit bitcode but not both).
    237 #ifdef __ENABLE_INTERNAL_OPTIONS
    238       if (lastBitwidthArg->getOption().matches(OPT_m32)) {
    239         Opts.mBitWidth = 32;
    240       } else {
    241         Opts.mBitWidth = 64;
    242       }
    243       Opts.mEmit3264 = false;
    244 #else
    245       DiagEngine.Report(
    246           DiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
    247                                      "cannot use -m32/-m64 without specifying "
    248                                      "C++ reflection (-reflect-c++)"));
    249 #endif
    250   }
    251 
    252   Opts.mDependencyOutputDir =
    253       Args.getLastArgValue(OPT_output_dep_dir, Opts.mBitcodeOutputDir);
    254   Opts.mAdditionalDepTargets = Args.getAllArgValues(OPT_additional_dep_target);
    255 
    256   Opts.mShowHelp = Args.hasArg(OPT_help);
    257   Opts.mShowVersion = Args.hasArg(OPT_version);
    258   Opts.mDebugEmission = Args.hasArg(OPT_emit_g);
    259   Opts.mVerbose = Args.hasArg(OPT_verbose);
    260   Opts.mASTPrint = Args.hasArg(OPT_ast_print);
    261 
    262   // Delegate options
    263 
    264   std::vector<std::string> DelegatedStrings;
    265   for (int Opt : std::vector<unsigned>{OPT_debug, OPT_print_after_all, OPT_print_before_all}) {
    266     if (Args.hasArg(Opt)) {
    267       // TODO: Don't assume that the option begins with "-"; determine this programmatically instead.
    268       DelegatedStrings.push_back(std::string("-") + std::string(OptParser->getOptionName(Opt)));
    269       slangAssert(OptParser->getOptionKind(Opt) == llvm::opt::Option::FlagClass);
    270     }
    271   }
    272   if (DelegatedStrings.size()) {
    273     std::vector<const char *> DelegatedCStrs;
    274     DelegatedCStrs.push_back(*ArgVector.data()); // program name
    275     std::for_each(DelegatedStrings.cbegin(), DelegatedStrings.cend(),
    276                   [&DelegatedCStrs](const std::string &String) { DelegatedCStrs.push_back(String.c_str()); });
    277     llvm::cl::ParseCommandLineOptions(DelegatedCStrs.size(), DelegatedCStrs.data());
    278   }
    279 
    280   // If we are emitting both 32-bit and 64-bit bitcode, we must embed it.
    281 
    282   size_t OptLevel =
    283       clang::getLastArgIntValue(Args, OPT_optimization_level, 3, DiagEngine);
    284 
    285   Opts.mOptimizationLevel =
    286       OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive;
    287 
    288   Opts.mTargetAPI =
    289       clang::getLastArgIntValue(Args, OPT_target_api, RS_VERSION, DiagEngine);
    290 
    291   if (Opts.mTargetAPI == 0) {
    292     Opts.mTargetAPI = UINT_MAX;
    293   } else if (Opts.mTargetAPI == SLANG_N_MR1_TARGET_API ||
    294              Opts.mTargetAPI == SLANG_O_TARGET_API ||
    295              Opts.mTargetAPI == SLANG_O_MR1_TARGET_API ||
    296              Opts.mTargetAPI == SLANG_P_TARGET_API) {
    297     // Bug: http://b/35767071
    298     // No new APIs for N_MR1, O, O_MR1 and P, convert to N.
    299     Opts.mTargetAPI = SLANG_N_TARGET_API;
    300   }
    301 
    302   if ((Opts.mTargetAPI < 21) || (Opts.mBitcodeStorage == BCST_CPP_CODE))
    303     Opts.mEmit3264 = false;
    304   if (Opts.mEmit3264)
    305     Opts.mBitcodeStorage = BCST_JAVA_CODE;
    306 
    307   if (DiagEngine.hasErrorOccurred()) {
    308     llvm::errs() << DiagsBuffer.str();
    309     return false;
    310   }
    311 
    312   return true;
    313 }
    314 }
    315