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