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