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