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