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