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