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   // Always turn deprecation warning into a warning even if -Werror is specified.
137   // This is because we will always emit RenderScript deprecation warning, and turning
138   // it into an error will make the compilation always fail.
139   DiagOpts.Warnings.push_back("no-error=deprecated-declarations");
140 
141   for (llvm::opt::ArgList::const_iterator it = Args.begin(), ie = Args.end();
142        it != ie; ++it) {
143     const llvm::opt::Arg *A = *it;
144     if (A->getOption().getKind() == llvm::opt::Option::InputClass)
145       Inputs.push_back(A->getValue());
146   }
147 
148   Opts.mIncludePaths = Args.getAllArgValues(OPT_I);
149 
150   Opts.mBitcodeOutputDir = Args.getLastArgValue(OPT_o);
151 
152   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_M_Group)) {
153     switch (A->getOption().getID()) {
154     case OPT_M: {
155       Opts.mEmitDependency = true;
156       Opts.mOutputType = Slang::OT_Dependency;
157       break;
158     }
159     case OPT_MD: {
160       Opts.mEmitDependency = true;
161       Opts.mOutputType = Slang::OT_Bitcode;
162       break;
163     }
164     case OPT_MP: {
165       Opts.mEmitDependency = true;
166       Opts.mOutputType = Slang::OT_Bitcode;
167       Opts.mEmitPhonyDependency = true;
168       break;
169     }
170     default: { slangAssert(false && "Invalid option in M group!"); }
171     }
172   }
173 
174   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_Output_Type_Group)) {
175     switch (A->getOption().getID()) {
176     case OPT_emit_asm: {
177       Opts.mOutputType = Slang::OT_Assembly;
178       break;
179     }
180     case OPT_emit_llvm: {
181       Opts.mOutputType = Slang::OT_LLVMAssembly;
182       break;
183     }
184     case OPT_emit_bc: {
185       Opts.mOutputType = Slang::OT_Bitcode;
186       break;
187     }
188     case OPT_emit_nothing: {
189       Opts.mOutputType = Slang::OT_Nothing;
190       break;
191     }
192     default: { slangAssert(false && "Invalid option in output type group!"); }
193     }
194   }
195 
196   if (Opts.mEmitDependency && ((Opts.mOutputType != Slang::OT_Bitcode) &&
197                                (Opts.mOutputType != Slang::OT_Dependency)))
198     DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
199         << Args.getLastArg(OPT_M_Group)->getAsString(Args)
200         << Args.getLastArg(OPT_Output_Type_Group)->getAsString(Args);
201 
202   Opts.mAllowRSPrefix = Args.hasArg(OPT_allow_rs_prefix);
203 
204   Opts.mJavaReflectionPathBase =
205       Args.getLastArgValue(OPT_java_reflection_path_base);
206   Opts.mJavaReflectionPackageName =
207       Args.getLastArgValue(OPT_java_reflection_package_name);
208 
209   Opts.mRSPackageName = Args.getLastArgValue(OPT_rs_package_name);
210 
211   llvm::StringRef BitcodeStorageValue =
212       Args.getLastArgValue(OPT_bitcode_storage);
213   if (BitcodeStorageValue == "ar")
214     Opts.mBitcodeStorage = BCST_APK_RESOURCE;
215   else if (BitcodeStorageValue == "jc")
216     Opts.mBitcodeStorage = BCST_JAVA_CODE;
217   else if (!BitcodeStorageValue.empty())
218     DiagEngine.Report(clang::diag::err_drv_invalid_value)
219         << OptParser->getOptionName(OPT_bitcode_storage) << BitcodeStorageValue;
220 
221   llvm::opt::Arg *lastBitwidthArg = Args.getLastArg(OPT_m32, OPT_m64);
222   if (Args.hasArg(OPT_reflect_cpp)) {
223     Opts.mBitcodeStorage = BCST_CPP_CODE;
224     // mJavaReflectionPathBase can be set for C++ reflected builds.
225     // Set it to the standard mBitcodeOutputDir (via -o) by default.
226     if (Opts.mJavaReflectionPathBase.empty()) {
227       Opts.mJavaReflectionPathBase = Opts.mBitcodeOutputDir;
228     }
229 
230     // Check for bitwidth arguments.
231     if (lastBitwidthArg) {
232       if (lastBitwidthArg->getOption().matches(OPT_m32)) {
233         Opts.mBitWidth = 32;
234       } else {
235         Opts.mBitWidth = 64;
236       }
237     }
238   } else if (lastBitwidthArg) {
239       // -m32/-m64 are forbidden for non-C++ reflection paths for non-eng builds
240       // (they would make it too easy for a developer to accidentally create and
241       // release an APK that has 32-bit or 64-bit bitcode but not both).
242 #ifdef __ENABLE_INTERNAL_OPTIONS
243       if (lastBitwidthArg->getOption().matches(OPT_m32)) {
244         Opts.mBitWidth = 32;
245       } else {
246         Opts.mBitWidth = 64;
247       }
248       Opts.mEmit3264 = false;
249 #else
250       DiagEngine.Report(
251           DiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
252                                      "cannot use -m32/-m64 without specifying "
253                                      "C++ reflection (-reflect-c++)"));
254 #endif
255   }
256 
257   Opts.mDependencyOutputDir =
258       Args.getLastArgValue(OPT_output_dep_dir, Opts.mBitcodeOutputDir);
259   Opts.mAdditionalDepTargets = Args.getAllArgValues(OPT_additional_dep_target);
260 
261   Opts.mShowHelp = Args.hasArg(OPT_help);
262   Opts.mShowVersion = Args.hasArg(OPT_version);
263   Opts.mDebugEmission = Args.hasArg(OPT_emit_g);
264   Opts.mVerbose = Args.hasArg(OPT_verbose);
265   Opts.mASTPrint = Args.hasArg(OPT_ast_print);
266 
267   // Delegate options
268 
269   std::vector<std::string> DelegatedStrings;
270   for (int Opt : std::vector<unsigned>{OPT_debug, OPT_print_after_all, OPT_print_before_all}) {
271     if (Args.hasArg(Opt)) {
272       // TODO: Don't assume that the option begins with "-"; determine this programmatically instead.
273       DelegatedStrings.push_back(std::string("-") + std::string(OptParser->getOptionName(Opt)));
274       slangAssert(OptParser->getOptionKind(Opt) == llvm::opt::Option::FlagClass);
275     }
276   }
277   if (DelegatedStrings.size()) {
278     std::vector<const char *> DelegatedCStrs;
279     DelegatedCStrs.push_back(*ArgVector.data()); // program name
280     std::for_each(DelegatedStrings.cbegin(), DelegatedStrings.cend(),
281                   [&DelegatedCStrs](const std::string &String) { DelegatedCStrs.push_back(String.c_str()); });
282     llvm::cl::ParseCommandLineOptions(DelegatedCStrs.size(), DelegatedCStrs.data());
283   }
284 
285   // If we are emitting both 32-bit and 64-bit bitcode, we must embed it.
286 
287   size_t OptLevel =
288       clang::getLastArgIntValue(Args, OPT_optimization_level, 3, DiagEngine);
289 
290   Opts.mOptimizationLevel =
291       OptLevel == 0 ? llvm::CodeGenOpt::None : llvm::CodeGenOpt::Aggressive;
292 
293   Opts.mTargetAPI =
294       clang::getLastArgIntValue(Args, OPT_target_api, RS_VERSION, DiagEngine);
295 
296   if (Opts.mTargetAPI == 0) {
297     Opts.mTargetAPI = UINT_MAX;
298   } else if (Opts.mTargetAPI > SLANG_MAXIMUM_TARGET_API &&
299              Opts.mTargetAPI <= SLANG_MAXIMUM_CMDLINE_TARGET_API) {
300     // Bug: http://b/35767071
301     // No new APIs after N, convert to N.
302     Opts.mTargetAPI = SLANG_MAXIMUM_TARGET_API;
303   }
304 
305   if ((Opts.mTargetAPI < 21) || (Opts.mBitcodeStorage == BCST_CPP_CODE))
306     Opts.mEmit3264 = false;
307   if (Opts.mEmit3264)
308     Opts.mBitcodeStorage = BCST_JAVA_CODE;
309 
310   if (DiagEngine.hasErrorOccurred()) {
311     llvm::errs() << DiagsBuffer.str();
312     return false;
313   }
314 
315   return true;
316 }
317 }
318