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