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