Skip to content

Commit b7329f4

Browse files
[Caching] Allow prefix mapping for generated bridging header
To allow prefix mapping of the bridging header to achieve cache hit when source files are located in different location, the generated chained bridging header should not include absolute paths of the headers. Fix the problem by concat the chained bridging header together. Fixes: swiftlang#84088
1 parent 963aad3 commit b7329f4

16 files changed

+241
-82
lines changed

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,9 +577,9 @@ bool extractCompilerFlagsFromInterface(
577577
llvm::VersionTuple extractUserModuleVersionFromInterface(StringRef moduleInterfacePath);
578578

579579
/// Extract embedded bridging header from binary module.
580-
std::string
580+
std::unique_ptr<llvm::MemoryBuffer>
581581
extractEmbeddedBridgingHeaderContent(std::unique_ptr<llvm::MemoryBuffer> file,
582-
ASTContext &Context);
582+
StringRef headerPath, ASTContext &Context);
583583
} // end namespace swift
584584

585585
#endif

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,12 +1364,8 @@ void ModuleDependencyScanner::resolveHeaderDependenciesForModule(
13641364
if (!moduleBuf)
13651365
return nullptr;
13661366

1367-
auto content = extractEmbeddedBridgingHeaderContent(std::move(*moduleBuf),
1368-
ScanASTContext);
1369-
if (content.empty())
1370-
return nullptr;
1371-
1372-
return llvm::MemoryBuffer::getMemBufferCopy(content, header);
1367+
return extractEmbeddedBridgingHeaderContent(std::move(*moduleBuf), header,
1368+
ScanASTContext);
13731369
};
13741370

13751371
if (isBinaryModuleWithHeaderInput) {
@@ -1636,85 +1632,116 @@ void ModuleDependencyScanner::resolveCrossImportOverlayDependencies(
16361632
allModules.end(), action);
16371633
}
16381634

