23
23
#include " swift/ClangImporter/ClangImporter.h"
24
24
#include " swift/Frontend/FrontendOptions.h"
25
25
26
+ #include " clang/Basic/FileManager.h"
26
27
#include " clang/Basic/Module.h"
28
+ #include " clang/Lex/HeaderSearch.h"
27
29
30
+ #include " llvm/Support/FormatVariadic.h"
28
31
#include " llvm/Support/raw_ostream.h"
29
32
30
33
using namespace swift ;
@@ -386,9 +389,141 @@ static int compareImportModulesByName(const ImportModuleTy *left,
386
389
return 1 ;
387
390
}
388
391
392
+ static llvm::SmallString<128 > makePathAbsolute (const llvm::StringRef path,
393
+ const llvm::StringRef cwd) {
394
+ if (llvm::sys::path::is_absolute (path))
395
+ return llvm::SmallString<128 >(path);
396
+
397
+ llvm::SmallString<128 > result = cwd;
398
+ llvm::sys::path::append (result,
399
+ llvm::sys::path::remove_leading_dotslash (path));
400
+ return result;
401
+ }
402
+
403
+ // Collect the set of header includes needed to import the given Clang module
404
+ // into an ObjectiveC program. Modeled after collectModuleHeaderIncludes in the
405
+ // Clang frontend (FrontendAction.cpp)
406
+ // Augment requiredTextualIncludes with the set of headers required.
407
+ static void collectClangModuleHeaderIncludes (
408
+ const clang::Module *clangModule, clang::FileManager &fileManager,
409
+ llvm::SmallSet<llvm::SmallString<128 >, 10 > &requiredTextualIncludes,
410
+ llvm::SmallSet<const clang::Module *, 10 > &visitedModules,
411
+ const llvm::SmallSet<llvm::SmallString<128 >, 10 > &includeDirs,
412
+ const llvm::StringRef cwd) {
413
+
414
+ if (!visitedModules.insert (clangModule).second )
415
+ return ;
416
+
417
+ auto addHeader = [&](llvm::StringRef headerPath,
418
+ llvm::StringRef pathRelativeToRootModuleDir) {
419
+ if (!clangModule->Directory )
420
+ return ;
421
+
422
+ llvm::SmallString<128 > textualInclude = makePathAbsolute (headerPath, cwd);
423
+ llvm::SmallString<128 > containingSearchDirPath;
424
+
425
+ // Check if the module map lies either in an include dir or in a direct
426
+ // subdirectory of an include dir
427
+ llvm::SmallString<128 > moduleMapDir =
428
+ makePathAbsolute (clangModule->Directory ->getName (), cwd);
429
+ llvm::SmallString<128 > moduleMapDirParent =
430
+ llvm::sys::path::parent_path (moduleMapDir);
431
+
432
+ for (auto &dir : {moduleMapDir, moduleMapDirParent}) {
433
+ if (includeDirs.contains (dir)) {
434
+ containingSearchDirPath = dir;
435
+ break ;
436
+ }
437
+ }
438
+
439
+ if (!containingSearchDirPath.empty ()) {
440
+ llvm::SmallString<128 > prefixToRemove =
441
+ llvm::formatv (" {0}/" , containingSearchDirPath);
442
+ llvm::sys::path::replace_path_prefix (textualInclude, prefixToRemove, " " );
443
+ } else {
444
+ // If we cannot find find the module map on the search path,
445
+ // fallback to including the header using the provided path relative
446
+ // to the module map
447
+ textualInclude = pathRelativeToRootModuleDir;
448
+ }
449
+
450
+ if (clangModule->getTopLevelModule ()->IsFramework ) {
451
+ llvm::SmallString<32 > frameworkName =
452
+ clangModule->getTopLevelModuleName ();
453
+ llvm::SmallString<64 > oldFrameworkPrefix =
454
+ llvm::formatv (" {0}.framework/Headers" , frameworkName);
455
+ llvm::sys::path::replace_path_prefix (textualInclude, oldFrameworkPrefix,
456
+ frameworkName);
457
+ }
458
+
459
+ requiredTextualIncludes.insert (textualInclude);
460
+ };
461
+
462
+ if (clang::Module::Header umbrellaHeader = clangModule->getUmbrellaHeader ()) {
463
+ addHeader (umbrellaHeader.Entry ->tryGetRealPathName (),
464
+ umbrellaHeader.PathRelativeToRootModuleDirectory );
465
+ } else if (clang::Module::DirectoryName umbrellaDir =
466
+ clangModule->getUmbrellaDir ()) {
467
+ SmallString<128 > nativeUmbrellaDirPath;
468
+ std::error_code errorCode;
469
+ llvm::sys::path::native (umbrellaDir.Entry ->getName (),
470
+ nativeUmbrellaDirPath);
471
+ llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem ();
472
+ for (llvm::vfs::recursive_directory_iterator
473
+ dir (fileSystem, nativeUmbrellaDirPath, errorCode),
474
+ end;
475
+ dir != end && !errorCode; dir.increment (errorCode)) {
476
+
477
+ if (llvm::StringSwitch<bool >(llvm::sys::path::extension (dir->path ()))
478
+ .Cases (" .h" , " .H" , " .hh" , " .hpp" , true )
479
+ .Default (false )) {
480
+
481
+ // Compute path to the header relative to the root of the module
482
+ // (location of the module map) First compute the relative path from
483
+ // umbrella directory to header file
484
+ SmallVector<StringRef> pathComponents;
485
+ auto pathIt = llvm::sys::path::rbegin (dir->path ());
486
+
487
+ for (int i = 0 ; i != dir.level () + 1 ; ++i, ++pathIt)
488
+ pathComponents.push_back (*pathIt);
489
+ // Then append this to the path from module root to umbrella dir
490
+ SmallString<128 > relativeHeaderPath;
491
+ if (umbrellaDir.PathRelativeToRootModuleDirectory != " ." )
492
+ relativeHeaderPath += umbrellaDir.PathRelativeToRootModuleDirectory ;
493
+
494
+ for (auto it = pathComponents.rbegin (), end = pathComponents.rend ();
495
+ it != end; ++it) {
496
+ llvm::sys::path::append (relativeHeaderPath, *it);
497
+ }
498
+
499
+ addHeader (dir->path (), relativeHeaderPath);
500
+ }
501
+ }
502
+ } else {
503
+ for (clang::Module::HeaderKind headerKind :
504
+ {clang::Module::HK_Normal, clang::Module::HK_Textual}) {
505
+ for (const clang::Module::Header &header :
506
+ clangModule->Headers [headerKind]) {
507
+ addHeader (header.Entry ->tryGetRealPathName (),
508
+ header.PathRelativeToRootModuleDirectory );
509
+ }
510
+ }
511
+ for (auto submodule : clangModule->submodules ()) {
512
+ if (submodule->IsExplicit )
513
+ continue ;
514
+
515
+ collectClangModuleHeaderIncludes (submodule, fileManager,
516
+ requiredTextualIncludes, visitedModules,
517
+ includeDirs, cwd);
518
+ }
519
+ }
520
+ }
521
+
389
522
static void writeImports (raw_ostream &out,
390
523
llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
391
524
ModuleDecl &M, StringRef bridgingHeader,
525
+ const FrontendOptions &frontendOpts,
526
+ clang::HeaderSearch &clangHeaderSearchInfo,
392
527
bool useCxxImport = false ) {
393
528
// Note: we can't use has_feature(modules) as it's always enabled in C++20
394
529
// mode.
@@ -413,6 +548,45 @@ static void writeImports(raw_ostream &out,
413
548
return import == importer->getImportedHeaderModule ();
414
549
};
415
550
551
+ clang::FileSystemOptions fileSystemOptions;
552
+ clang::FileManager fileManager{fileSystemOptions};
553
+
554
+ llvm::SmallSet<llvm::SmallString<128 >, 10 > requiredTextualIncludes;
555
+ llvm::SmallSet<const clang::Module *, 10 > visitedModules;
556
+ llvm::SmallSet<llvm::SmallString<128 >, 10 > includeDirs;
557
+
558
+ llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem ();
559
+ llvm::ErrorOr<std::string> cwd = fileSystem.getCurrentWorkingDirectory ();
560
+
561
+ if (frontendOpts.EmitObjCHeaderWithTextualImports ) {
562
+ assert (cwd && " Access to current working directory required" );
563
+
564
+ for (auto searchDir = clangHeaderSearchInfo.search_dir_begin ();
565
+ searchDir != clangHeaderSearchInfo.search_dir_end (); ++searchDir) {
566
+ includeDirs.insert (makePathAbsolute (searchDir->getName (), cwd.get ()));
567
+ }
568
+
569
+ const clang::Module *foundationModule = clangHeaderSearchInfo.lookupModule (
570
+ " Foundation" , clang::SourceLocation (), false , false );
571
+ const clang::Module *darwinModule = clangHeaderSearchInfo.lookupModule (
572
+ " Darwin" , clang::SourceLocation (), false , false );
573
+
574
+ std::function<void (const clang::Module *)>
575
+ collectTransitiveSubmoduleClosure;
576
+ collectTransitiveSubmoduleClosure = [&](const clang::Module *module) {
577
+ if (!module)
578
+ return ;
579
+
580
+ visitedModules.insert (module);
581
+ for (auto submodule : module->submodules ()) {
582
+ collectTransitiveSubmoduleClosure (submodule);
583
+ }
584
+ };
585
+
586
+ collectTransitiveSubmoduleClosure (foundationModule);
587
+ collectTransitiveSubmoduleClosure (darwinModule);
588
+ }
589
+
416
590
// Track printed names to handle overlay modules.
417
591
llvm::SmallPtrSet<Identifier, 8 > seenImports;
418
592
bool includeUnderlying = false ;
@@ -425,18 +599,46 @@ static void writeImports(raw_ostream &out,
425
599
includeUnderlying = true ;
426
600
continue ;
427
601
}
428
- if (seenImports.insert (Name).second )
429
- out << importDirective << ' ' << Name.str () << " ;\n " ;
602
+ if (seenImports.insert (Name).second ) {
603
+ out << " @import " << Name.str () << " ;\n " ;
604
+ if (frontendOpts.EmitObjCHeaderWithTextualImports ) {
605
+ if (const clang::Module *underlyingClangModule =
606
+ swiftModule->findUnderlyingClangModule ()) {
607
+ collectClangModuleHeaderIncludes (
608
+ underlyingClangModule, fileManager, requiredTextualIncludes,
609
+ visitedModules, includeDirs, cwd.get ());
610
+ } else if ((underlyingClangModule =
611
+ clangHeaderSearchInfo.lookupModule (
612
+ Name.str (), clang::SourceLocation (), true ,
613
+ true ))) {
614
+ collectClangModuleHeaderIncludes (
615
+ underlyingClangModule, fileManager, requiredTextualIncludes,
616
+ visitedModules, includeDirs, cwd.get ());
617
+ }
618
+ }
619
+ }
430
620
} else {
431
621
const auto *clangModule = import.get <const clang::Module *>();
432
622
assert (clangModule->isSubModule () &&
433
623
" top-level modules should use a normal swift::ModuleDecl" );
434
624
out << importDirective << ' ' ;
435
625
ModuleDecl::ReverseFullNameIterator (clangModule).printForward (out);
436
626
out << " ;\n " ;
627
+
628
+ if (frontendOpts.EmitObjCHeaderWithTextualImports ) {
629
+ collectClangModuleHeaderIncludes (
630
+ clangModule, fileManager, requiredTextualIncludes, visitedModules,
631
+ includeDirs, cwd.get ());
632
+ }
437
633
}
438
634
}
439
635
636
+ if (frontendOpts.EmitObjCHeaderWithTextualImports ) {
637
+ out << " #else\n " ;
638
+ for (auto header : requiredTextualIncludes) {
639
+ out << " #import <" << header << " >\n " ;
640
+ }
641
+ }
440
642
out << " #endif\n\n " ;
441
643
442
644
if (includeUnderlying) {
@@ -490,7 +692,8 @@ static std::string computeMacroGuard(const ModuleDecl *M) {
490
692
bool swift::printAsClangHeader (raw_ostream &os, ModuleDecl *M,
491
693
StringRef bridgingHeader,
492
694
const FrontendOptions &frontendOpts,
493
- const IRGenOptions &irGenOpts) {
695
+ const IRGenOptions &irGenOpts,
696
+ clang::HeaderSearch &clangHeaderSearchInfo) {
494
697
llvm::PrettyStackTraceString trace (" While generating Clang header" );
495
698
496
699
SwiftToClangInteropContext interopContext (*M, irGenOpts);
@@ -500,8 +703,10 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
500
703
llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf};
501
704
printModuleContentsAsObjC (objcModuleContents, imports, *M, interopContext);
502
705
writePrologue (os, M->getASTContext (), computeMacroGuard (M));
503
- emitObjCConditional (os,
504
- [&] { writeImports (os, imports, *M, bridgingHeader); });
706
+ emitObjCConditional (os, [&] {
707
+ writeImports (os, imports, *M, bridgingHeader, frontendOpts,
708
+ clangHeaderSearchInfo);
709
+ });
505
710
writePostImportPrologue (os, *M);
506
711
emitObjCConditional (os, [&] { os << objcModuleContents.str (); });
507
712
emitCxxConditional (os, [&] {
@@ -530,8 +735,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
530
735
moduleContents, *M, interopContext,
531
736
/* requiresExposedAttribute=*/ requiresExplicitExpose);
532
737
// FIXME: In ObjC++ mode, we do not need to reimport duplicate modules.
533
- writeImports (os, deps.imports , *M, bridgingHeader, /* useCxxImport= */ true );
534
-
738
+ writeImports (os, deps.imports , *M, bridgingHeader, frontendOpts,
739
+ clangHeaderSearchInfo, /* useCxxImport= */ true );
535
740
// Embed the standard library directly.
536
741
if (defaultDependencyBehavior && deps.dependsOnStandardLibrary ) {
537
742
assert (!M->isStdlibModule ());
0 commit comments