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