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(llvm::makeArrayRef(RSCCInfoTable)) {}
76};
77}
78
79namespace slang {
80
81llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
82
83// This function is similar to
84// clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
85bool ParseArguments(const llvm::ArrayRef<const char *> &ArgsIn,
86                    llvm::SmallVectorImpl<const char *> &Inputs,
87                    RSCCOptions &Opts, clang::DiagnosticOptions &DiagOpts,
88                    llvm::StringSaver &StringSaver) {
89  // We use a different diagnostic engine for argument parsing from the rest of
90  // the work.  This mimics what's done in clang.  I believe it is so the
91  // argument parsing errors are well formatted while the full errors can be
92  // influenced by command line arguments.
93  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> ArgumentParseDiagOpts(
94      new clang::DiagnosticOptions());
95  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
96      new clang::DiagnosticIDs());
97  DiagnosticBuffer DiagsBuffer;
98  clang::DiagnosticsEngine DiagEngine(DiagIDs, &*ArgumentParseDiagOpts,
99                                      &DiagsBuffer, false);
100
101  // Populate a vector with the command line arguments, expanding command files
102  // that have been included via the '@' argument.
103  llvm::SmallVector<const char *, 256> ArgVector;
104  // Skip over the command name, or we will mistakenly process it as a source file.
105  ArgVector.append(ArgsIn.slice(1).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  llvm::opt::InputArgList Args =
113      OptParser->ParseArgs(ArgVector, MissingArgIndex, MissingArgCount);
114
115  // Check for missing argument error.
116  if (MissingArgCount) {
117    DiagEngine.Report(clang::diag::err_drv_missing_argument)
118        << Args.getArgString(MissingArgIndex) << MissingArgCount;
119  }
120
121  // Issue errors on unknown arguments.
122  for (llvm::opt::arg_iterator it = Args.filtered_begin(OPT_UNKNOWN),
123                               ie = Args.filtered_end();
124       it != ie; ++it) {
125    DiagEngine.Report(clang::diag::err_drv_unknown_argument)
126        << (*it)->getAsString(Args);
127  }
128
129  DiagOpts.IgnoreWarnings = Args.hasArg(OPT_w);
130  DiagOpts.Warnings = Args.getAllArgValues(OPT_W);
131
132  // Always turn off warnings for empty initializers, since we really want to
133  // employ/encourage this extension for zero-initialization of structures.
134  DiagOpts.Warnings.push_back("no-gnu-empty-initializer");
135
136  for (llvm::opt::ArgList::const_iterator it = Args.begin(), ie = Args.end();
137       it != ie; ++it) {
138    const llvm::opt::Arg *A = *it;
139    if (A->getOption().getKind() == llvm::opt::Option::InputClass)
140      Inputs.push_back(A->getValue());
141  }
142
143  Opts.mIncludePaths = Args.getAllArgValues(OPT_I);
144
145  Opts.mBitcodeOutputDir = Args.getLastArgValue(OPT_o);
146
147  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_M_Group)) {
148    switch (A->getOption().getID()) {
149    case OPT_M: {
150      Opts.mEmitDependency = true;
151      Opts.mOutputType = Slang::OT_Dependency;
152      break;
153    }
154    case OPT_MD: {
155      Opts.mEmitDependency = true;
156      Opts.mOutputType = Slang::OT_Bitcode;
157      break;
158    }
159    case OPT_MP: {
160      Opts.mEmitDependency = true;
161      Opts.mOutputType = Slang::OT_Bitcode;
162      Opts.mEmitPhonyDependency = true;
163      break;
164    }
165    default: { slangAssert(false && "Invalid option in M group!"); }
166    }
167  }
168
169  if (const llvm::opt::Arg *A = Args.getLastArg(OPT_Output_Type_Group)) {
170    switch (A->getOption().getID()) {
171    case OPT_emit_asm: {
172      Opts.mOutputType = Slang::OT_Assembly;
173      break;
174    }
175    case OPT_emit_llvm: {
176      Opts.mOutputType = Slang::OT_LLVMAssembly;
177      break;
178    }
179    case OPT_emit_bc: {
180      Opts.mOutputType = Slang::OT_Bitcode;
181      break;
182    }
183    case OPT_emit_nothing: {
184      Opts.mOutputType = Slang::OT_Nothing;
185      break;
186    }
187    default: { slangAssert(false && "Invalid option in output type group!"); }
188    }
189  }
190
191  if (Opts.mEmitDependency && ((Opts.mOutputType != Slang::OT_Bitcode) &&
192                               (Opts.mOutputType != Slang::OT_Dependency)))
193    DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
194        << Args.getLastArg(OPT_M_Group)->getAsString(Args)
195        << Args.getLastArg(OPT_Output_Type_Group)->getAsString(Args);
196
197  Opts.mAllowRSPrefix = Args.hasArg(OPT_allow_rs_prefix);
198
199  Opts.mJavaReflectionPathBase =
200      Args.getLastArgValue(OPT_java_reflection_path_base);
201  Opts.mJavaReflectionPackageName =
202      Args.getLastArgValue(OPT_java_reflection_package_name);
203
204  Opts.mRSPackageName = Args.getLastArgValue(OPT_rs_package_name);
205
206  llvm::StringRef BitcodeStorageValue =
207      Args.getLastArgValue(OPT_bitcode_storage);
208  if (BitcodeStorageValue == "ar")
209    Opts.mBitcodeStorage = BCST_APK_RESOURCE;
210  else if (BitcodeStorageValue == "jc")
211    Opts.mBitcodeStorage = BCST_JAVA_CODE;
212  else if (!BitcodeStorageValue.empty())
213    DiagEngine.Report(clang::diag::err_drv_invalid_value)
214        << OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue;
215
216  llvm::opt::Arg *lastBitwidthArg = Args.getLastArg(OPT_m32, OPT_m64);
217  if (Args.hasArg(OPT_reflect_cpp)) {
218    Opts.mBitcodeStorage = BCST_CPP_CODE;
219    // mJavaReflectionPathBase can be set for C++ reflected builds.
220    // Set it to the standard mBitcodeOutputDir (via -o) by default.
221    if (Opts.mJavaReflectionPathBase.empty()) {
222      Opts.mJavaReflectionPathBase = Opts.mBitcodeOutputDir;
223    }
224
225    // Check for bitwidth arguments.
226    if (lastBitwidthArg) {
227      if (lastBitwidthArg->getOption().matches(OPT_m32)) {
228        Opts.mBitWidth = 32;
229      } else {
230        Opts.mBitWidth = 64;
231      }
232    }
233  } else if (lastBitwidthArg) {
234      // -m32/-m64 are forbidden for non-C++ reflection paths for non-eng builds
235      // (they would make it too easy for a developer to accidentally create and
236      // release an APK that has 32-bit or 64-bit bitcode but not both).
237#ifdef __ENABLE_INTERNAL_OPTIONS
238      if (lastBitwidthArg->getOption().matches(OPT_m32)) {
239        Opts.mBitWidth = 32;
240      } else {
241        Opts.mBitWidth = 64;
242      }
243      Opts.mEmit3264 = false;
244#else
245      DiagEngine.Report(
246          DiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
247                                     "cannot use -m32/-m64 without specifying "
248                                     "C++ reflection (-reflect-c++)"));
249#endif
250  }
251
252  Opts.mDependencyOutputDir =
253      Args.getLastArgValue(OPT_output_dep_dir, Opts.mBitcodeOutputDir);
254  Opts.mAdditionalDepTargets = Args.getAllArgValues(OPT_additional_dep_target);
255
256  Opts.mShowHelp = Args.hasArg(OPT_help);
257  Opts.mShowVersion = Args.hasArg(OPT_version);
258  Opts.mDebugEmission = Args.hasArg(OPT_emit_g);
259  Opts.mVerbose = Args.hasArg(OPT_verbose);
260  Opts.mASTPrint = Args.hasArg(OPT_ast_print);
261
262  // Delegate options
263
264  std::vector<std::string> DelegatedStrings;
265  for (int Opt : std::vector<unsigned>{OPT_debug, OPT_print_after_all, OPT_print_before_all}) {
266    if (Args.hasArg(Opt)) {
267      // TODO: Don't assume that the option begins with "-"; determine this programmatically instead.
268      DelegatedStrings.push_back(std::string("-") + std::string(OptParser->getOptionName(Opt)));
269      slangAssert(OptParser->getOptionKind(Opt) == llvm::opt::Option::FlagClass);
270    }
271  }
272  if (DelegatedStrings.size()) {
273    std::vector<const char *> DelegatedCStrs;
274    DelegatedCStrs.push_back(*ArgVector.data()); // program name
275    std::for_each(DelegatedStrings.cbegin(), DelegatedStrings.cend(),
276                  [&DelegatedCStrs](const std::string &String) { DelegatedCStrs.push_back(String.c_str()); });
277    llvm::cl::ParseCommandLineOptions(DelegatedCStrs.size(), DelegatedCStrs.data());
278  }
279
280  // If we are emitting both 32-bit and 64-bit bitcode, we must embed it.
281
282  size_t OptLevel =
283      clang::getLastArgIntValue(Args, OPT_optimization_level, 3, DiagEngine);
284
285  Opts.mOptimizationLevel =
286      OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive;
287
288  Opts.mTargetAPI =
289      clang::getLastArgIntValue(Args, OPT_target_api, RS_VERSION, DiagEngine);
290
291  if (Opts.mTargetAPI == 0) {
292    Opts.mTargetAPI = UINT_MAX;
293  }
294
295  if ((Opts.mTargetAPI < 21) || (Opts.mBitcodeStorage == BCST_CPP_CODE))
296    Opts.mEmit3264 = false;
297  if (Opts.mEmit3264)
298    Opts.mBitcodeStorage = BCST_JAVA_CODE;
299
300  if (DiagEngine.hasErrorOccurred()) {
301    llvm::errs() << DiagsBuffer.str();
302    return false;
303  }
304
305  return true;
306}
307}
308