llvm-rs-cc.cpp revision 8d5a2f6ab321615bfb3a46f68aff0b643a71caa0
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 35#include "llvm/Support/CommandLine.h" 36#include "llvm/Support/ManagedStatic.h" 37#include "llvm/Support/MemoryBuffer.h" 38 39#include "llvm/System/Path.h" 40 41#include "slang.h" 42#include "slang_assert.h" 43#include "slang_rs.h" 44#include "slang_rs_reflect_utils.h" 45 46// Class under clang::driver used are enumerated here. 47using clang::driver::arg_iterator; 48using clang::driver::options::DriverOption; 49using clang::driver::Arg; 50using clang::driver::ArgList; 51using clang::driver::InputArgList; 52using clang::driver::Option; 53using clang::driver::OptTable; 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::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 slang::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::Slang::OT_Bitcode; 141 mBitcodeStorage = slang::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::Slang::OT_Dependency; 188 break; 189 } 190 case OPT_MD: { 191 Opts.mOutputDep = 1; 192 Opts.mOutputType = slang::Slang::OT_Bitcode; 193 break; 194 } 195 default: { 196 slangAssert(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::Slang::OT_Assembly; 205 break; 206 } 207 case OPT_emit_llvm: { 208 Opts.mOutputType = slang::Slang::OT_LLVMAssembly; 209 break; 210 } 211 case OPT_emit_bc: { 212 Opts.mOutputType = slang::Slang::OT_Bitcode; 213 break; 214 } 215 case OPT_emit_nothing: { 216 Opts.mOutputType = slang::Slang::OT_Nothing; 217 break; 218 } 219 default: { 220 slangAssert(false && "Invalid option in output type group!"); 221 } 222 } 223 } 224 225 if (Opts.mOutputDep && 226 ((Opts.mOutputType != slang::Slang::OT_Bitcode) && 227 (Opts.mOutputType != slang::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 = slang::BCST_APK_RESOURCE; 248 else if (BitcodeStorageValue == "jc") 249 Opts.mBitcodeStorage = slang::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::Slang::OutputType OutputType, 270 std::set<std::string> &SavedStrings) { 271 if (OutputType == slang::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]) != OS_PATH_SEPARATOR) 279 OutputFile.append(1, OS_PATH_SEPARATOR); 280 281 if (OutputType == slang::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(slang::RSSlangReflectUtils::GetFileNameStem(InputFile)); 285 } else { 286 OutputFile.append( 287 slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile)); 288 } 289 290 switch (OutputType) { 291 case slang::Slang::OT_Dependency: { 292 OutputFile.append(".d"); 293 break; 294 } 295 case slang::Slang::OT_Assembly: { 296 OutputFile.append(".S"); 297 break; 298 } 299 case slang::Slang::OT_LLVMAssembly: { 300 OutputFile.append(".ll"); 301 break; 302 } 303 case slang::Slang::OT_Object: { 304 OutputFile.append(".o"); 305 break; 306 } 307 case slang::Slang::OT_Bitcode: { 308 OutputFile.append(".bc"); 309 break; 310 } 311 case slang::Slang::OT_Nothing: 312 default: { 313 slangAssert(false && "Invalid output type!"); 314 } 315 } 316 317 return SaveStringInSet(SavedStrings, OutputFile); 318} 319 320int main(int argc, const char **argv) { 321 std::set<std::string> SavedStrings; 322 llvm::SmallVector<const char*, 256> ArgVector; 323 RSCCOptions Opts; 324 llvm::SmallVector<const char*, 16> Inputs; 325 std::string Argv0; 326 327 atexit(llvm::llvm_shutdown); 328 329 ExpandArgv(argc, argv, ArgVector, SavedStrings); 330 331 // Argv0 332 llvm::sys::Path Path = llvm::sys::Path(ArgVector[0]); 333 Argv0 = Path.getBasename(); 334 335 // Setup diagnostic engine 336 clang::TextDiagnosticPrinter *DiagClient = 337 new clang::TextDiagnosticPrinter(llvm::errs(), clang::DiagnosticOptions()); 338 DiagClient->setPrefix(Argv0); 339 clang::Diagnostic Diags(DiagClient); 340 341 slang::Slang::GlobalInitialization(); 342 343 ParseArguments(ArgVector, Inputs, Opts, Diags); 344 345 // Exits when there's any error occurred during parsing the arguments 346 if (Diags.getNumErrors() > 0) 347 return 1; 348 349 if (Opts.mShowHelp) { 350 llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable()); 351 OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(), 352 "RenderScript source compiler"); 353 return 0; 354 } 355 356 if (Opts.mShowVersion) { 357 llvm::cl::PrintVersionMessage(); 358 return 0; 359 } 360 361 // No input file 362 if (Inputs.empty()) { 363 Diags.Report(clang::diag::err_drv_no_input_files); 364 return 1; 365 } 366 367 // Prepare input data for RS compiler. 368 std::list<std::pair<const char*, const char*> > IOFiles; 369 std::list<std::pair<const char*, const char*> > DepFiles; 370 371 llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS()); 372 373 Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures); 374 375 for (int i = 0, e = Inputs.size(); i != e; i++) { 376 const char *InputFile = Inputs[i]; 377 const char *OutputFile = 378 DetermineOutputFile(Opts.mOutputDir, InputFile, 379 Opts.mOutputType, SavedStrings); 380 381 if (Opts.mOutputDep) { 382 const char *BCOutputFile, *DepOutputFile; 383 384 if (Opts.mOutputType == slang::Slang::OT_Bitcode) 385 BCOutputFile = OutputFile; 386 else 387 BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir, 388 InputFile, 389 slang::Slang::OT_Bitcode, 390 SavedStrings); 391 392 if (Opts.mOutputType == slang::Slang::OT_Dependency) 393 DepOutputFile = OutputFile; 394 else 395 DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir, 396 InputFile, 397 slang::Slang::OT_Dependency, 398 SavedStrings); 399 400 DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile)); 401 } 402 403 IOFiles.push_back(std::make_pair(InputFile, OutputFile)); 404 } 405 406 // Let's rock! 407 if (!Compiler->compile(IOFiles, 408 DepFiles, 409 Opts.mIncludePaths, 410 Opts.mAdditionalDepTargets, 411 Opts.mOutputType, 412 Opts.mBitcodeStorage, 413 Opts.mAllowRSPrefix, 414 Opts.mOutputDep, 415 Opts.mJavaReflectionPathBase, 416 Opts.mJavaReflectionPackageName)) { 417 llvm::errs() << Compiler->getErrorMessage(); 418 return 1; 419 } 420 421 return 0; 422} 423 424/////////////////////////////////////////////////////////////////////////////// 425 426// ExpandArgsFromBuf - 427static void ExpandArgsFromBuf(const char *Arg, 428 llvm::SmallVectorImpl<const char*> &ArgVector, 429 std::set<std::string> &SavedStrings) { 430 const char *FName = Arg + 1; 431 llvm::MemoryBuffer *MemBuf = llvm::MemoryBuffer::getFile(FName); 432 if (!MemBuf) { 433 ArgVector.push_back(SaveStringInSet(SavedStrings, Arg)); 434 return; 435 } 436 437 const char *Buf = MemBuf->getBufferStart(); 438 char InQuote = ' '; 439 std::string CurArg; 440 441 for (const char *P = Buf; ; ++P) { 442 if (*P == '\0' || (isspace(*P) && InQuote == ' ')) { 443 if (!CurArg.empty()) { 444 if (CurArg[0] != '@') { 445 ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg)); 446 } else { 447 ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings); 448 } 449 450 CurArg = ""; 451 } 452 if (*P == '\0') 453 break; 454 else 455 continue; 456 } 457 458 if (isspace(*P)) { 459 if (InQuote != ' ') 460 CurArg.push_back(*P); 461 continue; 462 } 463 464 if (*P == '"' || *P == '\'') { 465 if (InQuote == *P) 466 InQuote = ' '; 467 else if (InQuote == ' ') 468 InQuote = *P; 469 else 470 CurArg.push_back(*P); 471 continue; 472 } 473 474 if (*P == '\\') { 475 ++P; 476 if (*P != '\0') 477 CurArg.push_back(*P); 478 continue; 479 } 480 CurArg.push_back(*P); 481 } 482 delete MemBuf; 483} 484 485// ExpandArgsFromBuf - 486static void ExpandArgv(int argc, const char **argv, 487 llvm::SmallVectorImpl<const char*> &ArgVector, 488 std::set<std::string> &SavedStrings) { 489 for (int i = 0; i < argc; ++i) { 490 const char *Arg = argv[i]; 491 if (Arg[0] != '@') { 492 ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg))); 493 continue; 494 } 495 496 ExpandArgsFromBuf(Arg, ArgVector, SavedStrings); 497 } 498} 499