Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2010, 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 <cstdlib>
     18 #include <list>
     19 #include <set>
     20 #include <string>
     21 #include <utility>
     22 #include <vector>
     23 
     24 #include "clang/Driver/Arg.h"
     25 #include "clang/Driver/ArgList.h"
     26 #include "clang/Driver/DriverDiagnostic.h"
     27 #include "clang/Driver/Option.h"
     28 #include "clang/Driver/OptTable.h"
     29 
     30 #include "clang/Frontend/DiagnosticOptions.h"
     31 #include "clang/Frontend/TextDiagnosticPrinter.h"
     32 
     33 #include "llvm/ADT/SmallVector.h"
     34 #include "llvm/ADT/IntrusiveRefCntPtr.h"
     35 #include "llvm/ADT/OwningPtr.h"
     36 
     37 #include "llvm/Support/CommandLine.h"
     38 #include "llvm/Support/ManagedStatic.h"
     39 #include "llvm/Support/MemoryBuffer.h"
     40 #include "llvm/Support/Path.h"
     41 #include "llvm/Support/raw_ostream.h"
     42 #include "llvm/Support/system_error.h"
     43 
     44 #include "slang.h"
     45 #include "slang_assert.h"
     46 #include "slang_rs.h"
     47 #include "slang_rs_reflect_utils.h"
     48 
     49 // Class under clang::driver used are enumerated here.
     50 using clang::driver::arg_iterator;
     51 using clang::driver::options::DriverOption;
     52 using clang::driver::Arg;
     53 using clang::driver::ArgList;
     54 using clang::driver::InputArgList;
     55 using clang::driver::Option;
     56 using clang::driver::OptTable;
     57 
     58 // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
     59 // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
     60 // main().
     61 static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
     62                                           llvm::StringRef S) {
     63   return SavedStrings.insert(S).first->c_str();
     64 }
     65 static void ExpandArgsFromBuf(const char *Arg,
     66                               llvm::SmallVectorImpl<const char*> &ArgVector,
     67                               std::set<std::string> &SavedStrings);
     68 static void ExpandArgv(int argc, const char **argv,
     69                        llvm::SmallVectorImpl<const char*> &ArgVector,
     70                        std::set<std::string> &SavedStrings);
     71 
     72 enum {
     73   OPT_INVALID = 0,  // This is not an option ID.
     74 #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
     75                HELPTEXT, METAVAR) OPT_##ID,
     76 #include "RSCCOptions.inc"
     77   LastOption
     78 #undef OPTION
     79 };
     80 
     81 static const OptTable::Info RSCCInfoTable[] = {
     82 #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
     83                HELPTEXT, METAVAR)   \
     84   { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \
     85     OPT_##GROUP, OPT_##ALIAS },
     86 #include "RSCCOptions.inc"
     87 };
     88 
     89 class RSCCOptTable : public OptTable {
     90  public:
     91   RSCCOptTable()
     92       : OptTable(RSCCInfoTable,
     93                  sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
     94   }
     95 };
     96 
     97 OptTable *createRSCCOptTable() {
     98   return new RSCCOptTable();
     99 }
    100 
    101 ///////////////////////////////////////////////////////////////////////////////
    102 
    103 class RSCCOptions {
    104  public:
    105   // The include search paths
    106   std::vector<std::string> mIncludePaths;
    107 
    108   // The output directory, if any.
    109   std::string mOutputDir;
    110 
    111   // The output type
    112   slang::Slang::OutputType mOutputType;
    113 
    114   unsigned mAllowRSPrefix : 1;
    115 
    116   // The name of the target triple to compile for.
    117   std::string mTriple;
    118 
    119   // The name of the target CPU to generate code for.
    120   std::string mCPU;
    121 
    122   // The list of target specific features to enable or disable -- this should
    123   // be a list of strings starting with by '+' or '-'.
    124   std::vector<std::string> mFeatures;
    125 
    126   std::string mJavaReflectionPathBase;
    127 
    128   std::string mJavaReflectionPackageName;
    129 
    130   slang::BitCodeStorageType mBitcodeStorage;
    131 
    132   unsigned mOutputDep : 1;
    133 
    134   std::string mOutputDepDir;
    135 
    136   std::vector<std::string> mAdditionalDepTargets;
    137 
    138   unsigned mShowHelp : 1;  // Show the -help text.
    139   unsigned mShowVersion : 1;  // Show the -version text.
    140 
    141   unsigned int mTargetAPI;
    142 
    143   RSCCOptions() {
    144     mOutputType = slang::Slang::OT_Bitcode;
    145     // Triple/CPU/Features must be hard-coded to our chosen portable ABI.
    146     mTriple = "armv7-none-linux-gnueabi";
    147     mCPU = "";
    148     slangAssert(mFeatures.empty());
    149     mBitcodeStorage = slang::BCST_APK_RESOURCE;
    150     mOutputDep = 0;
    151     mShowHelp = 0;
    152     mShowVersion = 0;
    153     mTargetAPI = RS_VERSION;
    154   }
    155 };
    156 
    157 // ParseArguments -
    158 static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector,
    159                            llvm::SmallVectorImpl<const char*> &Inputs,
    160                            RSCCOptions &Opts,
    161                            clang::Diagnostic &Diags) {
    162   if (ArgVector.size() > 1) {
    163     const char **ArgBegin = ArgVector.data() + 1;
    164     const char **ArgEnd = ArgVector.data() + ArgVector.size();
    165     unsigned MissingArgIndex, MissingArgCount;
    166     llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable());
    167     llvm::OwningPtr<InputArgList> Args(
    168       OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount));
    169 
    170     // Check for missing argument error.
    171     if (MissingArgCount)
    172       Diags.Report(clang::diag::err_drv_missing_argument)
    173         << Args->getArgString(MissingArgIndex) << MissingArgCount;
    174 
    175     // Issue errors on unknown arguments.
    176     for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
    177         ie = Args->filtered_end(); it != ie; ++it)
    178       Diags.Report(clang::diag::err_drv_unknown_argument)
    179         << (*it)->getAsString(*Args);
    180 
    181     for (ArgList::const_iterator it = Args->begin(), ie = Args->end();
    182         it != ie; ++it) {
    183       const Arg *A = *it;
    184       if (A->getOption().getKind() == Option::InputClass)
    185         Inputs.push_back(A->getValue(*Args));
    186     }
    187 
    188     Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
    189 
    190     Opts.mOutputDir = Args->getLastArgValue(OPT_o);
    191 
    192     if (const Arg *A = Args->getLastArg(OPT_M_Group)) {
    193       switch (A->getOption().getID()) {
    194         case OPT_M: {
    195           Opts.mOutputDep = 1;
    196           Opts.mOutputType = slang::Slang::OT_Dependency;
    197           break;
    198         }
    199         case OPT_MD: {
    200           Opts.mOutputDep = 1;
    201           Opts.mOutputType = slang::Slang::OT_Bitcode;
    202           break;
    203         }
    204         default: {
    205           slangAssert(false && "Invalid option in M group!");
    206         }
    207       }
    208     }
    209 
    210     if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
    211       switch (A->getOption().getID()) {
    212         case OPT_emit_asm: {
    213           Opts.mOutputType = slang::Slang::OT_Assembly;
    214           break;
    215         }
    216         case OPT_emit_llvm: {
    217           Opts.mOutputType = slang::Slang::OT_LLVMAssembly;
    218           break;
    219         }
    220         case OPT_emit_bc: {
    221           Opts.mOutputType = slang::Slang::OT_Bitcode;
    222           break;
    223         }
    224         case OPT_emit_nothing: {
    225           Opts.mOutputType = slang::Slang::OT_Nothing;
    226           break;
    227         }
    228         default: {
    229           slangAssert(false && "Invalid option in output type group!");
    230         }
    231       }
    232     }
    233 
    234     if (Opts.mOutputDep &&
    235         ((Opts.mOutputType != slang::Slang::OT_Bitcode) &&
    236          (Opts.mOutputType != slang::Slang::OT_Dependency)))
    237       Diags.Report(clang::diag::err_drv_argument_not_allowed_with)
    238           << Args->getLastArg(OPT_M_Group)->getAsString(*Args)
    239           << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
    240 
    241     Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
    242 
    243     Opts.mJavaReflectionPathBase =
    244         Args->getLastArgValue(OPT_java_reflection_path_base);
    245     Opts.mJavaReflectionPackageName =
    246         Args->getLastArgValue(OPT_java_reflection_package_name);
    247 
    248     llvm::StringRef BitcodeStorageValue =
    249         Args->getLastArgValue(OPT_bitcode_storage);
    250     if (BitcodeStorageValue == "ar")
    251       Opts.mBitcodeStorage = slang::BCST_APK_RESOURCE;
    252     else if (BitcodeStorageValue == "jc")
    253       Opts.mBitcodeStorage = slang::BCST_JAVA_CODE;
    254     else if (!BitcodeStorageValue.empty())
    255       Diags.Report(clang::diag::err_drv_invalid_value)
    256           << OptParser->getOptionName(OPT_bitcode_storage)
    257           << BitcodeStorageValue;
    258 
    259     Opts.mOutputDepDir =
    260         Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir);
    261     Opts.mAdditionalDepTargets =
    262         Args->getAllArgValues(OPT_additional_dep_target);
    263 
    264     Opts.mShowHelp = Args->hasArg(OPT_help);
    265     Opts.mShowVersion = Args->hasArg(OPT_version);
    266 
    267     Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api,
    268                                                RS_VERSION,
    269                                                Diags);
    270   }
    271 
    272   return;
    273 }
    274 
    275 static const char *DetermineOutputFile(const std::string &OutputDir,
    276                                        const char *InputFile,
    277                                        slang::Slang::OutputType OutputType,
    278                                        std::set<std::string> &SavedStrings) {
    279   if (OutputType == slang::Slang::OT_Nothing)
    280     return "/dev/null";
    281 
    282   std::string OutputFile(OutputDir);
    283 
    284   // Append '/' to Opts.mOutputDir if not presents
    285   if (!OutputFile.empty() &&
    286       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
    287     OutputFile.append(1, OS_PATH_SEPARATOR);
    288 
    289   if (OutputType == slang::Slang::OT_Dependency) {
    290     // The build system wants the .d file name stem to be exactly the same as
    291     // the source .rs file, instead of the .bc file.
    292     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
    293   } else {
    294     OutputFile.append(
    295         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
    296   }
    297 
    298   switch (OutputType) {
    299     case slang::Slang::OT_Dependency: {
    300       OutputFile.append(".d");
    301       break;
    302     }
    303     case slang::Slang::OT_Assembly: {
    304       OutputFile.append(".S");
    305       break;
    306     }
    307     case slang::Slang::OT_LLVMAssembly: {
    308       OutputFile.append(".ll");
    309       break;
    310     }
    311     case slang::Slang::OT_Object: {
    312       OutputFile.append(".o");
    313       break;
    314     }
    315     case slang::Slang::OT_Bitcode: {
    316       OutputFile.append(".bc");
    317       break;
    318     }
    319     case slang::Slang::OT_Nothing:
    320     default: {
    321       slangAssert(false && "Invalid output type!");
    322     }
    323   }
    324 
    325   return SaveStringInSet(SavedStrings, OutputFile);
    326 }
    327 
    328 #define str(s) #s
    329 #define wrap_str(s) str(s)
    330 static void llvm_rs_cc_VersionPrinter() {
    331   llvm::raw_ostream &OS = llvm::outs();
    332   OS << "llvm-rs-cc: Renderscript compiler\n"
    333      << "  (http://developer.android.com/guide/topics/renderscript)\n"
    334      << "  based on LLVM (http://llvm.org):\n";
    335   OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
    336   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
    337      << SLANG_MAXIMUM_TARGET_API;
    338   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
    339 #ifndef __DISABLE_ASSERTS
    340   OS << " with assertions";
    341 #endif
    342   OS << ".\n";
    343   return;
    344 }
    345 
    346 int main(int argc, const char **argv) {
    347   std::set<std::string> SavedStrings;
    348   llvm::SmallVector<const char*, 256> ArgVector;
    349   RSCCOptions Opts;
    350   llvm::SmallVector<const char*, 16> Inputs;
    351   std::string Argv0;
    352 
    353   atexit(llvm::llvm_shutdown);
    354 
    355   ExpandArgv(argc, argv, ArgVector, SavedStrings);
    356 
    357   // Argv0
    358   Argv0 = llvm::sys::path::stem(ArgVector[0]);
    359 
    360   // Setup diagnostic engine
    361   clang::TextDiagnosticPrinter *DiagClient =
    362     new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions());
    363   DiagClient->setPrefix(Argv0);
    364 
    365   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
    366     new clang::DiagnosticIDs());
    367 
    368   clang::Diagnostic Diags(DiagIDs, DiagClient, true);
    369 
    370   slang::Slang::GlobalInitialization();
    371 
    372   ParseArguments(ArgVector, Inputs, Opts, Diags);
    373 
    374   // Exits when there's any error occurred during parsing the arguments
    375   if (Diags.hasErrorOccurred())
    376     return 1;
    377 
    378   if (Opts.mShowHelp) {
    379     llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable());
    380     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
    381                       "Renderscript source compiler");
    382     return 0;
    383   }
    384 
    385   if (Opts.mShowVersion) {
    386     llvm_rs_cc_VersionPrinter();
    387     return 0;
    388   }
    389 
    390   // No input file
    391   if (Inputs.empty()) {
    392     Diags.Report(clang::diag::err_drv_no_input_files);
    393     return 1;
    394   }
    395 
    396   // Prepare input data for RS compiler.
    397   std::list<std::pair<const char*, const char*> > IOFiles;
    398   std::list<std::pair<const char*, const char*> > DepFiles;
    399 
    400   llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS());
    401 
    402   Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures);
    403 
    404   for (int i = 0, e = Inputs.size(); i != e; i++) {
    405     const char *InputFile = Inputs[i];
    406     const char *OutputFile =
    407         DetermineOutputFile(Opts.mOutputDir, InputFile,
    408                             Opts.mOutputType, SavedStrings);
    409 
    410     if (Opts.mOutputDep) {
    411       const char *BCOutputFile, *DepOutputFile;
    412 
    413       if (Opts.mOutputType == slang::Slang::OT_Bitcode)
    414         BCOutputFile = OutputFile;
    415       else
    416         BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    417                                            InputFile,
    418                                            slang::Slang::OT_Bitcode,
    419                                            SavedStrings);
    420 
    421       if (Opts.mOutputType == slang::Slang::OT_Dependency)
    422         DepOutputFile = OutputFile;
    423       else
    424         DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    425                                             InputFile,
    426                                             slang::Slang::OT_Dependency,
    427                                             SavedStrings);
    428 
    429       DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
    430     }
    431 
    432     IOFiles.push_back(std::make_pair(InputFile, OutputFile));
    433   }
    434 
    435   // Let's rock!
    436   int CompileFailed = !Compiler->compile(IOFiles,
    437                                          DepFiles,
    438                                          Opts.mIncludePaths,
    439                                          Opts.mAdditionalDepTargets,
    440                                          Opts.mOutputType,
    441                                          Opts.mBitcodeStorage,
    442                                          Opts.mAllowRSPrefix,
    443                                          Opts.mOutputDep,
    444                                          Opts.mTargetAPI,
    445                                          Opts.mJavaReflectionPathBase,
    446                                          Opts.mJavaReflectionPackageName);
    447   Compiler->reset();
    448 
    449   return CompileFailed;
    450 }
    451 
    452 ///////////////////////////////////////////////////////////////////////////////
    453 
    454 // ExpandArgsFromBuf -
    455 static void ExpandArgsFromBuf(const char *Arg,
    456                               llvm::SmallVectorImpl<const char*> &ArgVector,
    457                               std::set<std::string> &SavedStrings) {
    458   const char *FName = Arg + 1;
    459   llvm::OwningPtr<llvm::MemoryBuffer> MemBuf;
    460   if (llvm::MemoryBuffer::getFile(FName, MemBuf)) {
    461     // Unable to open the file
    462     ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
    463     return;
    464   }
    465 
    466   const char *Buf = MemBuf->getBufferStart();
    467   char InQuote = ' ';
    468   std::string CurArg;
    469 
    470   for (const char *P = Buf; ; ++P) {
    471     if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
    472       if (!CurArg.empty()) {
    473         if (CurArg[0] != '@') {
    474           ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
    475         } else {
    476           ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
    477         }
    478 
    479         CurArg = "";
    480       }
    481       if (*P == '\0')
    482         break;
    483       else
    484         continue;
    485     }
    486 
    487     if (isspace(*P)) {
    488       if (InQuote != ' ')
    489         CurArg.push_back(*P);
    490       continue;
    491     }
    492 
    493     if (*P == '"' || *P == '\'') {
    494       if (InQuote == *P)
    495         InQuote = ' ';
    496       else if (InQuote == ' ')
    497         InQuote = *P;
    498       else
    499         CurArg.push_back(*P);
    500       continue;
    501     }
    502 
    503     if (*P == '\\') {
    504       ++P;
    505       if (*P != '\0')
    506         CurArg.push_back(*P);
    507       continue;
    508     }
    509     CurArg.push_back(*P);
    510   }
    511 }
    512 
    513 // ExpandArgsFromBuf -
    514 static void ExpandArgv(int argc, const char **argv,
    515                        llvm::SmallVectorImpl<const char*> &ArgVector,
    516                        std::set<std::string> &SavedStrings) {
    517   for (int i = 0; i < argc; ++i) {
    518     const char *Arg = argv[i];
    519     if (Arg[0] != '@') {
    520       ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
    521       continue;
    522     }
    523 
    524     ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
    525   }
    526 }
    527