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/Host.h> 51 #include <llvm/Support/ManagedStatic.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 89 using namespace std; 90 using namespace clang; 91 using namespace clang::driver; 92 #ifdef HAVE_LLVM_OPTION_ARG_H 93 using namespace llvm::opt; 94 #endif 95 96 #ifdef HAVE_ADT_OWNINGPTR_H 97 #define unique_ptr llvm::OwningPtr 98 #endif 99 100 static llvm::cl::opt<string> InputFilename(llvm::cl::Positional, 101 llvm::cl::Required, llvm::cl::desc("<input file>")); 102 static llvm::cl::list<string> Includes("I", 103 llvm::cl::desc("Header search path"), 104 llvm::cl::value_desc("path"), llvm::cl::Prefix); 105 106 static 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 111 static 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 */ 118 bool 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 */ 137 static 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 */ 148 struct 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 180 static 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) 186 static 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) 192 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags) 193 { 194 return new Driver(binary, llvm::sys::getDefaultTargetTriple(), 195 "", Diags); 196 } 197 #else 198 static Driver *construct_driver(const char *binary, DiagnosticsEngine &Diags) 199 { 200 return new Driver(binary, llvm::sys::getDefaultTargetTriple(), Diags); 201 } 202 #endif 203 204 namespace 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 */ 209 struct 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 */ 220 static 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 */ 231 static 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 */ 246 static 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 */ 256 static 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 */ 266 static 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 292 static 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 302 static TextDiagnosticPrinter *construct_printer(void) 303 { 304 return new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions()); 305 } 306 307 #else 308 309 static 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 319 static 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 329 static 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 339 static 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 351 static void create_diagnostics(CompilerInstance *Clang) 352 { 353 Clang->createDiagnostics(0, NULL, construct_printer()); 354 } 355 356 #else 357 358 static void create_diagnostics(CompilerInstance *Clang) 359 { 360 Clang->createDiagnostics(construct_printer()); 361 } 362 363 #endif 364 365 #ifdef CREATEPREPROCESSOR_TAKES_TUKIND 366 367 static void create_preprocessor(CompilerInstance *Clang) 368 { 369 Clang->createPreprocessor(TU_Complete); 370 } 371 372 #else 373 374 static 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 */ 387 void 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 */ 398 void 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 407 static 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 415 static 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 426 static void set_lang_defaults(CompilerInstance *Clang) 427 { 428 PreprocessorOptions &PO = Clang->getPreprocessorOpts(); 429 TargetOptions &TO = Clang->getTargetOpts(); 430 llvm::Triple T(TO.Triple); 431 CompilerInvocation::setLangDefaults(Clang->getLangOpts(), IK_C, T, 432 setLangDefaultsArg4(PO), 433 LangStandard::lang_unspecified); 434 } 435 436 #else 437 438 static 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 448 static void set_invocation(CompilerInstance *Clang, 449 CompilerInvocation *invocation) 450 { 451 Clang->setInvocation(std::make_shared<CompilerInvocation>(*invocation)); 452 } 453 454 #else 455 456 static 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 */ 468 template <class T> 469 static 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 */ 479 template <class T> 480 static 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 */ 488 template <class T> 489 static 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 */ 497 static 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 */ 505 static void generate(MyASTConsumer &consumer, SourceManager &SM) 506 { 507 generator *gen; 508 509 if (OutputLanguage.compare("python") == 0) { 510 gen = new python_generator(SM, consumer.exported_types, 511 consumer.exported_functions, consumer.functions); 512 } else if (OutputLanguage.compare("cpp") == 0) { 513 gen = new plain_cpp_generator(SM, consumer.exported_types, 514 consumer.exported_functions, consumer.functions); 515 } else if (OutputLanguage.compare("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("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("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 534 int 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