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