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     mFeatures.push_back("+long64");
    150     mBitcodeStorage = slang::BCST_APK_RESOURCE;
    151     mOutputDep = 0;
    152     mShowHelp = 0;
    153     mShowVersion = 0;
    154     mTargetAPI = RS_VERSION;
    155   }
    156 };
    157 
    158 // ParseArguments -
    159 static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector,
    160                            llvm::SmallVectorImpl<const char*> &Inputs,
    161                            RSCCOptions &Opts,
    162                            clang::DiagnosticsEngine &DiagEngine) {
    163   if (ArgVector.size() > 1) {
    164     const char **ArgBegin = ArgVector.data() + 1;
    165     const char **ArgEnd = ArgVector.data() + ArgVector.size();
    166     unsigned MissingArgIndex, MissingArgCount;
    167     llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable());
    168     llvm::OwningPtr<InputArgList> Args(
    169       OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount));
    170 
    171     // Check for missing argument error.
    172     if (MissingArgCount)
    173       DiagEngine.Report(clang::diag::err_drv_missing_argument)
    174         << Args->getArgString(MissingArgIndex) << MissingArgCount;
    175 
    176     // Issue errors on unknown arguments.
    177     for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
    178         ie = Args->filtered_end(); it != ie; ++it)
    179       DiagEngine.Report(clang::diag::err_drv_unknown_argument)
    180         << (*it)->getAsString(*Args);
    181 
    182     for (ArgList::const_iterator it = Args->begin(), ie = Args->end();
    183         it != ie; ++it) {
    184       const Arg *A = *it;
    185       if (A->getOption().getKind() == Option::InputClass)
    186         Inputs.push_back(A->getValue(*Args));
    187     }
    188 
    189     Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
    190 
    191     Opts.mOutputDir = Args->getLastArgValue(OPT_o);
    192 
    193     if (const Arg *A = Args->getLastArg(OPT_M_Group)) {
    194       switch (A->getOption().getID()) {
    195         case OPT_M: {
    196           Opts.mOutputDep = 1;
    197           Opts.mOutputType = slang::Slang::OT_Dependency;
    198           break;
    199         }
    200         case OPT_MD: {
    201           Opts.mOutputDep = 1;
    202           Opts.mOutputType = slang::Slang::OT_Bitcode;
    203           break;
    204         }
    205         default: {
    206           slangAssert(false && "Invalid option in M group!");
    207         }
    208       }
    209     }
    210 
    211     if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
    212       switch (A->getOption().getID()) {
    213         case OPT_emit_asm: {
    214           Opts.mOutputType = slang::Slang::OT_Assembly;
    215           break;
    216         }
    217         case OPT_emit_llvm: {
    218           Opts.mOutputType = slang::Slang::OT_LLVMAssembly;
    219           break;
    220         }
    221         case OPT_emit_bc: {
    222           Opts.mOutputType = slang::Slang::OT_Bitcode;
    223           break;
    224         }
    225         case OPT_emit_nothing: {
    226           Opts.mOutputType = slang::Slang::OT_Nothing;
    227           break;
    228         }
    229         default: {
    230           slangAssert(false && "Invalid option in output type group!");
    231         }
    232       }
    233     }
    234 
    235     if (Opts.mOutputDep &&
    236         ((Opts.mOutputType != slang::Slang::OT_Bitcode) &&
    237          (Opts.mOutputType != slang::Slang::OT_Dependency)))
    238       DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
    239           << Args->getLastArg(OPT_M_Group)->getAsString(*Args)
    240           << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
    241 
    242     Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
    243 
    244     Opts.mJavaReflectionPathBase =
    245         Args->getLastArgValue(OPT_java_reflection_path_base);
    246     Opts.mJavaReflectionPackageName =
    247         Args->getLastArgValue(OPT_java_reflection_package_name);
    248 
    249     llvm::StringRef BitcodeStorageValue =
    250         Args->getLastArgValue(OPT_bitcode_storage);
    251     if (BitcodeStorageValue == "ar")
    252       Opts.mBitcodeStorage = slang::BCST_APK_RESOURCE;
    253     else if (BitcodeStorageValue == "jc")
    254       Opts.mBitcodeStorage = slang::BCST_JAVA_CODE;
    255     else if (!BitcodeStorageValue.empty())
    256       DiagEngine.Report(clang::diag::err_drv_invalid_value)
    257           << OptParser->getOptionName(OPT_bitcode_storage)
    258           << BitcodeStorageValue;
    259 
    260     Opts.mOutputDepDir =
    261         Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir);
    262     Opts.mAdditionalDepTargets =
    263         Args->getAllArgValues(OPT_additional_dep_target);
    264 
    265     Opts.mShowHelp = Args->hasArg(OPT_help);
    266     Opts.mShowVersion = Args->hasArg(OPT_version);
    267 
    268     Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api,
    269                                                RS_VERSION,
    270                                                DiagEngine);
    271   }
    272 
    273   return;
    274 }
    275 
    276 static const char *DetermineOutputFile(const std::string &OutputDir,
    277                                        const char *InputFile,
    278                                        slang::Slang::OutputType OutputType,
    279                                        std::set<std::string> &SavedStrings) {
    280   if (OutputType == slang::Slang::OT_Nothing)
    281     return "/dev/null";
    282 
    283   std::string OutputFile(OutputDir);
    284 
    285   // Append '/' to Opts.mOutputDir if not presents
    286   if (!OutputFile.empty() &&
    287       (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
    288     OutputFile.append(1, OS_PATH_SEPARATOR);
    289 
    290   if (OutputType == slang::Slang::OT_Dependency) {
    291     // The build system wants the .d file name stem to be exactly the same as
    292     // the source .rs file, instead of the .bc file.
    293     OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
    294   } else {
    295     OutputFile.append(
    296         slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
    297   }
    298 
    299   switch (OutputType) {
    300     case slang::Slang::OT_Dependency: {
    301       OutputFile.append(".d");
    302       break;
    303     }
    304     case slang::Slang::OT_Assembly: {
    305       OutputFile.append(".S");
    306       break;
    307     }
    308     case slang::Slang::OT_LLVMAssembly: {
    309       OutputFile.append(".ll");
    310       break;
    311     }
    312     case slang::Slang::OT_Object: {
    313       OutputFile.append(".o");
    314       break;
    315     }
    316     case slang::Slang::OT_Bitcode: {
    317       OutputFile.append(".bc");
    318       break;
    319     }
    320     case slang::Slang::OT_Nothing:
    321     default: {
    322       slangAssert(false && "Invalid output type!");
    323     }
    324   }
    325 
    326   return SaveStringInSet(SavedStrings, OutputFile);
    327 }
    328 
    329 #define str(s) #s
    330 #define wrap_str(s) str(s)
    331 static void llvm_rs_cc_VersionPrinter() {
    332   llvm::raw_ostream &OS = llvm::outs();
    333   OS << "llvm-rs-cc: Renderscript compiler\n"
    334      << "  (http://developer.android.com/guide/topics/renderscript)\n"
    335      << "  based on LLVM (http://llvm.org):\n";
    336   OS << "  Built " << __DATE__ << " (" << __TIME__ ").\n";
    337   OS << "  Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
    338      << SLANG_MAXIMUM_TARGET_API;
    339   OS << "\n  Build type: " << wrap_str(TARGET_BUILD_VARIANT);
    340 #ifndef __DISABLE_ASSERTS
    341   OS << " with assertions";
    342 #endif
    343   OS << ".\n";
    344   return;
    345 }
    346 
    347 int main(int argc, const char **argv) {
    348   std::set<std::string> SavedStrings;
    349   llvm::SmallVector<const char*, 256> ArgVector;
    350   RSCCOptions Opts;
    351   llvm::SmallVector<const char*, 16> Inputs;
    352   std::string Argv0;
    353 
    354   atexit(llvm::llvm_shutdown);
    355 
    356   ExpandArgv(argc, argv, ArgVector, SavedStrings);
    357 
    358   // Argv0
    359   Argv0 = llvm::sys::path::stem(ArgVector[0]);
    360 
    361   // Setup diagnostic engine
    362   clang::TextDiagnosticPrinter *DiagClient =
    363     new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions());
    364   DiagClient->setPrefix(Argv0);
    365 
    366   llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
    367     new clang::DiagnosticIDs());
    368 
    369   clang::DiagnosticsEngine DiagEngine(DiagIDs, DiagClient, true);
    370 
    371   clang::Diagnostic Diags(&DiagEngine);
    372 
    373   slang::Slang::GlobalInitialization();
    374 
    375   ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
    376 
    377   // Exits when there's any error occurred during parsing the arguments
    378   if (DiagEngine.hasErrorOccurred())
    379     return 1;
    380 
    381   if (Opts.mShowHelp) {
    382     llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable());
    383     OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
    384                       "Renderscript source compiler");
    385     return 0;
    386   }
    387 
    388   if (Opts.mShowVersion) {
    389     llvm_rs_cc_VersionPrinter();
    390     return 0;
    391   }
    392 
    393   // No input file
    394   if (Inputs.empty()) {
    395     DiagEngine.Report(clang::diag::err_drv_no_input_files);
    396     return 1;
    397   }
    398 
    399   // Prepare input data for RS compiler.
    400   std::list<std::pair<const char*, const char*> > IOFiles;
    401   std::list<std::pair<const char*, const char*> > DepFiles;
    402 
    403   llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS());
    404 
    405   Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures);
    406 
    407   for (int i = 0, e = Inputs.size(); i != e; i++) {
    408     const char *InputFile = Inputs[i];
    409     const char *OutputFile =
    410         DetermineOutputFile(Opts.mOutputDir, InputFile,
    411                             Opts.mOutputType, SavedStrings);
    412 
    413     if (Opts.mOutputDep) {
    414       const char *BCOutputFile, *DepOutputFile;
    415 
    416       if (Opts.mOutputType == slang::Slang::OT_Bitcode)
    417         BCOutputFile = OutputFile;
    418       else
    419         BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    420                                            InputFile,
    421                                            slang::Slang::OT_Bitcode,
    422                                            SavedStrings);
    423 
    424       if (Opts.mOutputType == slang::Slang::OT_Dependency)
    425         DepOutputFile = OutputFile;
    426       else
    427         DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
    428                                             InputFile,
    429                                             slang::Slang::OT_Dependency,
    430                                             SavedStrings);
    431 
    432       DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
    433     }
    434 
    435     IOFiles.push_back(std::make_pair(InputFile, OutputFile));
    436   }
    437 
    438   // Let's rock!
    439   int CompileFailed = !Compiler->compile(IOFiles,
    440                                          DepFiles,
    441                                          Opts.mIncludePaths,
    442                                          Opts.mAdditionalDepTargets,
    443                                          Opts.mOutputType,
    444                                          Opts.mBitcodeStorage,
    445                                          Opts.mAllowRSPrefix,
    446                                          Opts.mOutputDep,
    447                                          Opts.mTargetAPI,
    448                                          Opts.mJavaReflectionPathBase,
    449                                          Opts.mJavaReflectionPackageName);
    450   Compiler->reset();
    451 
    452   return CompileFailed;
    453 }
    454 
    455 ///////////////////////////////////////////////////////////////////////////////
    456 
    457 // ExpandArgsFromBuf -
    458 static void ExpandArgsFromBuf(const char *Arg,
    459                               llvm::SmallVectorImpl<const char*> &ArgVector,
    460                               std::set<std::string> &SavedStrings) {
    461   const char *FName = Arg + 1;
    462   llvm::OwningPtr<llvm::MemoryBuffer> MemBuf;
    463   if (llvm::MemoryBuffer::getFile(FName, MemBuf)) {
    464     // Unable to open the file
    465     ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
    466     return;
    467   }
    468 
    469   const char *Buf = MemBuf->getBufferStart();
    470   char InQuote = ' ';
    471   std::string CurArg;
    472 
    473   for (const char *P = Buf; ; ++P) {
    474     if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
    475       if (!CurArg.empty()) {
    476         if (CurArg[0] != '@') {
    477           ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
    478         } else {
    479           ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
    480         }
    481 
    482         CurArg = "";
    483       }
    484       if (*P == '\0')
    485         break;
    486       else
    487         continue;
    488     }
    489 
    490     if (isspace(*P)) {
    491       if (InQuote != ' ')
    492         CurArg.push_back(*P);
    493       continue;
    494     }
    495 
    496     if (*P == '"' || *P == '\'') {
    497       if (InQuote == *P)
    498         InQuote = ' ';
    499       else if (InQuote == ' ')
    500         InQuote = *P;
    501       else
    502         CurArg.push_back(*P);
    503       continue;
    504     }
    505 
    506     if (*P == '\\') {
    507       ++P;
    508       if (*P != '\0')
    509         CurArg.push_back(*P);
    510       continue;
    511     }
    512     CurArg.push_back(*P);
    513   }
    514 }
    515 
    516 // ExpandArgsFromBuf -
    517 static void ExpandArgv(int argc, const char **argv,
    518                        llvm::SmallVectorImpl<const char*> &ArgVector,
    519                        std::set<std::string> &SavedStrings) {
    520   for (int i = 0; i < argc; ++i) {
    521     const char *Arg = argv[i];
    522     if (Arg[0] != '@') {
    523       ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
    524       continue;
    525     }
    526 
    527     ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
    528   }
    529 }
    530