2323#include " swift/ClangImporter/ClangImporter.h"
2424#include " swift/Frontend/FrontendOptions.h"
2525
26+ #include " clang/Basic/FileManager.h"
2627#include " clang/Basic/Module.h"
28+ #include " clang/Lex/HeaderSearch.h"
2729
30+ #include " llvm/Support/FormatVariadic.h"
31+ #include " llvm/Support/Path.h"
2832#include " llvm/Support/raw_ostream.h"
2933
3034using namespace swift ;
@@ -386,9 +390,133 @@ static int compareImportModulesByName(const ImportModuleTy *left,
386390 return 1 ;
387391}
388392
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+
389515static void writeImports (raw_ostream &out,
390516 llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
391517 ModuleDecl &M, StringRef bridgingHeader,
518+ const FrontendOptions &frontendOpts,
519+ clang::HeaderSearch &clangHeaderSearchInfo,
392520 bool useCxxImport = false ) {
393521 // Note: we can't use has_feature(modules) as it's always enabled in C++20
394522 // mode.
@@ -413,6 +541,45 @@ static void writeImports(raw_ostream &out,
413541 return import == importer->getImportedHeaderModule ();
414542 };
415543
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+
416583 // Track printed names to handle overlay modules.
417584 llvm::SmallPtrSet<Identifier, 8 > seenImports;
418585 bool includeUnderlying = false ;
@@ -425,18 +592,46 @@ static void writeImports(raw_ostream &out,
425592 includeUnderlying = true ;
426593 continue ;
427594 }
428- if (seenImports.insert (Name).second )
595+ if (seenImports.insert (Name).second ) {
429596 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+ }
430613 } else {
431614 const auto *clangModule = import .get <const clang::Module *>();
432615 assert (clangModule->isSubModule () &&
433616 " top-level modules should use a normal swift::ModuleDecl" );
434617 out << importDirective << ' ' ;
435618 ModuleDecl::ReverseFullNameIterator (clangModule).printForward (out);
436619 out << " ;\n " ;
620+
621+ if (frontendOpts.EmitClangHeaderWithNonModularIncludes ) {
622+ collectClangModuleHeaderIncludes (
623+ clangModule, fileManager, requiredTextualIncludes, visitedModules,
624+ includeDirs, cwd.get ());
625+ }
437626 }
438627 }
439628
629+ if (frontendOpts.EmitClangHeaderWithNonModularIncludes ) {
630+ out << " #else\n " ;
631+ for (auto header : requiredTextualIncludes) {
632+ out << " #import <" << header << " >\n " ;
633+ }
634+ }
440635 out << " #endif\n\n " ;
441636
442637 if (includeUnderlying) {
@@ -490,7 +685,8 @@ static std::string computeMacroGuard(const ModuleDecl *M) {
490685bool swift::printAsClangHeader (raw_ostream &os, ModuleDecl *M,
491686 StringRef bridgingHeader,
492687 const FrontendOptions &frontendOpts,
493- const IRGenOptions &irGenOpts) {
688+ const IRGenOptions &irGenOpts,
689+ clang::HeaderSearch &clangHeaderSearchInfo) {
494690 llvm::PrettyStackTraceString trace (" While generating Clang header" );
495691
496692 SwiftToClangInteropContext interopContext (*M, irGenOpts);
@@ -500,8 +696,10 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
500696 llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf};
501697 printModuleContentsAsObjC (objcModuleContents, imports, *M, interopContext);
502698 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+ });
505703 writePostImportPrologue (os, *M);
506704 emitObjCConditional (os, [&] { os << objcModuleContents.str (); });
507705 emitCxxConditional (os, [&] {
@@ -530,8 +728,8 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
530728 moduleContents, *M, interopContext,
531729 /* requiresExposedAttribute=*/ requiresExplicitExpose);
532730 // 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 );
535733 // Embed the standard library directly.
536734 if (defaultDependencyBehavior && deps.dependsOnStandardLibrary ) {
537735 assert (!M->isStdlibModule ());
0 commit comments