1 /*
2 * Copyright 2010-2012, 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 <cstdlib>
18 #include <list>
19 #include <set>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "clang/Driver/DriverDiagnostic.h"
25 #include "clang/Driver/Options.h"
26
27 #include "clang/Basic/DiagnosticOptions.h"
28 #include "clang/Frontend/TextDiagnosticPrinter.h"
29 #include "clang/Frontend/Utils.h"
30
31 #include "llvm/ADT/SmallVector.h"
32 #include "llvm/ADT/IntrusiveRefCntPtr.h"
33 #include "llvm/ADT/OwningPtr.h"
34
35 #include "llvm/Option/Arg.h"
36 #include "llvm/Option/ArgList.h"
37 #include "llvm/Option/Option.h"
38 #include "llvm/Option/OptTable.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/ManagedStatic.h"
41 #include "llvm/Support/MemoryBuffer.h"
42 #include "llvm/Support/Path.h"
43 #include "llvm/Support/raw_ostream.h"
44 #include "llvm/Support/system_error.h"
45 #include "llvm/Target/TargetMachine.h"
46
47 #include "slang.h"
48 #include "slang_assert.h"
49 #include "slang_diagnostic_buffer.h"
50 #include "slang_rs.h"
51 #include "slang_rs_reflect_utils.h"
52
53 using clang::driver::options::DriverOption;
54 using llvm::opt::arg_iterator;
55 using llvm::opt::Arg;
56 using llvm::opt::ArgList;
57 using llvm::opt::InputArgList;
58 using llvm::opt::Option;
59 using llvm::opt::OptTable;
60
61 // SaveStringInSet, ExpandArgsFromBuf and ExpandArgv are all copied from
62 // $(CLANG_ROOT)/tools/driver/driver.cpp for processing argc/argv passed in
63 // main().
SaveStringInSet(std::set<std::string> & SavedStrings,llvm::StringRef S)64 static inline const char *SaveStringInSet(std::set<std::string> &SavedStrings,
65 llvm::StringRef S) {
66 return SavedStrings.insert(S).first->c_str();
67 }
68 static void ExpandArgsFromBuf(const char *Arg,
69 llvm::SmallVectorImpl<const char*> &ArgVector,
70 std::set<std::string> &SavedStrings);
71 static void ExpandArgv(int argc, const char **argv,
72 llvm::SmallVectorImpl<const char*> &ArgVector,
73 std::set<std::string> &SavedStrings);
74
75 enum {
76 OPT_INVALID = 0, // This is not an option ID.
77 #define PREFIX(NAME, VALUE)
78 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
79 HELPTEXT, METAVAR) OPT_##ID,
80 #include "RSCCOptions.inc"
81 LastOption
82 #undef OPTION
83 #undef PREFIX
84 };
85
86 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
87 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
88 HELPTEXT, METAVAR)
89 #include "RSCCOptions.inc"
90 #undef OPTION
91 #undef PREFIX
92
93 static const OptTable::Info RSCCInfoTable[] = {
94 #define PREFIX(NAME, VALUE)
95 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
96 HELPTEXT, METAVAR) \
97 { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, PARAM, \
98 FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
99 #include "RSCCOptions.inc"
100 #undef OPTION
101 #undef PREFIX
102 };
103
104 class RSCCOptTable : public OptTable {
105 public:
RSCCOptTable()106 RSCCOptTable()
107 : OptTable(RSCCInfoTable,
108 sizeof(RSCCInfoTable) / sizeof(RSCCInfoTable[0])) {
109 }
110 };
111
createRSCCOptTable()112 OptTable *createRSCCOptTable() {
113 return new RSCCOptTable();
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////
117
118 class RSCCOptions {
119 public:
120 // The include search paths
121 std::vector<std::string> mIncludePaths;
122
123 // The output directory, if any.
124 std::string mOutputDir;
125
126 // The output type
127 slang::Slang::OutputType mOutputType;
128
129 unsigned mAllowRSPrefix : 1;
130
131 // The name of the target triple to compile for.
132 std::string mTriple;
133
134 // The name of the target CPU to generate code for.
135 std::string mCPU;
136
137 // The list of target specific features to enable or disable -- this should
138 // be a list of strings starting with by '+' or '-'.
139 std::vector<std::string> mFeatures;
140
141 std::string mJavaReflectionPathBase;
142
143 std::string mJavaReflectionPackageName;
144
145 std::string mRSPackageName;
146
147 slang::BitCodeStorageType mBitcodeStorage;
148
149 unsigned mOutputDep : 1;
150
151 std::string mOutputDepDir;
152
153 std::vector<std::string> mAdditionalDepTargets;
154
155 unsigned mShowHelp : 1; // Show the -help text.
156 unsigned mShowVersion : 1; // Show the -version text.
157
158 unsigned int mTargetAPI;
159
160 // Enable emission of debugging symbols
161 unsigned mDebugEmission : 1;
162
163 // The optimization level used in CodeGen, and encoded in emitted bitcode
164 llvm::CodeGenOpt::Level mOptimizationLevel;
165
RSCCOptions()166 RSCCOptions() {
167 mOutputType = slang::Slang::OT_Bitcode;
168 // Triple/CPU/Features must be hard-coded to our chosen portable ABI.
169 mTriple = "armv7-none-linux-gnueabi";
170 mCPU = "";
171 slangAssert(mFeatures.empty());
172 mFeatures.push_back("+long64");
173 mBitcodeStorage = slang::BCST_APK_RESOURCE;
174 mOutputDep = 0;
175 mShowHelp = 0;
176 mShowVersion = 0;
177 mTargetAPI = RS_VERSION;
178 mDebugEmission = 0;
179 mOptimizationLevel = llvm::CodeGenOpt::Aggressive;
180 }
181 };
182
183 // ParseArguments -
ParseArguments(llvm::SmallVectorImpl<const char * > & ArgVector,llvm::SmallVectorImpl<const char * > & Inputs,RSCCOptions & Opts,clang::DiagnosticsEngine & DiagEngine)184 static void ParseArguments(llvm::SmallVectorImpl<const char*> &ArgVector,
185 llvm::SmallVectorImpl<const char*> &Inputs,
186 RSCCOptions &Opts,
187 clang::DiagnosticsEngine &DiagEngine) {
188 if (ArgVector.size() > 1) {
189 const char **ArgBegin = ArgVector.data() + 1;
190 const char **ArgEnd = ArgVector.data() + ArgVector.size();
191 unsigned MissingArgIndex, MissingArgCount;
192 llvm::OwningPtr<OptTable> OptParser(createRSCCOptTable());
193 llvm::OwningPtr<InputArgList> Args(
194 OptParser->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount));
195
196 // Check for missing argument error.
197 if (MissingArgCount)
198 DiagEngine.Report(clang::diag::err_drv_missing_argument)
199 << Args->getArgString(MissingArgIndex) << MissingArgCount;
200
201 clang::DiagnosticOptions DiagOpts;
202 DiagOpts.IgnoreWarnings = Args->hasArg(OPT_w);
203 DiagOpts.Warnings = Args->getAllArgValues(OPT_W);
204 clang::ProcessWarningOptions(DiagEngine, DiagOpts);
205
206 // Issue errors on unknown arguments.
207 for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN),
208 ie = Args->filtered_end(); it != ie; ++it)
209 DiagEngine.Report(clang::diag::err_drv_unknown_argument)
210 << (*it)->getAsString(*Args);
211
212 for (ArgList::const_iterator it = Args->begin(), ie = Args->end();
213 it != ie; ++it) {
214 const Arg *A = *it;
215 if (A->getOption().getKind() == Option::InputClass)
216 Inputs.push_back(A->getValue());
217 }
218
219 Opts.mIncludePaths = Args->getAllArgValues(OPT_I);
220
221 Opts.mOutputDir = Args->getLastArgValue(OPT_o);
222
223 if (const Arg *A = Args->getLastArg(OPT_M_Group)) {
224 switch (A->getOption().getID()) {
225 case OPT_M: {
226 Opts.mOutputDep = 1;
227 Opts.mOutputType = slang::Slang::OT_Dependency;
228 break;
229 }
230 case OPT_MD: {
231 Opts.mOutputDep = 1;
232 Opts.mOutputType = slang::Slang::OT_Bitcode;
233 break;
234 }
235 default: {
236 slangAssert(false && "Invalid option in M group!");
237 }
238 }
239 }
240
241 if (const Arg *A = Args->getLastArg(OPT_Output_Type_Group)) {
242 switch (A->getOption().getID()) {
243 case OPT_emit_asm: {
244 Opts.mOutputType = slang::Slang::OT_Assembly;
245 break;
246 }
247 case OPT_emit_llvm: {
248 Opts.mOutputType = slang::Slang::OT_LLVMAssembly;
249 break;
250 }
251 case OPT_emit_bc: {
252 Opts.mOutputType = slang::Slang::OT_Bitcode;
253 break;
254 }
255 case OPT_emit_nothing: {
256 Opts.mOutputType = slang::Slang::OT_Nothing;
257 break;
258 }
259 default: {
260 slangAssert(false && "Invalid option in output type group!");
261 }
262 }
263 }
264
265 if (Opts.mOutputDep &&
266 ((Opts.mOutputType != slang::Slang::OT_Bitcode) &&
267 (Opts.mOutputType != slang::Slang::OT_Dependency)))
268 DiagEngine.Report(clang::diag::err_drv_argument_not_allowed_with)
269 << Args->getLastArg(OPT_M_Group)->getAsString(*Args)
270 << Args->getLastArg(OPT_Output_Type_Group)->getAsString(*Args);
271
272 Opts.mAllowRSPrefix = Args->hasArg(OPT_allow_rs_prefix);
273
274 Opts.mJavaReflectionPathBase =
275 Args->getLastArgValue(OPT_java_reflection_path_base);
276 Opts.mJavaReflectionPackageName =
277 Args->getLastArgValue(OPT_java_reflection_package_name);
278
279 Opts.mRSPackageName = Args->getLastArgValue(OPT_rs_package_name);
280
281 llvm::StringRef BitcodeStorageValue =
282 Args->getLastArgValue(OPT_bitcode_storage);
283 if (BitcodeStorageValue == "ar")
284 Opts.mBitcodeStorage = slang::BCST_APK_RESOURCE;
285 else if (BitcodeStorageValue == "jc")
286 Opts.mBitcodeStorage = slang::BCST_JAVA_CODE;
287 else if (!BitcodeStorageValue.empty())
288 DiagEngine.Report(clang::diag::err_drv_invalid_value)
289 << OptParser->getOptionName(OPT_bitcode_storage)
290 << BitcodeStorageValue;
291
292 if (Args->hasArg(OPT_reflect_cpp)) {
293 Opts.mBitcodeStorage = slang::BCST_CPP_CODE;
294 // mJavaReflectionPathBase can be set for C++ reflected builds.
295 // Set it to the standard mOutputDir (via -o) by default.
296 if (Opts.mJavaReflectionPathBase.empty()) {
297 Opts.mJavaReflectionPathBase = Opts.mOutputDir;
298 }
299 }
300
301 Opts.mOutputDepDir =
302 Args->getLastArgValue(OPT_output_dep_dir, Opts.mOutputDir);
303 Opts.mAdditionalDepTargets =
304 Args->getAllArgValues(OPT_additional_dep_target);
305
306 Opts.mShowHelp = Args->hasArg(OPT_help);
307 Opts.mShowVersion = Args->hasArg(OPT_version);
308 Opts.mDebugEmission = Args->hasArg(OPT_emit_g);
309
310 size_t OptLevel = clang::getLastArgIntValue(*Args,
311 OPT_optimization_level,
312 3,
313 DiagEngine);
314
315 Opts.mOptimizationLevel = OptLevel == 0 ? llvm::CodeGenOpt::None
316 : llvm::CodeGenOpt::Aggressive;
317
318 Opts.mTargetAPI = clang::getLastArgIntValue(*Args,
319 OPT_target_api,
320 RS_VERSION,
321 DiagEngine);
322 }
323
324 return;
325 }
326
DetermineOutputFile(const std::string & OutputDir,const char * InputFile,slang::Slang::OutputType OutputType,std::set<std::string> & SavedStrings)327 static const char *DetermineOutputFile(const std::string &OutputDir,
328 const char *InputFile,
329 slang::Slang::OutputType OutputType,
330 std::set<std::string> &SavedStrings) {
331 if (OutputType == slang::Slang::OT_Nothing)
332 return "/dev/null";
333
334 std::string OutputFile(OutputDir);
335
336 // Append '/' to Opts.mOutputDir if not presents
337 if (!OutputFile.empty() &&
338 (OutputFile[OutputFile.size() - 1]) != OS_PATH_SEPARATOR)
339 OutputFile.append(1, OS_PATH_SEPARATOR);
340
341 if (OutputType == slang::Slang::OT_Dependency) {
342 // The build system wants the .d file name stem to be exactly the same as
343 // the source .rs file, instead of the .bc file.
344 OutputFile.append(slang::RSSlangReflectUtils::GetFileNameStem(InputFile));
345 } else {
346 OutputFile.append(
347 slang::RSSlangReflectUtils::BCFileNameFromRSFileName(InputFile));
348 }
349
350 switch (OutputType) {
351 case slang::Slang::OT_Dependency: {
352 OutputFile.append(".d");
353 break;
354 }
355 case slang::Slang::OT_Assembly: {
356 OutputFile.append(".S");
357 break;
358 }
359 case slang::Slang::OT_LLVMAssembly: {
360 OutputFile.append(".ll");
361 break;
362 }
363 case slang::Slang::OT_Object: {
364 OutputFile.append(".o");
365 break;
366 }
367 case slang::Slang::OT_Bitcode: {
368 OutputFile.append(".bc");
369 break;
370 }
371 case slang::Slang::OT_Nothing:
372 default: {
373 slangAssert(false && "Invalid output type!");
374 }
375 }
376
377 return SaveStringInSet(SavedStrings, OutputFile);
378 }
379
380 #define str(s) #s
381 #define wrap_str(s) str(s)
llvm_rs_cc_VersionPrinter()382 static void llvm_rs_cc_VersionPrinter() {
383 llvm::raw_ostream &OS = llvm::outs();
384 OS << "llvm-rs-cc: Renderscript compiler\n"
385 << " (http://developer.android.com/guide/topics/renderscript)\n"
386 << " based on LLVM (http://llvm.org):\n";
387 OS << " Built " << __DATE__ << " (" << __TIME__ ").\n";
388 OS << " Target APIs: " << SLANG_MINIMUM_TARGET_API << " - "
389 << SLANG_MAXIMUM_TARGET_API;
390 OS << "\n Build type: " << wrap_str(TARGET_BUILD_VARIANT);
391 #ifndef __DISABLE_ASSERTS
392 OS << " with assertions";
393 #endif
394 OS << ".\n";
395 return;
396 }
397 #undef wrap_str
398 #undef str
399
main(int argc,const char ** argv)400 int main(int argc, const char **argv) {
401 std::set<std::string> SavedStrings;
402 llvm::SmallVector<const char*, 256> ArgVector;
403 RSCCOptions Opts;
404 llvm::SmallVector<const char*, 16> Inputs;
405 std::string Argv0;
406
407 atexit(llvm::llvm_shutdown);
408
409 ExpandArgv(argc, argv, ArgVector, SavedStrings);
410
411 // Argv0
412 Argv0 = llvm::sys::path::stem(ArgVector[0]);
413
414 // Setup diagnostic engine
415 slang::DiagnosticBuffer *DiagClient = new slang::DiagnosticBuffer();
416
417 llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(
418 new clang::DiagnosticIDs());
419
420 llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts(
421 new clang::DiagnosticOptions());
422 clang::DiagnosticsEngine DiagEngine(DiagIDs, &*DiagOpts, DiagClient, true);
423
424 slang::Slang::GlobalInitialization();
425
426 ParseArguments(ArgVector, Inputs, Opts, DiagEngine);
427
428 // Exits when there's any error occurred during parsing the arguments
429 if (DiagEngine.hasErrorOccurred()) {
430 llvm::errs() << DiagClient->str();
431 return 1;
432 }
433
434 if (Opts.mShowHelp) {
435 llvm::OwningPtr<OptTable> OptTbl(createRSCCOptTable());
436 OptTbl->PrintHelp(llvm::outs(), Argv0.c_str(),
437 "Renderscript source compiler");
438 return 0;
439 }
440
441 if (Opts.mShowVersion) {
442 llvm_rs_cc_VersionPrinter();
443 return 0;
444 }
445
446 // No input file
447 if (Inputs.empty()) {
448 DiagEngine.Report(clang::diag::err_drv_no_input_files);
449 llvm::errs() << DiagClient->str();
450 return 1;
451 }
452
453 // Prepare input data for RS compiler.
454 std::list<std::pair<const char*, const char*> > IOFiles;
455 std::list<std::pair<const char*, const char*> > DepFiles;
456
457 llvm::OwningPtr<slang::SlangRS> Compiler(new slang::SlangRS());
458
459 Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures, &DiagEngine,
460 DiagClient);
461
462 for (int i = 0, e = Inputs.size(); i != e; i++) {
463 const char *InputFile = Inputs[i];
464 const char *OutputFile =
465 DetermineOutputFile(Opts.mOutputDir, InputFile,
466 Opts.mOutputType, SavedStrings);
467
468 if (Opts.mOutputDep) {
469 const char *BCOutputFile, *DepOutputFile;
470
471 if (Opts.mOutputType == slang::Slang::OT_Bitcode)
472 BCOutputFile = OutputFile;
473 else
474 BCOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
475 InputFile,
476 slang::Slang::OT_Bitcode,
477 SavedStrings);
478
479 if (Opts.mOutputType == slang::Slang::OT_Dependency)
480 DepOutputFile = OutputFile;
481 else
482 DepOutputFile = DetermineOutputFile(Opts.mOutputDepDir,
483 InputFile,
484 slang::Slang::OT_Dependency,
485 SavedStrings);
486
487 DepFiles.push_back(std::make_pair(BCOutputFile, DepOutputFile));
488 }
489
490 IOFiles.push_back(std::make_pair(InputFile, OutputFile));
491 }
492
493 // Let's rock!
494 int CompileFailed = !Compiler->compile(IOFiles,
495 DepFiles,
496 Opts.mIncludePaths,
497 Opts.mAdditionalDepTargets,
498 Opts.mOutputType,
499 Opts.mBitcodeStorage,
500 Opts.mAllowRSPrefix,
501 Opts.mOutputDep,
502 Opts.mTargetAPI,
503 Opts.mDebugEmission,
504 Opts.mOptimizationLevel,
505 Opts.mJavaReflectionPathBase,
506 Opts.mJavaReflectionPackageName,
507 Opts.mRSPackageName);
508
509 Compiler->reset();
510
511 return CompileFailed;
512 }
513
514 ///////////////////////////////////////////////////////////////////////////////
515
516 // ExpandArgsFromBuf -
ExpandArgsFromBuf(const char * Arg,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)517 static void ExpandArgsFromBuf(const char *Arg,
518 llvm::SmallVectorImpl<const char*> &ArgVector,
519 std::set<std::string> &SavedStrings) {
520 const char *FName = Arg + 1;
521 llvm::OwningPtr<llvm::MemoryBuffer> MemBuf;
522 if (llvm::MemoryBuffer::getFile(FName, MemBuf)) {
523 // Unable to open the file
524 ArgVector.push_back(SaveStringInSet(SavedStrings, Arg));
525 return;
526 }
527
528 const char *Buf = MemBuf->getBufferStart();
529 char InQuote = ' ';
530 std::string CurArg;
531
532 for (const char *P = Buf; ; ++P) {
533 if (*P == '\0' || (isspace(*P) && InQuote == ' ')) {
534 if (!CurArg.empty()) {
535 if (CurArg[0] != '@') {
536 ArgVector.push_back(SaveStringInSet(SavedStrings, CurArg));
537 } else {
538 ExpandArgsFromBuf(CurArg.c_str(), ArgVector, SavedStrings);
539 }
540
541 CurArg = "";
542 }
543 if (*P == '\0')
544 break;
545 else
546 continue;
547 }
548
549 if (isspace(*P)) {
550 if (InQuote != ' ')
551 CurArg.push_back(*P);
552 continue;
553 }
554
555 if (*P == '"' || *P == '\'') {
556 if (InQuote == *P)
557 InQuote = ' ';
558 else if (InQuote == ' ')
559 InQuote = *P;
560 else
561 CurArg.push_back(*P);
562 continue;
563 }
564
565 if (*P == '\\') {
566 ++P;
567 if (*P != '\0')
568 CurArg.push_back(*P);
569 continue;
570 }
571 CurArg.push_back(*P);
572 }
573 }
574
575 // ExpandArgsFromBuf -
ExpandArgv(int argc,const char ** argv,llvm::SmallVectorImpl<const char * > & ArgVector,std::set<std::string> & SavedStrings)576 static void ExpandArgv(int argc, const char **argv,
577 llvm::SmallVectorImpl<const char*> &ArgVector,
578 std::set<std::string> &SavedStrings) {
579 for (int i = 0; i < argc; ++i) {
580 const char *Arg = argv[i];
581 if (Arg[0] != '@') {
582 ArgVector.push_back(SaveStringInSet(SavedStrings, std::string(Arg)));
583 continue;
584 }
585
586 ExpandArgsFromBuf(Arg, ArgVector, SavedStrings);
587 }
588 }
589