1635+
static void appendHeaderContent(llvm::raw_ostream &OS,
1636+
llvm::MemoryBufferRef buffer,
1637+
ModuleDependencyID fromModule) {
1638+
// Use preprocessor directives to add some clues for where the content is
1639+
// coming from.
1640+
OS << "# 1 \"<module-" << fromModule.ModuleName << ">/"
1641+
<< llvm::sys::path::filename(buffer.getBufferIdentifier()) << "\" 1\n";
1642+
OS << buffer.getBuffer();
1643+
}
1644+
16391645
llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining(
16401646
const ModuleDependencyID &rootModuleID, ModuleDependenciesCache &cache,
16411647
ModuleDependencyIDSetVector &allModules) {
16421648
if (rootModuleID.Kind != ModuleDependencyKind::SwiftSource)
16431649
return llvm::Error::success();
16441650

1645-
bool hasBridgingHeader = false;
1646-
llvm::vfs::OnDiskOutputBackend outputBackend;
1647-
1648-
SmallString<256> outputPath(
1649-
ScanCompilerInvocation.getFrontendOptions().ScannerOutputDir);
1650-
1651-
if (outputPath.empty())
1652-
outputPath = "/<compiler-generated>";
1653-
1654-
llvm::sys::path::append(
1655-
outputPath, ScanCompilerInvocation.getFrontendOptions().ModuleName + "-" +
1656-
ScanCompilerInvocation.getModuleScanningHash() +
1657-
"-ChainedBridgingHeader.h");
1658-
1659-
llvm::SmallString<256> sourceBuf;
1660-
llvm::raw_svector_ostream outOS(sourceBuf);
1651+
llvm::SmallString<256> chainedHeaderBuffer;
1652+
llvm::raw_svector_ostream outOS(chainedHeaderBuffer);
16611653

16621654
// Iterate through all the modules and collect all the bridging header
16631655
// and chain them into a single file. The allModules list is in the order of
16641656
// discover, thus providing stable ordering for a deterministic generated
16651657
// buffer.
16661658
auto FS = ScanASTContext.SourceMgr.getFileSystem();
16671659
for (const auto &moduleID : allModules) {
1668-
if (moduleID.Kind != ModuleDependencyKind::SwiftSource &&
1669-
moduleID.Kind != ModuleDependencyKind::SwiftBinary)
1660+
if (moduleID.Kind != ModuleDependencyKind::SwiftBinary)
16701661
continue;
16711662

16721663
auto moduleDependencyInfo = cache.findKnownDependency(moduleID);
16731664
if (auto *binaryMod = moduleDependencyInfo.getAsSwiftBinaryModule()) {
16741665
if (!binaryMod->headerImport.empty()) {
1675-
hasBridgingHeader = true;
1676-
if (FS->exists(binaryMod->headerImport)) {
1677-
outOS << "#include \"" << binaryMod->headerImport << "\"\n";
1666+
if (auto buffer= FS->getBufferForFile(binaryMod->headerImport)) {
1667+
appendHeaderContent(outOS, (*buffer)->getMemBufferRef(), moduleID);
16781668
} else {
16791669
// Extract the embedded bridging header
16801670
auto moduleBuf = FS->getBufferForFile(binaryMod->compiledModulePath);
16811671
if (!moduleBuf)
16821672
return llvm::errorCodeToError(moduleBuf.getError());
16831673

16841674
auto content = extractEmbeddedBridgingHeaderContent(
1685-
std::move(*moduleBuf), ScanASTContext);
1686-
if (content.empty())
1675+
std::move(*moduleBuf), /*headerPath=*/"", ScanASTContext);
1676+
if (!content)
16871677
return llvm::createStringError("can't load embedded header from " +
16881678
binaryMod->compiledModulePath);
16891679

1690-
outOS << content << "\n";
1680+
outOS << content->getBuffer() << "\n";
16911681
}
16921682
}
1693-
} else if (auto *srcMod = moduleDependencyInfo.getAsSwiftSourceModule()) {
1694-
if (srcMod->textualModuleDetails.bridgingHeaderFile) {
1695-
hasBridgingHeader = true;
1696-
outOS << "#include \""
1697-
<< *srcMod->textualModuleDetails.bridgingHeaderFile << "\"\n";
1698-
}
16991683
}
17001684
}
17011685

1702-
if (!hasBridgingHeader)
1703-
return llvm::Error::success();
1686+
// Handle bridging header in main module.
1687+
auto mainModuleDeps = cache.findKnownDependency(rootModuleID);
1688+
auto *mainModule = mainModuleDeps.getAsSwiftSourceModule();
1689+
assert(mainModule && "expect main module to be a swift source module");
1690+
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer;
1691+
bool needChainedHeader = !chainedHeaderBuffer.empty();
1692+
if (!needChainedHeader) {
1693+
// There is no bridging header chained from dependencies.
1694+
// If main module also has no bridging header, ther is nothing to scan.
1695+
if (!mainModule->textualModuleDetails.bridgingHeaderFile)
1696+
return llvm::Error::success();
1697+
1698+
// Otherwise, there is no chaining needed. Just use the bridging header from
1699+
// main module.
1700+
if (auto headerBuffer = FS->getBufferForFile(
1701+
*mainModule->textualModuleDetails.bridgingHeaderFile))
1702+
sourceBuffer = std::move(*headerBuffer);
1703+
else
1704+
return llvm::errorCodeToError(headerBuffer.getError());
1705+
} else {
1706+
// There are bridging header needed to be chained. Append the bridging
1707+
// header from main module if needed and create use a new source buffer.
1708+
if (mainModule->textualModuleDetails.bridgingHeaderFile) {
1709+
auto srcBuf = FS->getBufferForFile(
1710+
*mainModule->textualModuleDetails.bridgingHeaderFile);
1711+
if (!srcBuf)
1712+
return llvm::errorCodeToError(srcBuf.getError());
1713+
appendHeaderContent(outOS, (*srcBuf)->getMemBufferRef(), rootModuleID);
1714+
}
17041715

1705-
if (ScanCompilerInvocation.getFrontendOptions().WriteScannerOutput) {
1706-
auto outFile = outputBackend.createFile(outputPath);
1707-
if (!outFile)
1708-
return outFile.takeError();
1709-
*outFile << sourceBuf;
1710-
if (auto err = outFile->keep())
1711-
return err;
1716+
SmallString<256> outputPath(
1717+
ScanCompilerInvocation.getFrontendOptions().ScannerOutputDir);
1718+
1719+
if (outputPath.empty())
1720+
outputPath = "/<compiler-generated>";
1721+
1722+
// Use the hash of the file content to differentiate the chained header.
1723+
auto fileHash =
1724+
llvm::toString(llvm::APInt(64, llvm::hash_value(chainedHeaderBuffer)),
1725+
36, /*Signed=*/false);
1726+
llvm::sys::path::append(
1727+
outputPath, ScanCompilerInvocation.getFrontendOptions().ModuleName +
1728+
"-" + fileHash + "-ChainedBridgingHeader.h");
1729+
1730+
if (ScanCompilerInvocation.getFrontendOptions().WriteScannerOutput) {
1731+
llvm::vfs::OnDiskOutputBackend outputBackend;
1732+
auto outFile = outputBackend.createFile(outputPath);
1733+
if (!outFile)
1734+
return outFile.takeError();
1735+
*outFile << chainedHeaderBuffer;
1736+
if (auto err = outFile->keep())
1737+
return err;
1738+
}
1739+
1740+
sourceBuffer =
1741+
llvm::MemoryBuffer::getMemBufferCopy(chainedHeaderBuffer, outputPath);
17121742
}
17131743

1714-
auto sourceBuffer =
1715-
llvm::MemoryBuffer::getMemBufferCopy(sourceBuf, outputPath);
17161744
// Scan and update the main module dependency.
1717-
auto mainModuleDeps = cache.findKnownDependency(rootModuleID);
17181745
ModuleDependencyIDSetVector headerClangModuleDependencies;
17191746
std::optional<std::string> includeTreeID;
17201747
auto err = withDependencyScanningWorker(
@@ -1733,7 +1760,8 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining(
17331760

17341761
if (!headerScanResult)
17351762
return llvm::createStringError(
1736-
"failed to scan generated bridging header " + outputPath);
1763+
"failed to scan generated bridging header " +
1764+
sourceBuffer->getBufferIdentifier());
17371765

17381766
// Record module dependencies for each new module we found.
17391767
cache.recordClangDependencies(
@@ -1774,9 +1802,25 @@ llvm::Error ModuleDependencyScanner::performBridgingHeaderChaining(
17741802
}
17751803
mainModuleDeps.updateBridgingHeaderCommandLine(
17761804
bridgingHeaderCommandLine);
1805+
if (needChainedHeader) {
1806+
// As only the chained bridging header is scanned, the dependency will
1807+
// not include the original bridging header passed by user. Fixup the
1808+
// headerFileInputs to include original bridging header and not
1809+
// include the generated header so build system can correctly computes
1810+
// the dependencies.
1811+
auto generated =
1812+
llvm::find(headerFileInputs, sourceBuffer->getBufferIdentifier());
1813+
if (generated != headerFileInputs.end()) {
1814+
if (mainModule->textualModuleDetails.bridgingHeaderFile)
1815+
*generated = *mainModule->textualModuleDetails.bridgingHeaderFile;
1816+
else
1817+
headerFileInputs.erase(generated);
1818+
}
1819+
}
17771820
mainModuleDeps.setHeaderSourceFiles(headerFileInputs);
1778-
mainModuleDeps.setChainedBridgingHeaderBuffer(
1779-
outputPath, sourceBuffer->getBuffer());
1821+
if (needChainedHeader)
1822+
mainModuleDeps.setChainedBridgingHeaderBuffer(
1823+
sourceBuffer->getBufferIdentifier(), sourceBuffer->getBuffer());
17801824
// Update the set of visible Clang modules
17811825
mainModuleDeps.addVisibleClangModules(headerScanResult->VisibleModules);
17821826

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,9 +675,9 @@ class ModuleFileSharedCore {
675675
}
676676

677677
/// Get embedded bridging header.
678-
std::string getEmbeddedHeader() const {
678+
StringRef getEmbeddedHeader() const {
679679
// Don't include the '\0' in the end.
680-
return importedHeaderInfo.contents.drop_back().str();
680+
return importedHeaderInfo.contents.drop_back();
681681
}
682682

683683
/// If the module-defining `.swiftinterface` file is an SDK-relative path,

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,8 +1478,9 @@ swift::extractUserModuleVersionFromInterface(StringRef moduleInterfacePath) {
14781478
return result;
14791479
}
14801480

1481-
std::string swift::extractEmbeddedBridgingHeaderContent(
1482-
std::unique_ptr<llvm::MemoryBuffer> file, ASTContext &Context) {
1481+
std::unique_ptr<llvm::MemoryBuffer> swift::extractEmbeddedBridgingHeaderContent(
1482+
std::unique_ptr<llvm::MemoryBuffer> file, StringRef headerPath,
1483+
ASTContext &Context) {
14831484
std::shared_ptr<const ModuleFileSharedCore> loadedModuleFile;
14841485
serialization::ValidationInfo loadInfo = ModuleFileSharedCore::load(
14851486
"", "", std::move(file), nullptr, nullptr, false,
@@ -1489,9 +1490,10 @@ std::string swift::extractEmbeddedBridgingHeaderContent(
14891490
loadedModuleFile);
14901491

14911492
if (loadInfo.status != serialization::Status::Valid)
1492-
return {};
1493+
return nullptr;;
14931494

1494-
return loadedModuleFile->getEmbeddedHeader();
1495+
return llvm::MemoryBuffer::getMemBufferCopy(
1496+
loadedModuleFile->getEmbeddedHeader(), headerPath);
14951497
}
14961498

14971499
bool SerializedModuleLoaderBase::canImportModule(

test/CAS/Inputs/BuildCommandExtractor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def printCmd(cmd):
3131
cmd = info["bridgingHeader"]["commandLine"][1:]
3232
printCmd(cmd)
3333
# print input file name.
34-
print(info["chainedBridgingHeaderPath"])
34+
if "chainedBridgingHeaderPath" in info:
35+
print(info["chainedBridgingHeaderPath"])
3536
else:
3637
module_names = deps["modules"][::2]
3738
module_details = deps["modules"][1::2]

test/CAS/Inputs/SwiftDepsExtractor.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010
key = sys.argv[3]
1111

1212
mode = 'swift'
13+
is_bridging_header = False
1314

1415
if module_name.startswith('clang:'):
1516
mode = 'clang'
1617
module_name = module_name[6:]
1718
elif module_name.startswith('swiftPrebuiltExternal:'):
1819
mode = 'swiftPrebuiltExternal'
1920
module_name = module_name[22:]
21+
elif module_name.startswith('bridgingHeader:'):
22+
is_bridging_header = True
23+
module_name = module_name[15:]
2024

2125
with open(input_json, 'r') as file:
2226
deps = json.load(file)
@@ -30,5 +34,10 @@
3034
json.dump(detail[key], sys.stdout, indent=2)
3135
break
3236

37+
if is_bridging_header:
38+
json.dump(detail['details'][mode]['bridgingHeader']
39+
[key], sys.stdout, indent=2)
40+
break
41+
3342
json.dump(detail['details'][mode][key], sys.stdout, indent=2)
3443
break
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
/// Test prefix mapped bridging header path will result in the same cache key.
5+
6+
// RUN: %target-swift-frontend -scan-dependencies -module-name Test -module-cache-path %t/clang-module-cache -O \
7+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
8+
// RUN: %t/test.swift -o %t/deps-1.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \
9+
// RUN: -scanner-prefix-map-paths %swift_src_root /^src -scanner-prefix-map-paths %t /^tmp \
10+
// RUN: -scanner-prefix-map-paths %t/header-1 /^header \
11+
// RUN: -scanner-output-dir %t/header-1 -import-objc-header %t/header-1/Bridging.h
12+
13+
// RUN: %target-swift-frontend -scan-dependencies -module-name Test -module-cache-path %t/clang-module-cache -O \
14+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
15+
// RUN: %t/test.swift -o %t/deps-2.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \
16+
// RUN: -scanner-prefix-map-paths %swift_src_root /^src -scanner-prefix-map-paths %t /^tmp \
17+
// RUN: -scanner-prefix-map-paths %t/header-2 /^header \
18+
// RUN: -scanner-output-dir %t/header-2 -import-objc-header %t/header-2/Bridging.h
19+
20+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-1.json bridgingHeader:Test includeTree > %t/includeTree-1.casid
21+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-2.json bridgingHeader:Test includeTree > %t/includeTree-2.casid
22+
// RUN: diff %t/includeTree-1.casid %t/includeTree-2.casid
23+
24+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-1.json Test casFSRootID > %t/root-1.casid
25+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-2.json Test casFSRootID > %t/root-2.casid
26+
// RUN: diff %t/root-1.casid %t/root-2.casid
27+
28+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps-1.json clang:SwiftShims > %t/shim.cmd
29+
// RUN: %swift_frontend_plain @%t/shim.cmd
30+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps-1.json bridgingHeader > %t/header.cmd
31+
// RUN: %target-swift-frontend @%t/header.cmd /^header/Bridging.h -disable-implicit-swift-modules -O -o %t/bridging.pch
32+
// RUN: %cache-tool -cas-path %t/cas -cache-tool-action print-output-keys -- \
33+
// RUN: %target-swift-frontend @%t/header.cmd /^header/Bridging.h -disable-implicit-swift-modules -O -o %t/bridging.pch > %t/keys.json
34+
// RUN: %{python} %S/Inputs/ExtractOutputKey.py %t/keys.json > %t/key
35+
36+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps-1.json Test > %t/MyApp.cmd
37+
// RUN: echo "\"-disable-implicit-string-processing-module-import\"" >> %t/MyApp.cmd
38+
// RUN: echo "\"-disable-implicit-concurrency-module-import\"" >> %t/MyApp.cmd
39+
// RUN: echo "\"-disable-implicit-swift-modules\"" >> %t/MyApp.cmd
40+
// RUN: echo "\"-import-objc-header\"" >> %t/MyApp.cmd
41+
// RUN: echo "\"/^header/Bridging.h\"" >> %t/MyApp.cmd
42+
// RUN: echo "\"-import-pch\"" >> %t/MyApp.cmd
43+
// RUN: echo "\"%t/bridging.pch\"" >> %t/MyApp.cmd
44+
// RUN: echo "\"-bridging-header-pch-key\"" >> %t/MyApp.cmd
45+
// RUN: echo "\"@%t/key\"" >> %t/MyApp.cmd
46+
// RUN: echo "\"-explicit-swift-module-map-file\"" >> %t/MyApp.cmd
47+
// RUN: echo "\"@%t/map.casid\"" >> %t/MyApp.cmd
48+
// RUN: %{python} %S/Inputs/GenerateExplicitModuleMap.py %t/deps-1.json > %t/map.json
49+
// RUN: llvm-cas --cas %t/cas --make-blob --data %t/map.json > %t/map.casid
50+
// RUN: %target-swift-frontend -cache-compile-job -module-name Test -O -cas-path %t/cas @%t/MyApp.cmd /^tmp/test.swift \
51+
// RUN: -emit-module -o %t/Test.swiftmodule
52+
53+
// RUN: %target-swift-frontend -scan-dependencies -module-name User -module-cache-path %t/clang-module-cache -O \
54+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
55+
// RUN: %t/user.swift -o %t/deps-3.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \
56+
// RUN: -scanner-prefix-map-paths %swift_src_root /^src -scanner-prefix-map-paths %t /^tmp \
57+
// RUN: -scanner-prefix-map-paths %t/header-1 /^header \
58+
// RUN: -scanner-output-dir %t/header-1 -I %t
59+
60+
// RUN: %target-swift-frontend -scan-dependencies -module-name User -module-cache-path %t/clang-module-cache -O \
61+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
62+
// RUN: %t/user.swift -o %t/deps-4.json -auto-bridging-header-chaining -cache-compile-job -cas-path %t/cas \
63+
// RUN: -scanner-prefix-map-paths %swift_src_root /^src -scanner-prefix-map-paths %t /^tmp \
64+
// RUN: -scanner-prefix-map-paths %t/header-2 /^header \
65+
// RUN: -scanner-output-dir %t/header-2 -I %t
66+
67+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-3.json bridgingHeader:Test includeTree > %t/includeTree-3.casid
68+
// RUN: %{python} %S/Inputs/SwiftDepsExtractor.py %t/deps-4.json bridgingHeader:Test includeTree > %t/includeTree-4.casid
69+
// RUN: diff %t/includeTree-3.casid %t/includeTree-4.casid
70+
71+
72+
73+
//--- test.swift
74+
public func test() {
75+
b()
76+
}
77+
78+
//--- user.swift
79+
import Test
80+
81+
//--- header-1/Bridging.h
82+
#include "Foo.h"
83+
//--- header-1/Foo.h
84+
void b(void);
85+
//--- header-2/Bridging.h
86+
#include "Foo.h"
87+
//--- header-2/Foo.h
88+
void b(void);

0 commit comments

Comments
 (0)