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