/* * Copyright 2010-2012, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <cstdlib> #include <list> #include <set> #include <string> #include <utility> #include <vector> #include "clang/Driver/Arg.h" #include "clang/Driver/ArgList.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Option.h" #include "clang/Driver/OptTable.h" #include "clang/Frontend/DiagnosticOptions.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/system_error.h" #include "llvm/Target/TargetMachine.h" #include "slang.h" #include "slang_assert.h" #include "slang_diagnostic_buffer.h" #include "slang_rs.h" #include "slang_rs_reflect_utils.h" // Class under clang::driver used are enumerated here. using clang::driver::arg_iterator; using clang::driver::options::DriverOption; using clang::driver::Arg; using clang::driver::ArgList; using clang::driver::InputArgList; using clang::driver::Option; using clang::driver::OptTable; using namespace clang::driver::options; // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in // main(). static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings, llvm::StringRef S) { return SavedStrings.insert(S).first->c_str(); } static void ExpandArgsFromBuf(const char *Arg, llvm::SmallVectorImpl<const char*> &ArgVector, std::set<std::string> &SavedStrings); static void ExpandArgv(int argc, const char **argv, llvm::SmallVectorImpl<const char*> &ArgVector, std::set<std::string> &SavedStrings); enum { OPT_INVALID = 0, // This is not an option ID. #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ HELPTEXT, METAVAR) OPT_##ID, #include "RSCCOptions.inc" LastOption #undef OPTION }; static const OptTable::Info RSCCInfoTable[] = { #define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ HELPTEXT, METAVAR) \ { NAME, HELPTEXT, METAVAR, Option::KIND##Class, PARAM, FLAGS, \ OPT_##GROUP, OPT_##ALIAS }, #include "RSCCOptions.inc" }; class RSCCOptTable : public OptTable { public: RSCCOptTable() : OptTable(RSCCInfoTable, sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) { } }; OptTable *createRSCCOptTable() { return new RSCCOptTable(); } /////////////////////////////////////////////////////////////////////////////// class RSCCOptions { public: // The include search paths std::vector<std::string> mIncludePaths; // The output directory, if any. std::string mOutputDir; // The output type slang::Slang::OutputType mOutputType; unsigned mAllowRSPrefix : 1; // The name of the target triple to compile for. std::string mTriple; // The name of the target CPU to generate code for. std::string mCPU; // The list of target specific features to enable or disable -- this should // be a list of strings starting with by '+' or '-'. std::vector<std::string> mFeatures; std::string mJavaReflectionPathBase; std::string mJavaReflectionPackageName; std::string mRSPackageName; slang::BitCodeStorageType mBitcodeStorage; unsigned mOutputDep : 1; std::string mOutputDepDir; std::vector<std::string> mAdditionalDepTargets; unsigned mShowHelp : 1; // Show the -help text. unsigned mShowVersion : 1; // Show the -version text. unsigned int mTargetAPI; // Enable emission of debugging symbols unsigned mDebugEmission : 1; // The optimization level used in CodeGen, and encoded in emitted bitcode llvm::CodeGenOpt::Level mOptimizationLevel; RSCCOptions() { mOutputType = slang::Slang::OT_Bitcode; // Triple/CPU/Features must be hard-coded to our chosen portable ABI. mTriple = "armv7-none-linux-gnueabi"; mCPU = ""; slangAssert(mFeatures.empty()); mFeatures.push_back("+long64"); mBitcodeStorage = slang::BCST_APK_RESOURCE; mOutputDep = 0; mShowHelp = 0; mShowVersion = 0; mTargetAPI = RS_VERSION; mDebugEmission = 0; mOptimizationLevel = llvm::CodeGenOpt::Aggressive; } }; // ParseArguments - static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector, llvm::SmallVectorImpl<const char*> &Inputs, RSCCOptions &Opts, clang::DiagnosticsEngine &DiagEngine) { if (ArgVector.size() > 1) { const char **ArgBegin = ArgVector.data() + 1; const char **ArgEnd = ArgVector.data() + ArgVector.size(); unsigned MissingArgIndex, MissingArgCount; llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable()); llvm::OwningPtr<InputArgList> Args( OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount)); // Check for missing argument error. if (MissingArgCount) DiagEngine.Report(clang::diag::err_drv_missing_argument) << Args->getArgString(MissingArgIndex) << MissingArgCount; clang::DiagnosticOptions DiagOpts; DiagOpts.IgnoreWarnings = Args->hasArg(OPT_w); DiagOpts.Warnings = Args->getAllArgValues(OPT_W); clang::ProcessWarningOptions(DiagEngine, DiagOpts); // Issue errors on unknown arguments. for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), ie = Args->filtered_end(); it != ie; ++it) DiagEngine.Report(clang::diag::err_drv_unknown_argument) << (*it)->getAsString(*Args); for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); it != ie; ++it) { const Arg *A = *it; if (A->getOption().getKind() == Option::InputClass) Inputs.push_back(A->getValue(*Args)); } Opts.mIncludePaths = Args->getAllArgValues(OPT_I); Opts.mOutputDir = Args->getLastArgValue(OPT_o); if (const Arg *A = Args->getLastArg(OPT_M_Group)) { switch (A->getOption().getID()) { case OPT_M: { Opts.mOutputDep = 1; Opts.mOutputType = slang::Slang::OT_Dependency; break; } case OPT_MD: { Opts.mOutputDep = 1; Opts.mOutputType = slang::Slang::OT_Bitcode; break; } default: { slangAssert(false && "Invalid option in M group!"); } } } if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) { switch (A->getOption().getID()) { case OPT_emit_asm: { Opts.mOutputType = slang::Slang::OT_Assembly; break; } case OPT_emit_llvm: { Opts.mOutputType = slang::Slang::OT_LLVMAssembly; break; } case OPT_emit_bc: { Opts.mOutputType = slang::Slang::OT_Bitcode; break; } case OPT_emit_nothing: { Opts.mOutputType = slang::Slang::OT_Nothing; break; } default: { slangAssert(false && "Invalid option in output type group!"); } } } if (Opts.mOutputDep && ((Opts.mOutputType != slang::Slang::OT_Bitcode) && (Opts.mOutputType != slang::Slang::OT_Dependency))) DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with) << Args->getLastArg(OPT_M_Group)->getAsString(*Args) << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args); Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix); Opts.mJavaReflectionPathBase = Args->getLastArgValue(OPT_java_reflection_path_base); Opts.mJavaReflectionPackageName = Args->getLastArgValue(OPT_java_reflection_package_name); Opts.mRSPackageName = Args->getLastArgValue(OPT_rs_package_name); llvm::StringRef BitcodeStorageValue = Args->getLastArgValue(OPT_bitcode_storage); if (BitcodeStorageValue == "ar") Opts.mBitcodeStorage = slang::BCST_APK_RESOURCE; else if (BitcodeStorageValue == "jc") Opts.mBitcodeStorage = slang::BCST_JAVA_CODE; else if (!BitcodeStorageValue.empty()) DiagEngine.Report(clang::diag::err_drv_invalid_value) << OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue; if (Args->hasArg(OPT_reflect_cpp)) { Opts.mBitcodeStorage = slang::BCST_CPP_CODE; } Opts.mOutputDepDir = Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir); Opts.mAdditionalDepTargets = Args->getAllArgValues(OPT_additional_dep_target); Opts.mShowHelp = Args->hasArg(OPT_help); Opts.mShowVersion = Args->hasArg(OPT_version); Opts.mDebugEmission = Args->hasArg(OPT_emit_g); size_t OptLevel = Args->getLastArgIntValue(OPT_optimization_level, 3, DiagEngine); Opts.mOptimizationLevel = OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive; Opts.mTargetAPI = Args->getLastArgIntValue(OPT_target_api, RS_VERSION, DiagEngine); } return; } static const char *DetermineOutputFile(const std::string &OutputDir, const char *InputFile, slang::Slang::OutputType OutputType, std::set<std::string> &SavedStrings) { if (OutputType == slang::Slang::OT_Nothing) return "/dev/null"; std::string OutputFile(OutputDir); // Append '/' to Opts.mOutputDir if not presents if (!OutputFile.empty() && (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR) OutputFile.append(1, OS_PATH_SEPARATOR); if (OutputType == slang::Slang::OT_Dependency) { // The build system wants the .d file name stem to be exactly the same as // the source .rs file, instead of the .bc file. OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile)); } else { OutputFile.append( slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile)); } switch (OutputType) { case slang::Slang::OT_Dependency: { OutputFile.append(".d"); break; } case slang::Slang::OT_Assembly: { OutputFile.append(".S"); break; } case slang::Slang::OT_LLVMAssembly: { OutputFile.append(".ll"); break; } case slang::Slang::OT_Object: { OutputFile.append(".o"); break; } case slang::Slang::OT_Bitcode: { OutputFile.append(".bc"); break; } case slang::Slang::OT_Nothing: default: { slangAssert(false && "Invalid output type!"); } } return SaveStringInSet(SavedStrings, OutputFile); } #define str(s) #s #define wrap_str(s) str(s) static void llvm_rs_cc_VersionPrinter() { llvm::raw_ostream &OS = llvm::outs(); OS << "llvm-rs-cc: Renderscript compiler\n" << " (http://developer.android.com/guide/topics/renderscript)\n" << " based on LLVM (http://llvm.org):\n"; OS << " Built " << __DATE__ << " (" << __TIME__ ").\n"; OS << " Target APIs: " << SLANG_MINIMUM_TARGET_API << " - " << SLANG_MAXIMUM_TARGET_API; OS << "\n Build type: " << wrap_str(TARGET_BUILD_VARIANT); #ifndef __DISABLE_ASSERTS OS << " with assertions"; #endif OS << ".\n"; return; } #undef wrap_str #undef str int main(int argc, const char **argv) { std::set<std::string> SavedStrings; llvm::SmallVector<const char*, 256> ArgVector; RSCCOptions Opts; llvm::SmallVector<const char*, 16> Inputs; std::string Argv0; atexit(llvm::llvm_shutdown); ExpandArgv(argc, argv, ArgVector, SavedStrings); // Argv0 Argv0 = llvm::sys::path::stem(ArgVector[0]); // Setup diagnostic engine slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer(); llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs( new clang::DiagnosticIDs()); clang::DiagnosticsEngine DiagEngine(DiagIDs, DiagClient, true); slang::Slang::GlobalInitialization(); ParseArguments(ArgVector, Inputs, Opts, DiagEngine); // Exits when there's any error occurred during parsing the arguments if (DiagEngine.hasErrorOccurred()) { llvm::errs() << DiagClient->str(); return 1; } if (Opts.mShowHelp) { llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable()); OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(), "Renderscript source compiler"); return 0; } if (Opts.mShowVersion) { llvm_rs_cc_VersionPrinter(); return 0; } // No input file if (Inputs.empty()) { DiagEngine.Report(clang::diag::err_drv_no_input_files); llvm::errs() << DiagClient->str(); return 1; } // Prepare input data for RS compiler. std::list<std::pair<const char*, const char*> > IOFiles; std::list<std::pair<const char*, const char*> > DepFiles; llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS()); Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures, &DiagEngine, DiagClient); for (int i = 0, e = Inputs.size(); i != e; i++) { const char *InputFile = Inputs[i]; const char *OutputFile = DetermineOutputFile(Opts.mOutputDir, InputFile, Opts.mOutputType, SavedStrings); if (Opts.mOutputDep) { const char *BCOutputFile, *DepOutputFile; if (Opts.mOutputType == slang::Slang::OT_Bitcode) BCOutputFile = OutputFile; else BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir, InputFile, slang::Slang::OT_Bitcode, SavedStrings); if (Opts.mOutputType == slang::Slang::OT_Dependency) DepOutputFile = OutputFile; else DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir, InputFile, slang::Slang::OT_Dependency, SavedStrings); DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile)); } IOFiles.push_back(std::make_pair(InputFile, OutputFile)); } // Let's rock! int CompileFailed = !Compiler->compile(IOFiles, DepFiles, Opts.mIncludePaths, Opts.mAdditionalDepTargets, Opts.mOutputType, Opts.mBitcodeStorage, Opts.mAllowRSPrefix, Opts.mOutputDep, Opts.mTargetAPI, Opts.mDebugEmission, Opts.mOptimizationLevel, Opts.mJavaReflectionPathBase, Opts.mJavaReflectionPackageName, Opts.mRSPackageName); Compiler->reset(); return CompileFailed; } /////////////////////////////////////////////////////////////////////////////// // ExpandArgsFromBuf - static void ExpandArgsFromBuf(const char *Arg, llvm::SmallVectorImpl<const char*> &ArgVector, std::set<std::string> &SavedStrings) { const char *FName = Arg + 1; llvm::OwningPtr<llvm::MemoryBuffer> MemBuf; if (llvm::MemoryBuffer::getFile(FName, MemBuf)) { // Unable to open the file ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); return; } const char *Buf = MemBuf->getBufferStart(); char InQuote = ' '; std::string CurArg; for (const char *P = Buf; ; ++P) { if (*P == '\0' || (isspace(*P) && InQuote == ' ')) { if (!CurArg.empty()) { if (CurArg[0] != '@') { ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); } else { ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); } CurArg = ""; } if (*P == '\0') break; else continue; } if (isspace(*P)) { if (InQuote != ' ') CurArg.push_back(*P); continue; } if (*P == '"' || *P == '\'') { if (InQuote == *P) InQuote = ' '; else if (InQuote == ' ') InQuote = *P; else CurArg.push_back(*P); continue; } if (*P == '\\') { ++P; if (*P != '\0') CurArg.push_back(*P); continue; } CurArg.push_back(*P); } } // ExpandArgsFromBuf - static void ExpandArgv(int argc, const char **argv, llvm::SmallVectorImpl<const char*> &ArgVector, std::set<std::string> &SavedStrings) { for (int i = 0; i < argc; ++i) { const char *Arg = argv[i]; if (Arg[0] != '@') { ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); continue; } ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); } }