Home | History | Annotate | Line # | Download | only in interface
extract_interface.cc revision 1.1.1.1
      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  */
    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  */
    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 
    157 	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
    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)
    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)
    196 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags)
    197 {
    198 	return new Driver(binary, llvm::sys::getDefaultTargetTriple(),
    199 			    "", Diags);
    200 }
    201 #else
    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 {
    214 	static Job *command(Job *J) { return J; }
    215 	static Job *command(Job &J) { return &J; }
    216 	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  */
    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  */
    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  */
    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  */
    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  */
    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 
    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 
    306 static TextDiagnosticPrinter *construct_printer(void)
    307 {
    308 	return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions());
    309 }
    310 
    311 #else
    312 
    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 
    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 
    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 
    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 
    355 static void create_diagnostics(CompilerInstance *Clang)
    356 {
    357 	Clang->createDiagnostics(0, NULL, construct_printer());
    358 }
    359 
    360 #else
    361 
    362 static void create_diagnostics(CompilerInstance *Clang)
    363 {
    364 	Clang->createDiagnostics(construct_printer());
    365 }
    366 
    367 #endif
    368 
    369 #ifdef CREATEPREPROCESSOR_TAKES_TUKIND
    370 
    371 static void create_preprocessor(CompilerInstance *Clang)
    372 {
    373 	Clang->createPreprocessor(TU_Complete);
    374 }
    375 
    376 #else
    377 
    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  */
    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  */
    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 
    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 
    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 
    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 
    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 
    452 static void set_invocation(CompilerInstance *Clang,
    453 	CompilerInvocation *invocation)
    454 {
    455 	Clang->setInvocation(std::make_shared<CompilerInvocation>(*invocation));
    456 }
    457 
    458 #else
    459 
    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>
    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>
    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>
    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  */
    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  */
    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 
    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