• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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