|
12 | 12 |
|
13 | 13 | #include "swift/Index/IndexRecord.h"
|
14 | 14 | #include "swift/AST/ASTContext.h"
|
| 15 | +#include "swift/AST/ASTPrinter.h" |
15 | 16 | #include "swift/AST/Decl.h"
|
16 | 17 | #include "swift/AST/DiagnosticsFrontend.h"
|
17 | 18 | #include "swift/AST/Expr.h"
|
|
24 | 25 | #include "swift/AST/Types.h"
|
25 | 26 | #include "swift/Basic/PathRemapper.h"
|
26 | 27 | #include "swift/ClangImporter/ClangModule.h"
|
| 28 | +#include "swift/IDE/ModuleInterfacePrinting.h" |
27 | 29 | #include "swift/Index/Index.h"
|
28 | 30 | #include "clang/Basic/FileManager.h"
|
29 | 31 | #include "clang/Frontend/CompilerInstance.h"
|
30 |
| -#include "clang/Index/IndexingAction.h" |
31 | 32 | #include "clang/Index/IndexRecordWriter.h"
|
32 | 33 | #include "clang/Index/IndexUnitWriter.h"
|
| 34 | +#include "clang/Index/IndexingAction.h" |
33 | 35 | #include "clang/Lex/Preprocessor.h"
|
| 36 | +#include "clang/Serialization/ASTReader.h" |
34 | 37 | #include "llvm/Support/Path.h"
|
35 | 38 |
|
36 | 39 | using namespace swift;
|
@@ -393,6 +396,176 @@ emitDataForSwiftSerializedModule(ModuleDecl *module,
|
393 | 396 | const PathRemapper &pathRemapper,
|
394 | 397 | SourceFile *initialFile);
|
395 | 398 |
|
| 399 | +// FIXME (Alex): Share code with importer. |
| 400 | +inline bool requiresCPlusPlus(const clang::Module *module) { |
| 401 | + // The libc++ modulemap doesn't currently declare the requirement. |
| 402 | + if (module->getTopLevelModuleName() == "std") |
| 403 | + return true; |
| 404 | + |
| 405 | + // Modulemaps often declare the requirement for the top-level module only. |
| 406 | + if (auto parent = module->Parent) { |
| 407 | + if (requiresCPlusPlus(parent)) |
| 408 | + return true; |
| 409 | + } |
| 410 | + |
| 411 | + return llvm::any_of(module->Requirements, [](clang::Module::Requirement req) { |
| 412 | + return req.first == "cplusplus"; |
| 413 | + }); |
| 414 | +} |
| 415 | + |
| 416 | +static void |
| 417 | +appendSymbolicInterfaceToIndexStorePath(SmallVectorImpl<char> &resultingPath) { |
| 418 | + llvm::sys::path::append(resultingPath, "interfaces"); |
| 419 | +} |
| 420 | + |
| 421 | +static bool initSymbolicInterfaceStorePath(StringRef storePath, |
| 422 | + std::string &error) { |
| 423 | + using namespace llvm::sys; |
| 424 | + SmallString<128> subPath = storePath; |
| 425 | + appendSymbolicInterfaceToIndexStorePath(subPath); |
| 426 | + std::error_code ec = fs::create_directories(subPath); |
| 427 | + if (ec) { |
| 428 | + llvm::raw_string_ostream err(error); |
| 429 | + err << "failed to create directory '" << subPath << "': " << ec.message(); |
| 430 | + return true; |
| 431 | + } |
| 432 | + return false; |
| 433 | +} |
| 434 | + |
| 435 | +static void appendSymbolicInterfaceClangModuleFilename( |
| 436 | + StringRef filePath, SmallVectorImpl<char> &resultingPath) { |
| 437 | + llvm::sys::path::append(resultingPath, llvm::sys::path::filename(filePath)); |
| 438 | + StringRef extension = ".symbolicswiftinterface"; |
| 439 | + resultingPath.append(extension.begin(), extension.end()); |
| 440 | +} |
| 441 | + |
| 442 | +// FIXME (Alex): Share code with IndexUnitWriter in LLVM after refactoring it. |
| 443 | +static Optional<bool> |
| 444 | +isFileUpToDateForOutputFile(StringRef filePath, |
| 445 | + Optional<StringRef> timeCompareFilePath, |
| 446 | + std::string &error) { |
| 447 | + llvm::sys::fs::file_status unitStat; |
| 448 | + if (std::error_code ec = llvm::sys::fs::status(filePath, unitStat)) { |
| 449 | + if (ec != std::errc::no_such_file_or_directory) { |
| 450 | + llvm::raw_string_ostream err(error); |
| 451 | + err << "could not access path '" << filePath << "': " << ec.message(); |
| 452 | + return {}; |
| 453 | + } |
| 454 | + return false; |
| 455 | + } |
| 456 | + |
| 457 | + if (!timeCompareFilePath) |
| 458 | + return true; |
| 459 | + |
| 460 | + llvm::sys::fs::file_status compareStat; |
| 461 | + if (std::error_code ec = |
| 462 | + llvm::sys::fs::status(*timeCompareFilePath, compareStat)) { |
| 463 | + if (ec != std::errc::no_such_file_or_directory) { |
| 464 | + llvm::raw_string_ostream err(error); |
| 465 | + err << "could not access path '" << *timeCompareFilePath |
| 466 | + << "': " << ec.message(); |
| 467 | + return {}; |
| 468 | + } |
| 469 | + return true; |
| 470 | + } |
| 471 | + |
| 472 | + // Return true (unit is up-to-date) if the file to compare is older than the |
| 473 | + // unit file. |
| 474 | + return compareStat.getLastModificationTime() <= |
| 475 | + unitStat.getLastModificationTime(); |
| 476 | +} |
| 477 | + |
| 478 | +/// Emit the symbolic swift interface file for an imported Clang module into the |
| 479 | +/// index store directory. |
| 480 | +/// |
| 481 | +/// The swift interface file is emitted only when it doesn't exist yet, or when |
| 482 | +/// the PCM for the Clang module has been updated. |
| 483 | +/// |
| 484 | +/// System modules without the 'cplusplus' requirement are not emitted. |
| 485 | +static void emitSymbolicInterfaceForClangModule( |
| 486 | + ClangModuleUnit *clangModUnit, ModuleDecl *M, |
| 487 | + const clang::Module *clangModule, StringRef indexStorePath, |
| 488 | + const clang::CompilerInstance &clangCI, DiagnosticEngine &diags) { |
| 489 | + if (!M->getASTContext().LangOpts.EnableCXXInterop) |
| 490 | + return; |
| 491 | + // Skip system modules without an explicit 'cplusplus' requirement. |
| 492 | + bool isSystem = clangModUnit->isSystemModule(); |
| 493 | + if (isSystem && !requiresCPlusPlus(clangModule)) |
| 494 | + return; |
| 495 | + |
| 496 | + // Make sure the `interfaces` directory is created. |
| 497 | + std::string error; |
| 498 | + if (initSymbolicInterfaceStorePath(indexStorePath, error)) { |
| 499 | + diags.diagnose(SourceLoc(), diag::error_create_index_dir, error); |
| 500 | + return; |
| 501 | + } |
| 502 | + |
| 503 | + // Determine the output name for the symbolic interface file. |
| 504 | + clang::serialization::ModuleFile *ModFile = |
| 505 | + clangCI.getASTReader()->getModuleManager().lookup( |
| 506 | + clangModule->getASTFile()); |
| 507 | + assert(ModFile && "no module file loaded for module ?"); |
| 508 | + SmallString<128> interfaceOutputPath = indexStorePath; |
| 509 | + appendSymbolicInterfaceToIndexStorePath(interfaceOutputPath); |
| 510 | + appendSymbolicInterfaceClangModuleFilename(ModFile->FileName, |
| 511 | + interfaceOutputPath); |
| 512 | + |
| 513 | + // Check if the symbolic interface file is already up to date. |
| 514 | + auto upToDate = isFileUpToDateForOutputFile( |
| 515 | + interfaceOutputPath, StringRef(ModFile->FileName), error); |
| 516 | + if (!upToDate) { |
| 517 | + diags.diagnose(SourceLoc(), diag::error_index_failed_status_check, error); |
| 518 | + return; |
| 519 | + } |
| 520 | + if (M->getASTContext().LangOpts.EnableIndexingSystemModuleRemarks) { |
| 521 | + diags.diagnose(SourceLoc(), diag::remark_emitting_symbolic_interface_module, |
| 522 | + interfaceOutputPath, *upToDate); |
| 523 | + } |
| 524 | + if (*upToDate) |
| 525 | + return; |
| 526 | + |
| 527 | + // Output the interface to a temporary file first. |
| 528 | + SmallString<128> tempOutputPath; |
| 529 | + tempOutputPath = llvm::sys::path::parent_path(interfaceOutputPath); |
| 530 | + llvm::sys::path::append(tempOutputPath, |
| 531 | + llvm::sys::path::filename(interfaceOutputPath)); |
| 532 | + tempOutputPath += "-%%%%%%%%"; |
| 533 | + int tempFD; |
| 534 | + if (llvm::sys::fs::createUniqueFile(tempOutputPath.str(), tempFD, |
| 535 | + tempOutputPath)) { |
| 536 | + llvm::raw_string_ostream errOS(error); |
| 537 | + errOS << "failed to create temporary file: " << tempOutputPath; |
| 538 | + diags.diagnose(SourceLoc(), diag::error_write_index_record, errOS.str()); |
| 539 | + return; |
| 540 | + } |
| 541 | + |
| 542 | + llvm::raw_fd_ostream os(tempFD, /*shouldClose=*/true); |
| 543 | + std::unique_ptr<ASTPrinter> printer; |
| 544 | + printer.reset(new StreamPrinter(os)); |
| 545 | + ide::printSymbolicSwiftClangModuleInterface(M, *printer, clangModule); |
| 546 | + os.close(); |
| 547 | + |
| 548 | + if (os.has_error()) { |
| 549 | + llvm::raw_string_ostream errOS(error); |
| 550 | + errOS << "failed to write '" << tempOutputPath |
| 551 | + << "': " << os.error().message(); |
| 552 | + diags.diagnose(SourceLoc(), diag::error_write_index_record, errOS.str()); |
| 553 | + os.clear_error(); |
| 554 | + return; |
| 555 | + } |
| 556 | + |
| 557 | + // Move the resulting output to the destination symbolic interface file. |
| 558 | + std::error_code ec = llvm::sys::fs::rename( |
| 559 | + /*from=*/tempOutputPath.c_str(), /*to=*/interfaceOutputPath.c_str()); |
| 560 | + if (ec) { |
| 561 | + llvm::raw_string_ostream errOS(error); |
| 562 | + errOS << "failed to rename '" << tempOutputPath << "' to '" |
| 563 | + << interfaceOutputPath << "': " << ec.message(); |
| 564 | + diags.diagnose(SourceLoc(), diag::error_write_index_record, errOS.str()); |
| 565 | + return; |
| 566 | + } |
| 567 | +} |
| 568 | + |
396 | 569 | static void addModuleDependencies(ArrayRef<ImportedModule> imports,
|
397 | 570 | StringRef indexStorePath,
|
398 | 571 | bool indexClangModules,
|
@@ -444,6 +617,9 @@ static void addModuleDependencies(ArrayRef<ImportedModule> imports,
|
444 | 617 | if (shouldIndexModule)
|
445 | 618 | clang::index::emitIndexDataForModuleFile(clangMod,
|
446 | 619 | clangCI, unitWriter);
|
| 620 | + // Emit the symbolic interface file in addition to index data. |
| 621 | + emitSymbolicInterfaceForClangModule( |
| 622 | + clangModUnit, mod, clangMod, indexStorePath, clangCI, diags); |
447 | 623 | }
|
448 | 624 | } else {
|
449 | 625 | // Serialized AST file.
|
|
0 commit comments