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