codegen_test.sh.in: determine option to produce context diff
[isl.git] / interface / extract_interface.cc
blobaebb049eca4de3ad7058e1a33360419530705529
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.
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.
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.
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 */
34 #include "isl_config.h"
35 #undef PACKAGE
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>
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"
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
100 #ifdef HAVE_ADT_OWNINGPTR_H
101 #define unique_ptr llvm::OwningPtr
102 #endif
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);
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"));
115 static const char *ResourceDir =
116 CLANG_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
118 /* Does decl have an attribute of the following form?
120 * __attribute__((annotate("name")))
122 bool has_annotation(Decl *decl, const char *name)
124 if (!decl->hasAttrs())
125 return false;
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;
136 return false;
139 /* Is decl marked as exported?
141 static bool is_exported(Decl *decl)
143 return has_annotation(decl, "isl_export");
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".
150 * We currently only consider single declarations.
152 struct MyASTConsumer : public ASTConsumer {
153 set<RecordDecl *> exported_types;
154 set<FunctionDecl *> exported_functions;
155 set<FunctionDecl *> functions;
157 virtual HandleTopLevelDeclReturn HandleTopLevelDecl(DeclGroupRef D) {
158 Decl *decl;
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;
177 return HandleTopLevelDeclContinue;
181 #ifdef USE_ARRAYREF
183 #ifdef HAVE_CXXISPRODUCTION
184 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
186 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
187 "", false, false, Diags);
189 #elif defined(HAVE_ISPRODUCTION)
190 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
192 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
193 "", false, Diags);
195 #elif defined(DRIVER_CTOR_TAKES_DEFAULTIMAGENAME)
196 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
198 return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
199 "", Diags);
201 #else
202 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
204 return new Driver(binary, llvm::sys::getDefaultTargetTriple(), Diags);
206 #endif
208 namespace clang { namespace driver { class Job; } }
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.
213 struct ClangAPI {
214 static Job *command(Job *J) { return J; }
215 static Job *command(Job &J) { return &J; }
216 static Command *command(Command &C) { return &C; }
219 #ifdef CREATE_FROM_ARGS_TAKES_ARRAYREF
221 /* Call CompilerInvocation::CreateFromArgs with the right arguments.
222 * In this case, an ArrayRef<const char *>.
224 static void create_from_args(CompilerInvocation &invocation,
225 const ArgStringList *args, DiagnosticsEngine &Diags)
227 CompilerInvocation::CreateFromArgs(invocation, *args, Diags);
230 #else
232 /* Call CompilerInvocation::CreateFromArgs with the right arguments.
233 * In this case, two "const char *" pointers.
235 static void create_from_args(CompilerInvocation &invocation,
236 const ArgStringList *args, DiagnosticsEngine &Diags)
238 CompilerInvocation::CreateFromArgs(invocation, args->data() + 1,
239 args->data() + args->size(),
240 Diags);
243 #endif
245 #ifdef CLANG_SYSROOT
246 /* Set sysroot if required.
248 * If CLANG_SYSROOT is defined, then set it to this value.
250 static void set_sysroot(ArgStringList &args)
252 args.push_back("-isysroot");
253 args.push_back(CLANG_SYSROOT);
255 #else
256 /* Set sysroot if required.
258 * If CLANG_SYSROOT is not defined, then it does not need to be set.
260 static void set_sysroot(ArgStringList &args)
263 #endif
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.
270 static CompilerInvocation *construct_invocation(const char *filename,
271 DiagnosticsEngine &Diags)
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();
282 Command *cmd = cast<Command>(ClangAPI::command(*Jobs.begin()));
283 if (strcmp(cmd->getCreator().getName(), "clang"))
284 return NULL;
286 ArgStringList args = cmd->getArguments();
287 set_sysroot(args);
289 CompilerInvocation *invocation = new CompilerInvocation;
290 create_from_args(*invocation, &args, Diags);
291 return invocation;
294 #else
296 static CompilerInvocation *construct_invocation(const char *filename,
297 DiagnosticsEngine &Diags)
299 return NULL;
302 #endif
304 #ifdef HAVE_BASIC_DIAGNOSTICOPTIONS_H
306 static TextDiagnosticPrinter *construct_printer(void)
308 return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
311 #else
313 static TextDiagnosticPrinter *construct_printer(void)
315 DiagnosticOptions DO;
316 return new TextDiagnosticPrinter(llvm::errs(), DO);
319 #endif
321 #ifdef CREATETARGETINFO_TAKES_SHARED_PTR
323 static TargetInfo *create_target_info(CompilerInstance *Clang,
324 DiagnosticsEngine &Diags)
326 shared_ptr<TargetOptions> TO = Clang->getInvocation().TargetOpts;
327 TO->Triple = llvm::sys::getDefaultTargetTriple();
328 return TargetInfo::CreateTargetInfo(Diags, TO);
331 #elif defined(CREATETARGETINFO_TAKES_POINTER)
333 static TargetInfo *create_target_info(CompilerInstance *Clang,
334 DiagnosticsEngine &Diags)
336 TargetOptions &TO = Clang->getTargetOpts();
337 TO.Triple = llvm::sys::getDefaultTargetTriple();
338 return TargetInfo::CreateTargetInfo(Diags, &TO);
341 #else
343 static TargetInfo *create_target_info(CompilerInstance *Clang,
344 DiagnosticsEngine &Diags)
346 TargetOptions &TO = Clang->getTargetOpts();
347 TO.Triple = llvm::sys::getDefaultTargetTriple();
348 return TargetInfo::CreateTargetInfo(Diags, TO);
351 #endif
353 #ifdef CREATEDIAGNOSTICS_TAKES_ARG
355 static void create_diagnostics(CompilerInstance *Clang)
357 Clang->createDiagnostics(0, NULL, construct_printer());
360 #else
362 static void create_diagnostics(CompilerInstance *Clang)
364 Clang->createDiagnostics(construct_printer());
367 #endif
369 #ifdef CREATEPREPROCESSOR_TAKES_TUKIND
371 static void create_preprocessor(CompilerInstance *Clang)
373 Clang->createPreprocessor(TU_Complete);
376 #else
378 static void create_preprocessor(CompilerInstance *Clang)
380 Clang->createPreprocessor();
383 #endif
385 #ifdef ADDPATH_TAKES_4_ARGUMENTS
387 /* Add "Path" to the header search options.
389 * Do not take into account sysroot, i.e., set ignoreSysRoot to true.
391 void add_path(HeaderSearchOptions &HSO, string Path)
393 HSO.AddPath(Path, frontend::Angled, false, true);
396 #else
398 /* Add "Path" to the header search options.
400 * Do not take into account sysroot, i.e., set IsSysRootRelative to false.
402 void add_path(HeaderSearchOptions &HSO, string Path)
404 HSO.AddPath(Path, frontend::Angled, true, false, false);
407 #endif
409 #ifdef HAVE_SETMAINFILEID
411 static void create_main_file_id(SourceManager &SM, const FileEntry *file)
413 SM.setMainFileID(SM.createFileID(file, SourceLocation(),
414 SrcMgr::C_User));
417 #else
419 static void create_main_file_id(SourceManager &SM, const FileEntry *file)
421 SM.createMainFileID(file);
424 #endif
426 #ifdef SETLANGDEFAULTS_TAKES_5_ARGUMENTS
428 #include "set_lang_defaults_arg4.h"
430 static void set_lang_defaults(CompilerInstance *Clang)
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);
440 #else
442 static void set_lang_defaults(CompilerInstance *Clang)
444 CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C,
445 LangStandard::lang_unspecified);
448 #endif
450 #ifdef SETINVOCATION_TAKES_SHARED_PTR
452 static void set_invocation(CompilerInstance *Clang,
453 CompilerInvocation *invocation)
455 Clang->setInvocation(std::make_shared<CompilerInvocation>(*invocation));
458 #else
460 static void set_invocation(CompilerInstance *Clang,
461 CompilerInvocation *invocation)
463 Clang->setInvocation(invocation);
466 #endif
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 *>.
472 template <class T>
473 static const FileEntry *ignore_error_helper(const T obj, int,
474 int[1][sizeof(obj.getError())])
476 return *obj;
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 *.
483 template <class T>
484 static const FileEntry *ignore_error_helper(const T obj, long, void *)
486 return obj;
489 /* Given either a const FileEntry * or a llvm::ErrorOr<const FileEntry *>,
490 * extract out the const FileEntry *.
492 template <class T>
493 static const FileEntry *ignore_error(const T obj)
495 return ignore_error_helper(obj, 0, NULL);
498 /* Return the FileEntry corresponding to the given file name
499 * in the given compiler instances, ignoring any error.
501 static const FileEntry *getFile(CompilerInstance *Clang, std::string Filename)
503 return ignore_error(Clang->getFileManager().getFile(Filename));
506 /* Create an interface generator for the selected language and
507 * then use it to generate the interface.
509 static void generate(MyASTConsumer &consumer, SourceManager &SM)
511 generator *gen;
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);
535 gen->generate();
538 int main(int argc, char *argv[])
540 llvm::cl::ParseCommandLineOptions(argc, argv);
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;
560 for (llvm::cl::list<string>::size_type i = 0; i < Includes.size(); ++i)
561 add_path(HSO, Includes[i]);
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\")))");
573 create_preprocessor(Clang);
574 Preprocessor &PP = Clang->getPreprocessor();
576 PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), LO);
578 const FileEntry *file = getFile(Clang, InputFilename);
579 assert(file);
580 create_main_file_id(Clang->getSourceManager(), file);
582 Clang->createASTContext();
583 MyASTConsumer consumer;
584 Sema *sema = new Sema(PP, Clang->getASTContext(), consumer);
586 Diags.getClient()->BeginSourceFile(LO, &PP);
587 ParseAST(*sema);
588 Diags.getClient()->EndSourceFile();
590 generate(consumer, Clang->getSourceManager());
592 delete sema;
593 delete Clang;
594 llvm::llvm_shutdown();
596 if (Diags.hasErrorOccurred())
597 return EXIT_FAILURE;
598 return EXIT_SUCCESS;