1 /*
2 * Copyright 2011 Sven Verdoolaege. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Sven Verdoolaege.
32 */
33
34 #include "isl_config.h"
35 #undef PACKAGE
36
37 #include <assert.h>
38 #include <iostream>
39 #include <stdlib.h>
40 #ifdef HAVE_ADT_OWNINGPTR_H
41 #include <llvm/ADT/OwningPtr.h>
42 #else
43 #include <memory>
44 #endif
45 #ifdef HAVE_LLVM_OPTION_ARG_H
46 #include <llvm/Option/Arg.h>
47 #endif
48 #include <llvm/Support/raw_ostream.h>
49 #include <llvm/Support/CommandLine.h>
50 #ifdef HAVE_TARGETPARSER_HOST_H
51 #include <llvm/TargetParser/Host.h>
52 #else
53 #include <llvm/Support/Host.h>
54 #endif
55 #include <llvm/Support/ManagedStatic.h>
56 #include <clang/AST/ASTContext.h>
57 #include <clang/AST/ASTConsumer.h>
58 #include <clang/Basic/Builtins.h>
59 #include <clang/Basic/FileSystemOptions.h>
60 #include <clang/Basic/FileManager.h>
61 #include <clang/Basic/TargetOptions.h>
62 #include <clang/Basic/TargetInfo.h>
63 #include <clang/Basic/Version.h>
64 #include <clang/Driver/Compilation.h>
65 #include <clang/Driver/Driver.h>
66 #include <clang/Driver/Tool.h>
67 #include <clang/Frontend/CompilerInstance.h>
68 #include <clang/Frontend/CompilerInvocation.h>
69 #ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
70 #include <clang/Basic/DiagnosticOptions.h>
71 #else
72 #include <clang/Frontend/DiagnosticOptions.h>
73 #endif
74 #include <clang/Frontend/TextDiagnosticPrinter.h>
75 #include <clang/Frontend/Utils.h>
76 #include <clang/Lex/HeaderSearch.h>
77 #ifdef HAVE_LEX_PREPROCESSOROPTIONS_H
78 #include <clang/Lex/PreprocessorOptions.h>
79 #else
80 #include <clang/Frontend/PreprocessorOptions.h>
81 #endif
82 #include <clang/Lex/Preprocessor.h>
83 #include <clang/Parse/ParseAST.h>
84 #include <clang/Sema/Sema.h>
85
86 #include "extract_interface.h"
87 #include "generator.h"
88 #include "python.h"
89 #include "plain_cpp.h"
90 #include "cpp_conversion.h"
91 #include "template_cpp.h"
92
93 using namespace std;
94 using namespace clang;
95 using namespace clang::driver;
96 #ifdef HAVE_LLVM_OPTION_ARG_H
97 using namespace llvm::opt;
98 #endif
99
100 #ifdef HAVE_ADT_OWNINGPTR_H
101 #define unique_ptr llvm::OwningPtr
102 #endif
103
104 static llvm::cl::opt<string> InputFilename(llvm::cl::Positional,
105 llvm::cl::Required, llvm::cl::desc("<input file>"));
106 static llvm::cl::list<string> Includes("I",
107 llvm::cl::desc("Header search path"),
108 llvm::cl::value_desc("path"), llvm::cl::Prefix);
109
110 static llvm::cl::opt<string> OutputLanguage(llvm::cl::Required,
111 llvm::cl::ValueRequired, "language",
112 llvm::cl::desc("Bindings to generate"),
113 llvm::cl::value_desc("name"));
114
115 static const char *ResourceDir =
116 CLANG_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
117
118 /* Does decl have an attribute of the following form?
119 *
120 * __attribute__((annotate("name")))
121 */
has_annotation(Decl * decl,const char * name)122 bool has_annotation(Decl *decl, const char *name)
123 {
124 if (!decl->hasAttrs())
125 return false;
126
127 AttrVec attrs = decl->getAttrs();
128 for (AttrVec::const_iterator i = attrs.begin() ; i != attrs.end(); ++i) {
129 const AnnotateAttr *ann = dyn_cast<AnnotateAttr>(*i);
130 if (!ann)
131 continue;
132 if (ann->getAnnotation().str() == name)
133 return true;
134 }
135
136 return false;
137 }
138
139 /* Is decl marked as exported?
140 */
is_exported(Decl * decl)141 static bool is_exported(Decl *decl)
142 {
143 return has_annotation(decl, "isl_export");
144 }
145
146 /* Collect all types and functions that are annotated "isl_export"
147 * in "exported_types" and "exported_function". Collect all function
148 * declarations in "functions".
149 *
150 * We currently only consider single declarations.
151 */
152 struct MyASTConsumer : public ASTConsumer {
153 set<RecordDecl *> exported_types;
154 set<FunctionDecl *> exported_functions;
155 set<FunctionDecl *> functions;
156
HandleTopLevelDeclMyASTConsumer157 virtual HandleTopLevelDeclReturn HandleTopLevelDecl(DeclGroupRef D) {
158 Decl *decl;
159
160 if (!D.isSingleDecl())
161 return HandleTopLevelDeclContinue;
162 decl = D.getSingleDecl();
163 if (isa<FunctionDecl>(decl))
164 functions.insert(cast<FunctionDecl>(decl));
165 if (!is_exported(decl))
166 return HandleTopLevelDeclContinue;
167 switch (decl->getKind()) {
168 case Decl::Record:
169 exported_types.insert(cast<RecordDecl>(decl));
170 break;
171 case Decl::Function:
172 exported_functions.insert(cast<FunctionDecl>(decl));
173 break;
174 default:
175 break;
176 }
177 return HandleTopLevelDeclContinue;
178 }
179 };
180
181 #ifdef USE_ARRAYREF
182
183 #ifdef HAVE_CXXISPRODUCTION
construct_driver(const char * binary,DiagnosticsEngine & Diags)184 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
185 {
186 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
187 "", false, false, Diags);
188 }
189 #elif defined(HAVE_ISPRODUCTION)
construct_driver(const char * binary,DiagnosticsEngine & Diags)190 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
191 {
192 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
193 "", false, Diags);
194 }
195 #elif defined(DRIVER_CTOR_TAKES_DEFAULTIMAGENAME)
construct_driver(const char * binary,DiagnosticsEngine & Diags)196 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
197 {
198 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
199 "", Diags);
200 }
201 #else
construct_driver(const char * binary,DiagnosticsEngine & Diags)202 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
203 {
204 return new Driver(binary, llvm::sys::getDefaultTargetTriple(), Diags);
205 }
206 #endif
207
208 namespace clang { namespace driver { class Job; } }
209
210 /* Clang changed its API from 3.5 to 3.6 and once more in 3.7.
211 * We fix this with a simple overloaded function here.
212 */
213 struct ClangAPI {
commandClangAPI214 static Job *command(Job *J) { return J; }
commandClangAPI215 static Job *command(Job &J) { return &J; }
commandClangAPI216 static Command *command(Command &C) { return &C; }
217 };
218
219 #ifdef CREATE_FROM_ARGS_TAKES_ARRAYREF
220
221 /* Call CompilerInvocation::CreateFromArgs with the right arguments.
222 * In this case, an ArrayRef<const char *>.
223 */
create_from_args(CompilerInvocation & invocation,const ArgStringList * args,DiagnosticsEngine & Diags)224 static void create_from_args(CompilerInvocation &invocation,
225 const ArgStringList *args, DiagnosticsEngine &Diags)
226 {
227 CompilerInvocation::CreateFromArgs(invocation, *args, Diags);
228 }
229
230 #else
231
232 /* Call CompilerInvocation::CreateFromArgs with the right arguments.
233 * In this case, two "const char *" pointers.
234 */
create_from_args(CompilerInvocation & invocation,const ArgStringList * args,DiagnosticsEngine & Diags)235 static void create_from_args(CompilerInvocation &invocation,
236 const ArgStringList *args, DiagnosticsEngine &Diags)
237 {
238 CompilerInvocation::CreateFromArgs(invocation, args->data() + 1,
239 args->data() + args->size(),
240 Diags);
241 }
242
243 #endif
244
245 #ifdef CLANG_SYSROOT
246 /* Set sysroot if required.
247 *
248 * If CLANG_SYSROOT is defined, then set it to this value.
249 */
set_sysroot(ArgStringList & args)250 static void set_sysroot(ArgStringList &args)
251 {
252 args.push_back("-isysroot");
253 args.push_back(CLANG_SYSROOT);
254 }
255 #else
256 /* Set sysroot if required.
257 *
258 * If CLANG_SYSROOT is not defined, then it does not need to be set.
259 */
set_sysroot(ArgStringList & args)260 static void set_sysroot(ArgStringList &args)
261 {
262 }
263 #endif
264
265 /* Create a CompilerInvocation object that stores the command line
266 * arguments constructed by the driver.
267 * The arguments are mainly useful for setting up the system include
268 * paths on newer clangs and on some platforms.
269 */
construct_invocation(const char * filename,DiagnosticsEngine & Diags)270 static CompilerInvocation *construct_invocation(const char *filename,
271 DiagnosticsEngine &Diags)
272 {
273 const char *binary = CLANG_PREFIX"/bin/clang";
274 const unique_ptr<Driver> driver(construct_driver(binary, Diags));
275 std::vector<const char *> Argv;
276 Argv.push_back(binary);
277 Argv.push_back(filename);
278 const unique_ptr<Compilation> compilation(
279 driver->BuildCompilation(llvm::ArrayRef<const char *>(Argv)));
280 JobList &Jobs = compilation->getJobs();
281
282 Command *cmd = cast<Command>(ClangAPI::command(*Jobs.begin()));
283 if (strcmp(cmd->getCreator().getName(), "clang"))
284 return NULL;
285
286 ArgStringList args = cmd->getArguments();
287 set_sysroot(args);
288
289 CompilerInvocation *invocation = new CompilerInvocation;
290 create_from_args(*invocation, &args, Diags);
291 return invocation;
292 }
293
294 #else
295
construct_invocation(const char * filename,DiagnosticsEngine & Diags)296 static CompilerInvocation *construct_invocation(const char *filename,
297 DiagnosticsEngine &Diags)
298 {
299 return NULL;
300 }
301
302 #endif
303
304 #ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
305
construct_printer(void)306 static TextDiagnosticPrinter *construct_printer(void)
307 {
308 return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
309 }
310
311 #else
312
construct_printer(void)313 static TextDiagnosticPrinter *construct_printer(void)
314 {
315 DiagnosticOptions DO;
316 return new TextDiagnosticPrinter(llvm::errs(), DO);
317 }
318
319 #endif
320
321 #ifdef CREATETARGETINFO_TAKES_SHARED_PTR
322
create_target_info(CompilerInstance * Clang,DiagnosticsEngine & Diags)323 static TargetInfo *create_target_info(CompilerInstance *Clang,
324 DiagnosticsEngine &Diags)
325 {
326 shared_ptr<TargetOptions> TO = Clang->getInvocation().TargetOpts;
327 TO->Triple = llvm::sys::getDefaultTargetTriple();
328 return TargetInfo::CreateTargetInfo(Diags, TO);
329 }
330
331 #elif defined(CREATETARGETINFO_TAKES_POINTER)
332
create_target_info(CompilerInstance * Clang,DiagnosticsEngine & Diags)333 static TargetInfo *create_target_info(CompilerInstance *Clang,
334 DiagnosticsEngine &Diags)
335 {
336 TargetOptions &TO = Clang->getTargetOpts();
337 TO.Triple = llvm::sys::getDefaultTargetTriple();
338 return TargetInfo::CreateTargetInfo(Diags, &TO);
339 }
340
341 #else
342
create_target_info(CompilerInstance * Clang,DiagnosticsEngine & Diags)343 static TargetInfo *create_target_info(CompilerInstance *Clang,
344 DiagnosticsEngine &Diags)
345 {
346 TargetOptions &TO = Clang->getTargetOpts();
347 TO.Triple = llvm::sys::getDefaultTargetTriple();
348 return TargetInfo::CreateTargetInfo(Diags, TO);
349 }
350
351 #endif
352
353 #ifdef CREATEDIAGNOSTICS_TAKES_ARG
354
create_diagnostics(CompilerInstance * Clang)355 static void create_diagnostics(CompilerInstance *Clang)
356 {
357 Clang->createDiagnostics(0, NULL, construct_printer());
358 }
359
360 #else
361
create_diagnostics(CompilerInstance * Clang)362 static void create_diagnostics(CompilerInstance *Clang)
363 {
364 Clang->createDiagnostics(construct_printer());
365 }
366
367 #endif
368
369 #ifdef CREATEPREPROCESSOR_TAKES_TUKIND
370
create_preprocessor(CompilerInstance * Clang)371 static void create_preprocessor(CompilerInstance *Clang)
372 {
373 Clang->createPreprocessor(TU_Complete);
374 }
375
376 #else
377
create_preprocessor(CompilerInstance * Clang)378 static void create_preprocessor(CompilerInstance *Clang)
379 {
380 Clang->createPreprocessor();
381 }
382
383 #endif
384
385 #ifdef ADDPATH_TAKES_4_ARGUMENTS
386
387 /* Add "Path" to the header search options.
388 *
389 * Do not take into account sysroot, i.e., set ignoreSysRoot to true.
390 */
add_path(HeaderSearchOptions & HSO,string Path)391 void add_path(HeaderSearchOptions &HSO, string Path)
392 {
393 HSO.AddPath(Path, frontend::Angled, false, true);
394 }
395
396 #else
397
398 /* Add "Path" to the header search options.
399 *
400 * Do not take into account sysroot, i.e., set IsSysRootRelative to false.
401 */
add_path(HeaderSearchOptions & HSO,string Path)402 void add_path(HeaderSearchOptions &HSO, string Path)
403 {
404 HSO.AddPath(Path, frontend::Angled, true, false, false);
405 }
406
407 #endif
408
409 #ifdef HAVE_SETMAINFILEID
410
create_main_file_id(SourceManager & SM,const FileEntry * file)411 static void create_main_file_id(SourceManager &SM, const FileEntry *file)
412 {
413 SM.setMainFileID(SM.createFileID(file, SourceLocation(),
414 SrcMgr::C_User));
415 }
416
417 #else
418
create_main_file_id(SourceManager & SM,const FileEntry * file)419 static void create_main_file_id(SourceManager &SM, const FileEntry *file)
420 {
421 SM.createMainFileID(file);
422 }
423
424 #endif
425
426 #ifdef SETLANGDEFAULTS_TAKES_5_ARGUMENTS
427
428 #include "set_lang_defaults_arg4.h"
429
set_lang_defaults(CompilerInstance * Clang)430 static void set_lang_defaults(CompilerInstance *Clang)
431 {
432 PreprocessorOptions &PO = Clang->getPreprocessorOpts();
433 TargetOptions &TO = Clang->getTargetOpts();
434 llvm::Triple T(TO.Triple);
435 SETLANGDEFAULTS::setLangDefaults(Clang->getLangOpts(), IK_C, T,
436 setLangDefaultsArg4(PO),
437 LangStandard::lang_unspecified);
438 }
439
440 #else
441
set_lang_defaults(CompilerInstance * Clang)442 static void set_lang_defaults(CompilerInstance *Clang)
443 {
444 CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C,
445 LangStandard::lang_unspecified);
446 }
447
448 #endif
449
450 #ifdef SETINVOCATION_TAKES_SHARED_PTR
451
set_invocation(CompilerInstance * Clang,CompilerInvocation * invocation)452 static void set_invocation(CompilerInstance *Clang,
453 CompilerInvocation *invocation)
454 {
455 Clang->setInvocation(std::make_shared<CompilerInvocation>(*invocation));
456 }
457
458 #else
459
set_invocation(CompilerInstance * Clang,CompilerInvocation * invocation)460 static void set_invocation(CompilerInstance *Clang,
461 CompilerInvocation *invocation)
462 {
463 Clang->setInvocation(invocation);
464 }
465
466 #endif
467
468 /* Helper function for ignore_error that only gets enabled if T
469 * (which is either const FileEntry * or llvm::ErrorOr<const FileEntry *>)
470 * has getError method, i.e., if it is llvm::ErrorOr<const FileEntry *>.
471 */
472 template <class T>
ignore_error_helper(const T obj,int,int[1][sizeof (obj.getError ())])473 static const FileEntry *ignore_error_helper(const T obj, int,
474 int[1][sizeof(obj.getError())])
475 {
476 return *obj;
477 }
478
479 /* Helper function for ignore_error that is always enabled,
480 * but that only gets selected if the variant above is not enabled,
481 * i.e., if T is const FileEntry *.
482 */
483 template <class T>
ignore_error_helper(const T obj,long,void *)484 static const FileEntry *ignore_error_helper(const T obj, long, void *)
485 {
486 return obj;
487 }
488
489 /* Given either a const FileEntry * or a llvm::ErrorOr<const FileEntry *>,
490 * extract out the const FileEntry *.
491 */
492 template <class T>
ignore_error(const T obj)493 static const FileEntry *ignore_error(const T obj)
494 {
495 return ignore_error_helper(obj, 0, NULL);
496 }
497
498 /* Return the FileEntry corresponding to the given file name
499 * in the given compiler instances, ignoring any error.
500 */
getFile(CompilerInstance * Clang,std::string Filename)501 static const FileEntry *getFile(CompilerInstance *Clang, std::string Filename)
502 {
503 return ignore_error(Clang->getFileManager().getFile(Filename));
504 }
505
506 /* Create an interface generator for the selected language and
507 * then use it to generate the interface.
508 */
generate(MyASTConsumer & consumer,SourceManager & SM)509 static void generate(MyASTConsumer &consumer, SourceManager &SM)
510 {
511 generator *gen;
512
513 if (OutputLanguage.compare("python") == 0) {
514 gen = new python_generator(SM, consumer.exported_types,
515 consumer.exported_functions, consumer.functions);
516 } else if (OutputLanguage.compare("cpp") == 0) {
517 gen = new plain_cpp_generator(SM, consumer.exported_types,
518 consumer.exported_functions, consumer.functions);
519 } else if (OutputLanguage.compare("cpp-checked") == 0) {
520 gen = new plain_cpp_generator(SM, consumer.exported_types,
521 consumer.exported_functions, consumer.functions, true);
522 } else if (OutputLanguage.compare("cpp-checked-conversion") == 0) {
523 gen = new cpp_conversion_generator(SM, consumer.exported_types,
524 consumer.exported_functions, consumer.functions);
525 } else if (OutputLanguage.compare("template-cpp") == 0) {
526 gen = new template_cpp_generator(SM, consumer.exported_types,
527 consumer.exported_functions, consumer.functions);
528 } else {
529 cerr << "Language '" << OutputLanguage
530 << "' not recognized." << endl
531 << "Not generating bindings." << endl;
532 exit(EXIT_FAILURE);
533 }
534
535 gen->generate();
536 }
537
main(int argc,char * argv[])538 int main(int argc, char *argv[])
539 {
540 llvm::cl::ParseCommandLineOptions(argc, argv);
541
542 CompilerInstance *Clang = new CompilerInstance();
543 create_diagnostics(Clang);
544 DiagnosticsEngine &Diags = Clang->getDiagnostics();
545 Diags.setSuppressSystemWarnings(true);
546 TargetInfo *target = create_target_info(Clang, Diags);
547 Clang->setTarget(target);
548 set_lang_defaults(Clang);
549 CompilerInvocation *invocation =
550 construct_invocation(InputFilename.c_str(), Diags);
551 if (invocation)
552 set_invocation(Clang, invocation);
553 Clang->createFileManager();
554 Clang->createSourceManager(Clang->getFileManager());
555 HeaderSearchOptions &HSO = Clang->getHeaderSearchOpts();
556 LangOptions &LO = Clang->getLangOpts();
557 PreprocessorOptions &PO = Clang->getPreprocessorOpts();
558 HSO.ResourceDir = ResourceDir;
559
560 for (llvm::cl::list<string>::size_type i = 0; i < Includes.size(); ++i)
561 add_path(HSO, Includes[i]);
562
563 PO.addMacroDef("__isl_give=__attribute__((annotate(\"isl_give\")))");
564 PO.addMacroDef("__isl_keep=__attribute__((annotate(\"isl_keep\")))");
565 PO.addMacroDef("__isl_take=__attribute__((annotate(\"isl_take\")))");
566 PO.addMacroDef("__isl_export=__attribute__((annotate(\"isl_export\")))");
567 PO.addMacroDef("__isl_overload="
568 "__attribute__((annotate(\"isl_overload\"))) "
569 "__attribute__((annotate(\"isl_export\")))");
570 PO.addMacroDef("__isl_constructor=__attribute__((annotate(\"isl_constructor\"))) __attribute__((annotate(\"isl_export\")))");
571 PO.addMacroDef("__isl_subclass(super)=__attribute__((annotate(\"isl_subclass(\" #super \")\"))) __attribute__((annotate(\"isl_export\")))");
572
573 create_preprocessor(Clang);
574 Preprocessor &PP = Clang->getPreprocessor();
575
576 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), LO);
577
578 const FileEntry *file = getFile(Clang, InputFilename);
579 assert(file);
580 create_main_file_id(Clang->getSourceManager(), file);
581
582 Clang->createASTContext();
583 MyASTConsumer consumer;
584 Sema *sema = new Sema(PP, Clang->getASTContext(), consumer);
585
586 Diags.getClient()->BeginSourceFile(LO, &PP);
587 ParseAST(*sema);
588 Diags.getClient()->EndSourceFile();
589
590 generate(consumer, Clang->getSourceManager());
591
592 delete sema;
593 delete Clang;
594 llvm::llvm_shutdown();
595
596 if (Diags.hasErrorOccurred())
597 return EXIT_FAILURE;
598 return EXIT_SUCCESS;
599 }
600