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