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

source code of polly/lib/External/isl/interface/extract_interface.cc