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