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(llvm::makeArrayRef(RSCCInfoTable)) {}
76 };
77 }
78
79 namespace slang {
80
createRSCCOptTable()81 llvm::opt::OptTable *createRSCCOptTable() { return new RSCCOptTable(); }
82
83 // This function is similar to
84 // clang/lib/Frontend/CompilerInvocation::CreateFromArgs.
ParseArguments(const llvm::ArrayRef<const char * > & ArgsIn,llvm::SmallVectorImpl<const char * > & Inputs,RSCCOptions & Opts,clang::DiagnosticOptions & DiagOpts,llvm::StringSaver & StringSaver)85 bool 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