1/*
2 * Copyright 2014, 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 "clang/Basic/DiagnosticOptions.h"
18#include "clang/Driver/DriverDiagnostic.h"
19#include "clang/Driver/Options.h"
20#include "clang/Frontend/Utils.h"
21
22#include "llvm/Option/Arg.h"
23#include "llvm/Option/ArgList.h"
24#include "llvm/Option/Option.h"
25#include "llvm/Option/OptTable.h"
26#include "llvm/Support/CommandLine.h"
27
28#include "rs_cc_options.h"
29#include "slang.h"
30#include "slang_assert.h"
31
32#include <cstdlib>
33#include <string>
34#include <utility>
35#include <vector>
36
37enum {
38  OPT_INVALID = 0,  // This is not an option ID.
39#define PREFIX(NAME, VALUE)
40#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
41               HELPTEXT, METAVAR)                                             \
42  OPT_##ID,
43#include "RSCCOptions.inc"
44  LastOption
45#undef OPTION
46#undef PREFIX
47};
48
49#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
50#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
51               HELPTEXT, METAVAR)
52#include "RSCCOptions.inc"
53#undef OPTION
54#undef PREFIX
55
56static const llvm::opt::OptTable::Info RSCCInfoTable[] = {
57#define PREFIX(NAME, VALUE)
58#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
59               HELPTEXT, METAVAR)                                              \
60  {                                                                            \
61    PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
62        PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS                      \
63  }                                                                            \
64  ,
65#include "RSCCOptions.inc"
66#undef OPTION
67#undef PREFIX
68};
69
70namespace {
71
72class RSCCOptTable : public llvm::opt::OptTable {
73 public:
74  RSCCOptTable()
75      : OptTable(RSCCInfoTable,
76                 sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {}
77};
78}
79
80namespace slang {
81
82llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
83
84// This function is similar to
85// clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
86bool ParseArguments(const llvm::ArrayRef<const char *> &ArgsIn,
87                    llvm::SmallVectorImpl<const char *> &Inputs,
88                    RSCCOptions &Opts, clang::DiagnosticOptions &DiagOpts,
89                    llvm::cl::StringSaver &StringSaver) {
90  // We use a different diagnostic engine for argument parsing from the rest of
91  // the work.  This mimics what's done in clang.  I believe it is so the
92  // argument parsing errors are well formatted while the full errors can be
93  // influenced by command line arguments.
94  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> ArgumentParseDiagOpts(
95      new clang::DiagnosticOptions());
96  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
97      new clang::DiagnosticIDs());
98  DiagnosticBuffer DiagsBuffer;
99  clang::DiagnosticsEngine DiagEngine(DiagIDs, &*ArgumentParseDiagOpts,
100                                      &DiagsBuffer, false);
101
102  // Populate a vector with the command line arguments, expanding command files
103  // that have been included via the '@' argument.
104  llvm::SmallVector<const char *, 256> ArgVector;
105  ArgVector.append(ArgsIn.begin(), ArgsIn.end());
106  llvm::cl::ExpandResponseFiles(StringSaver, llvm::cl::TokenizeGNUCommandLine,
107                                ArgVector, false);
108
109  std::unique_ptr<llvm::opt::OptTable> OptParser(createRSCCOptTable());
110  unsigned MissingArgIndex = 0;
111  unsigned MissingArgCount = 0;
112  std::unique_ptr<llvm::opt::InputArgList> Args(
113      OptParser->ParseArgs(ArgVector.begin() + 1, ArgVector.end(),
114                           MissingArgIndex, MissingArgCount));
115
116  // Check for missing argument error.
117  if (MissingArgCount) {
118    DiagEngine.Report(clang::diag::err_drv_missing_argument)
119        << Args->getArgString(MissingArgIndex) << MissingArgCount;
120  }
121
122  // Issue errors on unknown arguments.
123  for (llvm::opt::arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
124                               ie = Args->filtered_end();
125       it != ie; ++it) {
126    DiagEngine.Report(clang::diag::err_drv_unknown_argument)
127        << (*it)->getAsString(*Args);
128  }
129
130  DiagOpts.IgnoreWarnings = Args->hasArg(OPT_w);
131  DiagOpts.Warnings = Args->getAllArgValues(OPT_W);
132
133  for (llvm::opt::ArgList::const_iterator it = Args->begin(), ie = Args->end();
134       it != ie; ++it) {
135    const llvm::opt::Arg *A = *it;
136    if (A->getOption().getKind() == llvm::opt::Option::InputClass)
137      Inputs.push_back(A->getValue());
138  }
139
140  Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
141
142  Opts.mBitcodeOutputDir = Args->getLastArgValue(OPT_o);
143
144  if (const llvm::opt::Arg *A = Args->getLastArg(OPT_M_Group)) {
145    switch (A->getOption().getID()) {
146    case OPT_M: {
147      Opts.mEmitDependency = true;
148      Opts.mOutputType = Slang::OT_Dependency;
149      break;
150    }
151    case OPT_MD: {
152      Opts.mEmitDependency = true;
153      Opts.mOutputType = Slang::OT_Bitcode;
154      break;
155    }
156    default: { slangAssert(false && "Invalid option in M group!"); }
157    }
158  }
159
160  if (const llvm::opt::Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
161    switch (A->getOption().getID()) {
162    case OPT_emit_asm: {
163      Opts.mOutputType = Slang::OT_Assembly;
164      break;
165    }
166    case OPT_emit_llvm: {
167      Opts.mOutputType = Slang::OT_LLVMAssembly;
168      break;
169    }
170    case OPT_emit_bc: {
171      Opts.mOutputType = Slang::OT_Bitcode;
172      break;
173    }
174    case OPT_emit_nothing: {
175      Opts.mOutputType = Slang::OT_Nothing;
176      break;
177    }
178    default: { slangAssert(false && "Invalid option in output type group!"); }
179    }
180  }
181
182  if (Opts.mEmitDependency && ((Opts.mOutputType != Slang::OT_Bitcode) &&
183                               (Opts.mOutputType != Slang::OT_Dependency)))
184    DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
185        << Args->getLastArg(OPT_M_Group)->getAsString(*Args)
186        << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
187
188  Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
189
190  Opts.mJavaReflectionPathBase =
191      Args->getLastArgValue(OPT_java_reflection_path_base);
192  Opts.mJavaReflectionPackageName =
193      Args->getLastArgValue(OPT_java_reflection_package_name);
194
195  Opts.mRSPackageName = Args->getLastArgValue(OPT_rs_package_name);
196
197  llvm::StringRef BitcodeStorageValue =
198      Args->getLastArgValue(OPT_bitcode_storage);
199  if (BitcodeStorageValue == "ar")
200    Opts.mBitcodeStorage = BCST_APK_RESOURCE;
201  else if (BitcodeStorageValue == "jc")
202    Opts.mBitcodeStorage = BCST_JAVA_CODE;
203  else if (!BitcodeStorageValue.empty())
204    DiagEngine.Report(clang::diag::err_drv_invalid_value)
205        << OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue;
206
207  llvm::opt::Arg *lastBitwidthArg = Args->getLastArg(OPT_m32, OPT_m64);
208  if (Args->hasArg(OPT_reflect_cpp)) {
209    Opts.mBitcodeStorage = BCST_CPP_CODE;
210    // mJavaReflectionPathBase can be set for C++ reflected builds.
211    // Set it to the standard mBitcodeOutputDir (via -o) by default.
212    if (Opts.mJavaReflectionPathBase.empty()) {
213      Opts.mJavaReflectionPathBase = Opts.mBitcodeOutputDir;
214    }
215
216    // Check for bitwidth arguments.
217    if (lastBitwidthArg) {
218      if (lastBitwidthArg->getOption().matches(OPT_m32)) {
219        Opts.mBitWidth = 32;
220      } else {
221        Opts.mBitWidth = 64;
222      }
223    }
224  } else if (lastBitwidthArg) {
225    // -m32/-m64 are forbidden for non-C++ reflection paths.
226    DiagEngine.Report(
227        DiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
228                                   "cannot use -m32/-m64 without specifying "
229                                   "C++ reflection (-reflect-c++)"));
230  }
231
232  Opts.mDependencyOutputDir =
233      Args->getLastArgValue(OPT_output_dep_dir, Opts.mBitcodeOutputDir);
234  Opts.mAdditionalDepTargets = Args->getAllArgValues(OPT_additional_dep_target);
235
236  Opts.mShowHelp = Args->hasArg(OPT_help);
237  Opts.mShowVersion = Args->hasArg(OPT_version);
238  Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
239  Opts.mVerbose = Args->hasArg(OPT_verbose);
240
241  // If we are emitting both 32-bit and 64-bit bitcode, we must embed it.
242
243  size_t OptLevel =
244      clang::getLastArgIntValue(*Args, OPT_optimization_level, 3, DiagEngine);
245
246  Opts.mOptimizationLevel =
247      OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive;
248
249  Opts.mTargetAPI =
250      clang::getLastArgIntValue(*Args, OPT_target_api, RS_VERSION, DiagEngine);
251
252  if (Opts.mTargetAPI == 0) {
253    Opts.mTargetAPI = UINT_MAX;
254  }
255
256  Opts.mEmit3264 =
257      (Opts.mTargetAPI >= 21) && (Opts.mBitcodeStorage != BCST_CPP_CODE);
258  if (Opts.mEmit3264) {
259    Opts.mBitcodeStorage = BCST_JAVA_CODE;
260  }
261
262  if (DiagEngine.hasErrorOccurred()) {
263    llvm::errs() << DiagsBuffer.str();
264    return false;
265  }
266
267  return true;
268}
269}
270