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