From 88b719d7fc52d0c937adcd9b832eee2d51b85e1f Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Fri, 3 Oct 2025 16:34:51 -0700 Subject: [PATCH] [Caching] Adjust the bridging header chaining rule Adjust the rule for how bridging header chaining is performed to increase compatibilities with existing project configuration. Previously in #84442, bridging header chaining was done via content concatenation, rather than `#include` via absolute path, to enable prefix mapping to drop references to absolute path. This triggers an incompatibility since the directory of the header file is also considered as part of the search path when resolve the include directives in the file. Simple concatenation will result in some `#include` can no longer be resolved. Now relax the rule to do header content concatenation only when prefix map is used. This will keep the breakage minimal, and the breakage can always be fixed by additional include paths when turning on prefix mapping. In the future, we should move towards internal bridging header implementation to avoid the need of chaining completely. rdar://161854282 --- .../ModuleDependencyScanner.cpp | 82 +++++++++++-------- test/CAS/bridging-header-prefix-map.swift | 11 +++ .../bridging-header-autochaining.swift | 11 +-- 3 files changed, 65 insertions(+), 39 deletions(-) diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 5430adb2e940a..5cd19423bf489 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -1644,16 +1644,6 @@ void ModuleDependencyScanner::resolveCrossImportOverlayDependencies( allModules.end(), action); } -static void appendHeaderContent(llvm::raw_ostream &OS, - llvm::MemoryBufferRef buffer, - ModuleDependencyID fromModule) { - // Use preprocessor directives to add some clues for where the content is - // coming from. - OS << "# 1 \"/" - << llvm::sys::path::filename(buffer.getBufferIdentifier()) << "\" 1\n"; - OS << buffer.getBuffer(); -} - llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining( const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache, ModuleDependencyIDSetVector &allModules) { @@ -1663,35 +1653,60 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining( llvm::SmallString<256> chainedHeaderBuffer; llvm::raw_svector_ostream outOS(chainedHeaderBuffer); + // If prefix mapping is used, don't try to import header since that will add + // a path-relative component into dependency scanning and defeat the purpose + // of prefix mapping. Additionally, if everything is prefix mapped, the + // embedded header path is also prefix mapped, thus it can't be found anyway. + bool useImportHeader = !hasPathMapping(); + auto FS = ScanASTContext.SourceMgr.getFileSystem(); + + auto chainBridgingHeader = [&](StringRef moduleName, StringRef headerPath, + StringRef binaryModulePath) -> llvm::Error { + if (useImportHeader) { + if (auto buffer = FS->getBufferForFile(headerPath)) { + outOS << "#include \"" << headerPath << "\"\n"; + return llvm::Error::success(); + } + } + + if (binaryModulePath.empty()) + return llvm::createStringError("failed to load bridging header " + + headerPath); + + // Extract the embedded bridging header + auto moduleBuf = FS->getBufferForFile(binaryModulePath); + if (!moduleBuf) + return llvm::errorCodeToError(moduleBuf.getError()); + + auto content = extractEmbeddedBridgingHeaderContent( + std::move(*moduleBuf), /*headerPath=*/"", ScanASTContext); + if (!content) + return llvm::createStringError("can't load embedded header from " + + binaryModulePath); + + outOS << "# 1 \"\" 1\n"; + outOS << content->getBuffer() << "\n"; + return llvm::Error::success(); + }; + // Iterate through all the modules and collect all the bridging header // and chain them into a single file. The allModules list is in the order of // discover, thus providing stable ordering for a deterministic generated // buffer. - auto FS = ScanASTContext.SourceMgr.getFileSystem(); for (const auto &moduleID : allModules) { if (moduleID.Kind != ModuleDependencyKind::SwiftBinary) continue; auto moduleDependencyInfo = cache.findKnownDependency(moduleID); if (auto *binaryMod = moduleDependencyInfo.getAsSwiftBinaryModule()) { - if (!binaryMod->headerImport.empty()) { - if (auto buffer= FS->getBufferForFile(binaryMod->headerImport)) { - appendHeaderContent(outOS, (*buffer)->getMemBufferRef(), moduleID); - } else { - // Extract the embedded bridging header - auto moduleBuf = FS->getBufferForFile(binaryMod->compiledModulePath); - if (!moduleBuf) - return llvm::errorCodeToError(moduleBuf.getError()); - - auto content = extractEmbeddedBridgingHeaderContent( - std::move(*moduleBuf), /*headerPath=*/"", ScanASTContext); - if (!content) - return llvm::createStringError("can't load embedded header from " + - binaryMod->compiledModulePath); - - outOS << content->getBuffer() << "\n"; - } - } + if (binaryMod->headerImport.empty()) + continue; + + if (auto E = + chainBridgingHeader(moduleID.ModuleName, binaryMod->headerImport, + binaryMod->compiledModulePath)) + return E; } } @@ -1718,11 +1733,10 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining( // There are bridging header needed to be chained. Append the bridging // header from main module if needed and create use a new source buffer. if (mainModule->textualModuleDetails.bridgingHeaderFile) { - auto srcBuf = FS->getBufferForFile( - *mainModule->textualModuleDetails.bridgingHeaderFile); - if (!srcBuf) - return llvm::errorCodeToError(srcBuf.getError()); - appendHeaderContent(outOS, (*srcBuf)->getMemBufferRef(), rootModuleID); + if (auto E = chainBridgingHeader( + rootModuleID.ModuleName, + *mainModule->textualModuleDetails.bridgingHeaderFile, "")) + return E; } SmallString<256> outputPath( diff --git a/test/CAS/bridging-header-prefix-map.swift b/test/CAS/bridging-header-prefix-map.swift index 6c8cef9c5e935..fb594e082a08b 100644 --- a/test/CAS/bridging-header-prefix-map.swift +++ b/test/CAS/bridging-header-prefix-map.swift @@ -57,6 +57,17 @@ // RUN: -scanner-prefix-map-paths %t/header-1 /^header \ // RUN: -scanner-output-dir %t/header-1 -I %t +// RUN: %swift-scan-test -action get_chained_bridging_header -- %target-swift-frontend -scan-dependencies\ +// RUN: -module-name User -module-cache-path %t/clang-module-cache -O \ +// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ +// RUN: %t/user.swift -o %t/deps-3.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \ +// RUN: -scanner-prefix-map-paths %swift_src_root /^src -scanner-prefix-map-paths %t /^tmp \ +// RUN: -scanner-prefix-map-paths %t/header-1 /^header \ +// RUN: -scanner-output-dir %t/header-1 -I %t > %t/bridging-header1.h +// RUN: %FileCheck %s --input-file=%t/bridging-header1.h --check-prefix=HEADER + +// HEADER: # 1 "" 1 + // RUN: %target-swift-frontend -scan-dependencies -module-name User -module-cache-path %t/clang-module-cache -O \ // RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \ // RUN: %t/user.swift -o %t/deps-4.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \ diff --git a/test/ScanDependencies/bridging-header-autochaining.swift b/test/ScanDependencies/bridging-header-autochaining.swift index edd54b6ff1385..57839b7493cba 100644 --- a/test/ScanDependencies/bridging-header-autochaining.swift +++ b/test/ScanDependencies/bridging-header-autochaining.swift @@ -60,9 +60,8 @@ // RUN: %t/user.swift -auto-bridging-header-chaining -o %t/User.swiftmodule \ // RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -I %t > %t/header1.h // RUN: %FileCheck %s --check-prefix=HEADER1 --input-file=%t/header1.h -// HEADER1: # 1 "/Bridging.h" 1 -// HEADER1: #include "Foo.h" -// HEADER1: #include "Foo2.h" +// HEADER1: #include +// HEADER1-SAME: Bridging.h // RUN: %{python} %S/../CAS/Inputs/BuildCommandExtractor.py %t/deps2.json bridgingHeader > %t/header1.cmd // RUN: %target-swift-frontend @%t/header1.cmd -disable-implicit-swift-modules -O -o %t/bridging1.pch @@ -94,8 +93,10 @@ // RUN: %t/user.swift -auto-bridging-header-chaining -o %t/User.swiftmodule -import-objc-header %t/Bridging2.h \ // RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -I %t > %t/header2.h // RUN: %FileCheck %s --check-prefix=HEADER2 --input-file=%t/header2.h -// HEADER2: # 1 "/Bridging.h" 1 -// HEADER2: # 1 "/Bridging2.h" 1 +// HEADER2: #include +// HEADER2-SAME: Bridging.h +// HEADER2: #include +// HEADER2-SAME: Bridging2.h // RUN: %FileCheck %s --check-prefix DEPS_JSON --input-file=%t/deps3.json // DEPS_JSON: "chainedBridgingHeaderPath":