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