llvm-rs-cc.cpp revision c6d19a19c34899b2f6f61b0359ce0b2bc2fe969e
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() << Compiler->getErrorMessage();
436      return 1;
437    }
438  }
439
440  return 0;
441}
442
443///////////////////////////////////////////////////////////////////////////////
444
445// ExpandArgsFromBuf -
446static void ExpandArgsFromBuf(const char *Arg,
447                              llvm::SmallVectorImpl<const char*> &ArgVector,
448                              std::set<std::string> &SavedStrings) {
449  const char *FName = Arg + 1;
450  llvm::MemoryBuffer *MemBuf = llvm::MemoryBuffer::getFile(FName);
451  if (!MemBuf) {
452    ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
453    return;
454  }
455
456  const char *Buf = MemBuf->getBufferStart();
457  char InQuote = ' ';
458  std::string CurArg;
459
460  for (const char *P = Buf; ; ++P) {
461    if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
462      if (!CurArg.empty()) {
463        if (CurArg[0] != '@') {
464          ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
465        } else {
466          ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
467        }
468
469        CurArg = "";
470      }
471      if (*P == '\0')
472        break;
473      else
474        continue;
475    }
476
477    if (isspace(*P)) {
478      if (InQuote != ' ')
479        CurArg.push_back(*P);
480      continue;
481    }
482
483    if (*P == '"' || *P == '\'') {
484      if (InQuote == *P)
485        InQuote = ' ';
486      else if (InQuote == ' ')
487        InQuote = *P;
488      else
489        CurArg.push_back(*P);
490      continue;
491    }
492
493    if (*P == '\\') {
494      ++P;
495      if (*P != '\0')
496        CurArg.push_back(*P);
497      continue;
498    }
499    CurArg.push_back(*P);
500  }
501  delete MemBuf;
502}
503
504// ExpandArgsFromBuf -
505static void ExpandArgv(int argc, const char **argv,
506                       llvm::SmallVectorImpl<const char*> &ArgVector,
507                       std::set<std::string> &SavedStrings) {
508  for (int i = 0; i < argc; ++i) {
509    const char *Arg = argv[i];
510    if (Arg[0] != '@') {
511      ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
512      continue;
513    }
514
515    ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
516  }
517}
518