llvm-rs-cc.cpp revision 603217c72ef310ea385f906662c7d6136d45f464
1#include "slang.h" 2 3#include <set> 4#include <string> 5#include <cstdlib> 6 7#include "llvm/ADT/SmallVector.h" 8 9#include "llvm/Support/CommandLine.h" 10#include "llvm/Support/ManagedStatic.h" 11#include "llvm/Support/MemoryBuffer.h" 12 13#include "llvm/System/Path.h" 14 15#include "clang/Driver/Arg.h" 16#include "clang/Driver/ArgList.h" 17#include "clang/Driver/DriverDiagnostic.h" 18#include "clang/Driver/Option.h" 19#include "clang/Driver/OptTable.h" 20 21#include "clang/Frontend/DiagnosticOptions.h" 22#include "clang/Frontend/TextDiagnosticPrinter.h" 23 24#include "slang_rs.h" 25#include "slang_rs_reflect_utils.h" 26 27using namespace slang; 28 29using namespace clang::driver::options; 30 31// Class under clang::driver used are enumerated here. 32using clang::driver::Arg; 33using clang::driver::ArgList; 34using clang::driver::InputArgList; 35using clang::driver::Option; 36using clang::driver::OptTable; 37using clang::driver::arg_iterator; 38 39// SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from 40// $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in 41// main(). 42static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings, 43 llvm::StringRef S) { 44 return SavedStrings.insert(S).first->c_str(); 45} 46static void ExpandArgsFromBuf(const char *Arg, 47 llvm::SmallVectorImpl<const char*> &ArgVector, 48 std::set<std::string> &SavedStrings); 49static void ExpandArgv(int argc, const char **argv, 50 llvm::SmallVectorImpl<const char*> &ArgVector, 51 std::set<std::string> &SavedStrings); 52 53enum { 54 OPT_INVALID = 0, // This is not an option ID. 55#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ 56 HELPTEXT, METAVAR) OPT_##ID, 57#include "RSCCOptions.inc" 58 LastOption 59#undef OPTION 60}; 61 62static const OptTable::Info RSCCInfoTable[] = { 63#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ 64 HELPTEXT, METAVAR) \ 65 { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \ 66 OPT_##GROUP, OPT_##ALIAS }, 67#include "RSCCOptions.inc" 68}; 69 70class RSCCOptTable : public OptTable { 71 public: 72 RSCCOptTable() 73 : OptTable(RSCCInfoTable, 74 sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) { 75 } 76}; 77 78OptTable *createRSCCOptTable() { 79 return new RSCCOptTable(); 80} 81 82/////////////////////////////////////////////////////////////////////////////// 83 84class RSCCOptions { 85 public: 86 // The include search paths 87 std::vector<std::string> mIncludePaths; 88 89 // The output directory, if any. 90 std::string mOutputDir; 91 92 // The output type 93 Slang::OutputType mOutputType; 94 95 unsigned mAllowRSPrefix : 1; 96 97 // If given, the name of the target triple to compile for. If not given the 98 // target will be selected to match the host. 99 std::string mTriple; 100 101 // If given, the name of the target CPU to generate code for. 102 std::string mCPU; 103 104 // The list of target specific features to enable or disable -- this should 105 // be a list of strings starting with by '+' or '-'. 106 std::vector<std::string> mFeatures; 107 108 std::string mJavaReflectionPathBase; 109 110 std::string mJavaReflectionPackageName; 111 112 BitCodeStorageType mBitcodeStorage; 113 114 unsigned mOutputDep : 1; 115 116 std::string mOutputDepDir; 117 118 std::vector<std::string> mAdditionalDepTargets; 119 120 unsigned mShowHelp : 1; // Show the -help text. 121 unsigned mShowVersion : 1; // Show the -version text. 122 123 RSCCOptions() { 124 mOutputType = Slang::OT_Bitcode; 125 mBitcodeStorage = BCST_APK_RESOURCE; 126 mOutputDep = 0; 127 mShowHelp = 0; 128 mShowVersion = 0; 129 } 130}; 131 132// ParseArguments - 133static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector, 134 llvm::SmallVectorImpl<const char*> &Inputs, 135 RSCCOptions &Opts, 136 clang::Diagnostic &Diags) { 137 if (ArgVector.size() > 1) { 138 const char **ArgBegin = ArgVector.data() + 1; 139 const char **ArgEnd = ArgVector.data() + ArgVector.size(); 140 unsigned MissingArgIndex, MissingArgCount; 141 llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable()); 142 llvm::OwningPtr<InputArgList> Args( 143 OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount)); 144 145 // Check for missing argument error. 146 if (MissingArgCount) 147 Diags.Report(clang::diag::err_drv_missing_argument) 148 << Args->getArgString(MissingArgIndex) << MissingArgCount; 149 150 // Issue errors on unknown arguments. 151 for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), 152 ie = Args->filtered_end(); it != ie; ++it) 153 Diags.Report(clang::diag::err_drv_unknown_argument) 154 << (*it)->getAsString(*Args); 155 156 for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); 157 it != ie; ++it) { 158 const Arg *A = *it; 159 if (A->getOption().getKind() == Option::InputClass) 160 Inputs.push_back(A->getValue(*Args)); 161 } 162 163 Opts.mIncludePaths = Args->getAllArgValues(OPT_I); 164 165 Opts.mOutputDir = Args->getLastArgValue(OPT_o); 166 167 if (const Arg *A = Args->getLastArg(OPT_M_Group)) { 168 switch (A->getOption().getID()) { 169 case OPT_M: { 170 Opts.mOutputDep = 1; 171 Opts.mOutputType = Slang::OT_Dependency; 172 break; 173 } 174 case OPT_MD: { 175 Opts.mOutputDep = 1; 176 Opts.mOutputType = Slang::OT_Bitcode; 177 } 178 default: { 179 assert(false && "Invalid option in M group!"); 180 } 181 } 182 } 183 184 if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) { 185 switch (A->getOption().getID()) { 186 case OPT_emit_asm: { 187 Opts.mOutputType = Slang::OT_Assembly; 188 break; 189 } 190 case OPT_emit_llvm: { 191 Opts.mOutputType = Slang::OT_LLVMAssembly; 192 break; 193 } 194 case OPT_emit_bc: { 195 Opts.mOutputType = Slang::OT_Bitcode; 196 break; 197 } 198 case OPT_emit_nothing: { 199 Opts.mOutputType = Slang::OT_Nothing; 200 break; 201 } 202 default: { 203 assert(false && "Invalid option in output type group!"); 204 } 205 } 206 } 207 208 if (Opts.mOutputType != Slang::OT_Bitcode) 209 Diags.Report(clang::diag::err_drv_argument_not_allowed_with) 210 << Args->getLastArg(OPT_M_Group)->getAsString(*Args) 211 << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args); 212 213 Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix); 214 215 Opts.mTriple = Args->getLastArgValue(OPT_triple, 216 "armv7-none-linux-gnueabi"); 217 Opts.mCPU = Args->getLastArgValue(OPT_target_cpu); 218 Opts.mFeatures = Args->getAllArgValues(OPT_target_feature); 219 220 Opts.mJavaReflectionPathBase = 221 Args->getLastArgValue(OPT_java_reflection_path_base); 222 Opts.mJavaReflectionPackageName = 223 Args->getLastArgValue(OPT_java_reflection_package_name); 224 225 llvm::StringRef BitcodeStorageValue = 226 Args->getLastArgValue(OPT_bitcode_storage); 227 if (BitcodeStorageValue == "ar") 228 Opts.mBitcodeStorage = BCST_APK_RESOURCE; 229 else if (BitcodeStorageValue == "jc") 230 Opts.mBitcodeStorage = BCST_JAVA_CODE; 231 else if (!BitcodeStorageValue.empty()) 232 Diags.Report(clang::diag::err_drv_invalid_value) 233 << OptParser->getOptionName(OPT_bitcode_storage) 234 << BitcodeStorageValue; 235 236 Opts.mOutputDepDir = 237 Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir); 238 Opts.mAdditionalDepTargets = 239 Args->getAllArgValues(OPT_additional_dep_target); 240 241 Opts.mShowHelp = Args->hasArg(OPT_help); 242 Opts.mShowVersion = Args->hasArg(OPT_version); 243 } 244 245 return; 246} 247 248static const char *DetermineOutputFile(const std::string &OutputDir, 249 const char *InputFile, 250 Slang::OutputType OutputTyupe, 251 std::set<std::string> &SavedStrings) { 252 if (OutputTyupe == Slang::OT_Nothing) 253 return "/dev/null"; 254 255 std::string OutputFile(OutputDir); 256 257 // Append '/' to Opts.mOutputDir if not presents 258 if (!OutputFile.empty() && 259 (OutputFile[OutputFile.size() - 1]) != '/') 260 OutputFile.append(1, '/'); 261 262 if (OutputTyupe == Slang::OT_Dependency) 263 // The build system wants the .d file name stem to be exactly the same as 264 // the source .rs file, instead of the .bc file. 265 OutputFile.append(RSSlangReflectUtils::GetFileNameStem(InputFile)); 266 else 267 OutputFile.append(RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile)); 268 269 switch (OutputTyupe) { 270 case Slang::OT_Dependency: { 271 OutputFile.append(".d"); 272 break; 273 } 274 case Slang::OT_Assembly: { 275 OutputFile.append(".S"); 276 break; 277 } 278 case Slang::OT_LLVMAssembly: { 279 OutputFile.append(".ll"); 280 break; 281 } 282 case Slang::OT_Object: { 283 OutputFile.append(".o"); 284 break; 285 } 286 case Slang::OT_Bitcode: { 287 OutputFile.append(".bc"); 288 break; 289 } 290 case Slang::OT_Nothing: 291 default: { 292 assert(false && "Invalid output type!"); 293 } 294 } 295 296 return SaveStringInSet(SavedStrings, OutputFile); 297} 298 299static bool GenerateBitcodeAccessor(const char *InputFile, 300 const char *OutputFile, 301 const char *PackageName, 302 const RSCCOptions &Opts) { 303 if ((Opts.mOutputType != Slang::OT_Bitcode) || 304 (Opts.mBitcodeStorage != BCST_JAVA_CODE)) 305 return true; 306 307 RSSlangReflectUtils::BitCodeAccessorContext BCAccessorContext; 308 BCAccessorContext.rsFileName = InputFile; 309 BCAccessorContext.bcFileName = OutputFile; 310 BCAccessorContext.reflectPath = Opts.mJavaReflectionPathBase.c_str(); 311 BCAccessorContext.packageName = PackageName; 312 BCAccessorContext.bcStorage = Opts.mBitcodeStorage; 313 314 return RSSlangReflectUtils::GenerateBitCodeAccessor(BCAccessorContext); 315} 316 317// ExecuteCompilation - 318static bool ExecuteCompilation(SlangRS &Compiler, 319 const char *InputFile, 320 const char *OutputFile, 321 const char *DepOutputFile, 322 const RSCCOptions &Opts) { 323 std::string RealPackageName; 324 325 Compiler.reset(); 326 327 Compiler.setIncludePaths(Opts.mIncludePaths); 328 Compiler.setOutputType(Opts.mOutputType); 329 Compiler.allowRSPrefix(Opts.mAllowRSPrefix); 330 331 if (!Compiler.setInputSource(InputFile)) 332 return false; 333 334 if (!Compiler.setOutput(OutputFile)) 335 return false; 336 337 if (Opts.mOutputDep) { 338 if (!Compiler.setDepOutput(DepOutputFile)) 339 return false; 340 341 if (Opts.mOutputType != Slang::OT_Dependency) 342 Compiler.setDepTargetBC(OutputFile); 343 Compiler.setAdditionalDepTargets(Opts.mAdditionalDepTargets); 344 345 if (Compiler.generateDepFile() > 0) 346 return false; 347 } 348 349 if (Compiler.compile() > 0) 350 return false; 351 352 if (Opts.mOutputType != Slang::OT_Dependency) { 353 if (!Compiler.reflectToJava(Opts.mJavaReflectionPathBase, 354 Opts.mJavaReflectionPackageName, 355 &RealPackageName)) 356 return false; 357 358 if (!GenerateBitcodeAccessor(InputFile, 359 OutputFile, 360 RealPackageName.c_str(), 361 Opts)) 362 return false; 363 } 364 365 return true; 366} 367 368int main(int argc, const char **argv) { 369 std::set<std::string> SavedStrings; 370 llvm::SmallVector<const char*, 256> ArgVector; 371 RSCCOptions Opts; 372 llvm::SmallVector<const char*, 16> Inputs; 373 std::string Argv0; 374 375 atexit(llvm::llvm_shutdown); 376 377 ExpandArgv(argc, argv, ArgVector, SavedStrings); 378 379 // Argv0 380 llvm::sys::Path Path = llvm::sys::Path(ArgVector[0]); 381 Argv0 = Path.getBasename(); 382 383 // Setup diagnostic engine 384 clang::TextDiagnosticPrinter *DiagClient = 385 new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions()); 386 DiagClient->setPrefix(Argv0); 387 clang::Diagnostic Diags(DiagClient); 388 389 Slang::GlobalInitialization(); 390 391 ParseArguments(ArgVector, Inputs, Opts, Diags); 392 393 // Exits when there's any error occurred during parsing the arguments 394 if (Diags.getNumErrors() > 0) 395 return 1; 396 397 if (Opts.mShowHelp) { 398 llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable()); 399 OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(), 400 "RenderScript source compiler"); 401 return 0; 402 } 403 404 if (Opts.mShowVersion) { 405 llvm::cl::PrintVersionMessage(); 406 return 0; 407 } 408 409 // No input file 410 if (Inputs.empty()) { 411 Diags.Report(clang::diag::err_drv_no_input_files); 412 return 1; 413 } 414 415 const char *InputFile, *OutputFile, *DepOutputFile = NULL; 416 llvm::OwningPtr<SlangRS> Compiler(new SlangRS(Opts.mTriple, Opts.mCPU, 417 Opts.mFeatures)); 418 419 for (int i = 0, e = Inputs.size(); i != e; i++) { 420 InputFile = Inputs[i]; 421 OutputFile = DetermineOutputFile(Opts.mOutputDir, InputFile, 422 Opts.mOutputType, SavedStrings); 423 if (Opts.mOutputDep) { 424 if (Opts.mOutputType == Slang::OT_Dependency) 425 DepOutputFile = OutputFile; 426 else 427 DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir, InputFile, 428 Slang::OT_Dependency, SavedStrings); 429 } 430 if (!ExecuteCompilation(*Compiler, 431 InputFile, 432 OutputFile, 433 DepOutputFile, 434 Opts)) { 435 llvm::errs() << "Failed to compile '" << InputFile << "' (" 436 << Compiler->getErrorMessage() << ")"; 437 break; 438 } 439 } 440 441 return 0; 442} 443 444/////////////////////////////////////////////////////////////////////////////// 445 446// ExpandArgsFromBuf - 447static void ExpandArgsFromBuf(const char *Arg, 448 llvm::SmallVectorImpl<const char*> &ArgVector, 449 std::set<std::string> &SavedStrings) { 450 const char *FName = Arg + 1; 451 llvm::MemoryBuffer *MemBuf = llvm::MemoryBuffer::getFile(FName); 452 if (!MemBuf) { 453 ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); 454 return; 455 } 456 457 const char *Buf = MemBuf->getBufferStart(); 458 char InQuote = ' '; 459 std::string CurArg; 460 461 for (const char *P = Buf; ; ++P) { 462 if (*P == '\0' || (isspace(*P) && InQuote == ' ')) { 463 if (!CurArg.empty()) { 464 if (CurArg[0] != '@') { 465 ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); 466 } else { 467 ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); 468 } 469 470 CurArg = ""; 471 } 472 if (*P == '\0') 473 break; 474 else 475 continue; 476 } 477 478 if (isspace(*P)) { 479 if (InQuote != ' ') 480 CurArg.push_back(*P); 481 continue; 482 } 483 484 if (*P == '"' || *P == '\'') { 485 if (InQuote == *P) 486 InQuote = ' '; 487 else if (InQuote == ' ') 488 InQuote = *P; 489 else 490 CurArg.push_back(*P); 491 continue; 492 } 493 494 if (*P == '\\') { 495 ++P; 496 if (*P != '\0') 497 CurArg.push_back(*P); 498 continue; 499 } 500 CurArg.push_back(*P); 501 } 502 delete MemBuf; 503} 504 505// ExpandArgsFromBuf - 506static void ExpandArgv(int argc, const char **argv, 507 llvm::SmallVectorImpl<const char*> &ArgVector, 508 std::set<std::string> &SavedStrings) { 509 for (int i = 0; i < argc; ++i) { 510 const char *Arg = argv[i]; 511 if (Arg[0] != '@') { 512 ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); 513 continue; 514 } 515 516 ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); 517 } 518} 519