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 
37 enum {
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 
56 static 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 
70 namespace {
71 
72 class RSCCOptTable : public llvm::opt::OptTable {
73  public:
RSCCOptTable()74   RSCCOptTable()
75       : OptTable(RSCCInfoTable,
76                  sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {}
77 };
78 }
79 
80 namespace slang {
81 
createRSCCOptTable()82 llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
83 
84 // This function is similar to
85 // clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
ParseArguments(const llvm::ArrayRef<const char * > & ArgsIn,llvm::SmallVectorImpl<const char * > & Inputs,RSCCOptions & Opts,clang::DiagnosticOptions & DiagOpts,llvm::cl::StringSaver & StringSaver)86 bool 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