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