llvm-rs-cc.cpp revision 2e35b136cc2434080fcd682d2f95e53a87675dd4
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/system_error.h" 42 43#include "slang.h" 44#include "slang_assert.h" 45#include "slang_rs.h" 46#include "slang_rs_reflect_utils.h" 47 48// Class under clang::driver used are enumerated here. 49using clang::driver::arg_iterator; 50using clang::driver::options::DriverOption; 51using clang::driver::Arg; 52using clang::driver::ArgList; 53using clang::driver::InputArgList; 54using clang::driver::Option; 55using clang::driver::OptTable; 56 57// SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from 58// $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in 59// main(). 60static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings, 61 llvm::StringRef S) { 62 return SavedStrings.insert(S).first->c_str(); 63} 64static void ExpandArgsFromBuf(const char *Arg, 65 llvm::SmallVectorImpl<const char*> &ArgVector, 66 std::set<std::string> &SavedStrings); 67static void ExpandArgv(int argc, const char **argv, 68 llvm::SmallVectorImpl<const char*> &ArgVector, 69 std::set<std::string> &SavedStrings); 70 71enum { 72 OPT_INVALID = 0, // This is not an option ID. 73#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ 74 HELPTEXT, METAVAR) OPT_##ID, 75#include "RSCCOptions.inc" 76 LastOption 77#undef OPTION 78}; 79 80static const OptTable::Info RSCCInfoTable[] = { 81#define OPTION(NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \ 82 HELPTEXT, METAVAR) \ 83 { NAME, HELPTEXT, METAVAR, Option::KIND##Class, FLAGS, PARAM, \ 84 OPT_##GROUP, OPT_##ALIAS }, 85#include "RSCCOptions.inc" 86}; 87 88class RSCCOptTable : public OptTable { 89 public: 90 RSCCOptTable() 91 : OptTable(RSCCInfoTable, 92 sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) { 93 } 94}; 95 96OptTable *createRSCCOptTable() { 97 return new RSCCOptTable(); 98} 99 100/////////////////////////////////////////////////////////////////////////////// 101 102class RSCCOptions { 103 public: 104 // The include search paths 105 std::vector<std::string> mIncludePaths; 106 107 // The output directory, if any. 108 std::string mOutputDir; 109 110 // The output type 111 slang::Slang::OutputType mOutputType; 112 113 unsigned mAllowRSPrefix : 1; 114 115 // If given, the name of the target triple to compile for. If not given the 116 // target will be selected to match the host. 117 std::string mTriple; 118 119 // If given, 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 mBitcodeStorage = slang::BCST_APK_RESOURCE; 146 mOutputDep = 0; 147 mShowHelp = 0; 148 mShowVersion = 0; 149 mTargetAPI = RS_VERSION; 150 } 151}; 152 153// ParseArguments - 154static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector, 155 llvm::SmallVectorImpl<const char*> &Inputs, 156 RSCCOptions &Opts, 157 clang::Diagnostic &Diags) { 158 if (ArgVector.size() > 1) { 159 const char **ArgBegin = ArgVector.data() + 1; 160 const char **ArgEnd = ArgVector.data() + ArgVector.size(); 161 unsigned MissingArgIndex, MissingArgCount; 162 llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable()); 163 llvm::OwningPtr<InputArgList> Args( 164 OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount)); 165 166 // Check for missing argument error. 167 if (MissingArgCount) 168 Diags.Report(clang::diag::err_drv_missing_argument) 169 << Args->getArgString(MissingArgIndex) << MissingArgCount; 170 171 // Issue errors on unknown arguments. 172 for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), 173 ie = Args->filtered_end(); it != ie; ++it) 174 Diags.Report(clang::diag::err_drv_unknown_argument) 175 << (*it)->getAsString(*Args); 176 177 for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); 178 it != ie; ++it) { 179 const Arg *A = *it; 180 if (A->getOption().getKind() == Option::InputClass) 181 Inputs.push_back(A->getValue(*Args)); 182 } 183 184 Opts.mIncludePaths = Args->getAllArgValues(OPT_I); 185 186 Opts.mOutputDir = Args->getLastArgValue(OPT_o); 187 188 if (const Arg *A = Args->getLastArg(OPT_M_Group)) { 189 switch (A->getOption().getID()) { 190 case OPT_M: { 191 Opts.mOutputDep = 1; 192 Opts.mOutputType = slang::Slang::OT_Dependency; 193 break; 194 } 195 case OPT_MD: { 196 Opts.mOutputDep = 1; 197 Opts.mOutputType = slang::Slang::OT_Bitcode; 198 break; 199 } 200 default: { 201 slangAssert(false && "Invalid option in M group!"); 202 } 203 } 204 } 205 206 if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) { 207 switch (A->getOption().getID()) { 208 case OPT_emit_asm: { 209 Opts.mOutputType = slang::Slang::OT_Assembly; 210 break; 211 } 212 case OPT_emit_llvm: { 213 Opts.mOutputType = slang::Slang::OT_LLVMAssembly; 214 break; 215 } 216 case OPT_emit_bc: { 217 Opts.mOutputType = slang::Slang::OT_Bitcode; 218 break; 219 } 220 case OPT_emit_nothing: { 221 Opts.mOutputType = slang::Slang::OT_Nothing; 222 break; 223 } 224 default: { 225 slangAssert(false && "Invalid option in output type group!"); 226 } 227 } 228 } 229 230 if (Opts.mOutputDep && 231 ((Opts.mOutputType != slang::Slang::OT_Bitcode) && 232 (Opts.mOutputType != slang::Slang::OT_Dependency))) 233 Diags.Report(clang::diag::err_drv_argument_not_allowed_with) 234 << Args->getLastArg(OPT_M_Group)->getAsString(*Args) 235 << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args); 236 237 Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix); 238 239 Opts.mTriple = Args->getLastArgValue(OPT_triple, 240 "armv7-none-linux-gnueabi"); 241 Opts.mCPU = Args->getLastArgValue(OPT_target_cpu); 242 Opts.mFeatures = Args->getAllArgValues(OPT_target_feature); 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 Diags.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 Diags); 271 } 272 273 return; 274} 275 276static 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 329int main(int argc, const char **argv) { 330 std::set<std::string> SavedStrings; 331 llvm::SmallVector<const char*, 256> ArgVector; 332 RSCCOptions Opts; 333 llvm::SmallVector<const char*, 16> Inputs; 334 std::string Argv0; 335 336 atexit(llvm::llvm_shutdown); 337 338 ExpandArgv(argc, argv, ArgVector, SavedStrings); 339 340 // Argv0 341 Argv0 = llvm::sys::path::stem(ArgVector[0]); 342 343 // Setup diagnostic engine 344 clang::TextDiagnosticPrinter *DiagClient = 345 new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions()); 346 DiagClient->setPrefix(Argv0); 347 348 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs( 349 new clang::DiagnosticIDs()); 350 351 clang::Diagnostic Diags(DiagIDs, DiagClient, true); 352 353 slang::Slang::GlobalInitialization(); 354 355 ParseArguments(ArgVector, Inputs, Opts, Diags); 356 357 // Exits when there's any error occurred during parsing the arguments 358 if (Diags.hasErrorOccurred()) 359 return 1; 360 361 if (Opts.mShowHelp) { 362 llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable()); 363 OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(), 364 "RenderScript source compiler"); 365 return 0; 366 } 367 368 if (Opts.mShowVersion) { 369 llvm::cl::PrintVersionMessage(); 370 return 0; 371 } 372 373 // No input file 374 if (Inputs.empty()) { 375 Diags.Report(clang::diag::err_drv_no_input_files); 376 return 1; 377 } 378 379 // Prepare input data for RS compiler. 380 std::list<std::pair<const char*, const char*> > IOFiles; 381 std::list<std::pair<const char*, const char*> > DepFiles; 382 383 llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS()); 384 385 Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures); 386 387 for (int i = 0, e = Inputs.size(); i != e; i++) { 388 const char *InputFile = Inputs[i]; 389 const char *OutputFile = 390 DetermineOutputFile(Opts.mOutputDir, InputFile, 391 Opts.mOutputType, SavedStrings); 392 393 if (Opts.mOutputDep) { 394 const char *BCOutputFile, *DepOutputFile; 395 396 if (Opts.mOutputType == slang::Slang::OT_Bitcode) 397 BCOutputFile = OutputFile; 398 else 399 BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir, 400 InputFile, 401 slang::Slang::OT_Bitcode, 402 SavedStrings); 403 404 if (Opts.mOutputType == slang::Slang::OT_Dependency) 405 DepOutputFile = OutputFile; 406 else 407 DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir, 408 InputFile, 409 slang::Slang::OT_Dependency, 410 SavedStrings); 411 412 DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile)); 413 } 414 415 IOFiles.push_back(std::make_pair(InputFile, OutputFile)); 416 } 417 418 // Let's rock! 419 int CompileFailed = !Compiler->compile(IOFiles, 420 DepFiles, 421 Opts.mIncludePaths, 422 Opts.mAdditionalDepTargets, 423 Opts.mOutputType, 424 Opts.mBitcodeStorage, 425 Opts.mAllowRSPrefix, 426 Opts.mOutputDep, 427 Opts.mTargetAPI, 428 Opts.mJavaReflectionPathBase, 429 Opts.mJavaReflectionPackageName); 430 llvm::errs() << Compiler->getErrorMessage(); 431 432 return CompileFailed; 433} 434 435/////////////////////////////////////////////////////////////////////////////// 436 437// ExpandArgsFromBuf - 438static void ExpandArgsFromBuf(const char *Arg, 439 llvm::SmallVectorImpl<const char*> &ArgVector, 440 std::set<std::string> &SavedStrings) { 441 const char *FName = Arg + 1; 442 llvm::OwningPtr<llvm::MemoryBuffer> MemBuf; 443 if (llvm::MemoryBuffer::getFile(FName, MemBuf)) { 444 // Unable to open the file 445 ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); 446 return; 447 } 448 449 const char *Buf = MemBuf->getBufferStart(); 450 char InQuote = ' '; 451 std::string CurArg; 452 453 for (const char *P = Buf; ; ++P) { 454 if (*P == '\0' || (isspace(*P) && InQuote == ' ')) { 455 if (!CurArg.empty()) { 456 if (CurArg[0] != '@') { 457 ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); 458 } else { 459 ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); 460 } 461 462 CurArg = ""; 463 } 464 if (*P == '\0') 465 break; 466 else 467 continue; 468 } 469 470 if (isspace(*P)) { 471 if (InQuote != ' ') 472 CurArg.push_back(*P); 473 continue; 474 } 475 476 if (*P == '"' || *P == '\'') { 477 if (InQuote == *P) 478 InQuote = ' '; 479 else if (InQuote == ' ') 480 InQuote = *P; 481 else 482 CurArg.push_back(*P); 483 continue; 484 } 485 486 if (*P == '\\') { 487 ++P; 488 if (*P != '\0') 489 CurArg.push_back(*P); 490 continue; 491 } 492 CurArg.push_back(*P); 493 } 494} 495 496// ExpandArgsFromBuf - 497static void ExpandArgv(int argc, const char **argv, 498 llvm::SmallVectorImpl<const char*> &ArgVector, 499 std::set<std::string> &SavedStrings) { 500 for (int i = 0; i < argc; ++i) { 501 const char *Arg = argv[i]; 502 if (Arg[0] != '@') { 503 ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); 504 continue; 505 } 506 507 ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); 508 } 509} 510