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