[Dependency Scanning] Scan embedded header content if file doesn't exist

Fallback to scan embedded header content when the bridging header path
encoded in the swift binary module doesn't exit on the disk.

rdar://144261730
This commit is contained in:
Steven Wu
2025-02-05 15:30:23 -08:00
parent e17556a549
commit 0e8d794ec8
2 changed files with 126 additions and 6 deletions

View File

@@ -36,6 +36,7 @@
#include "llvm/CAS/CachingOnDiskFileSystem.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/MemoryBufferRef.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/VersionTuple.h"
@@ -1063,6 +1064,42 @@ void ModuleDependencyScanner::resolveHeaderDependenciesForModule(
if (!isTextualModuleWithABridgingHeader && !isBinaryModuleWithHeaderInput)
return;
std::optional<std::string> headerPath;
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer;
std::optional<llvm::MemoryBufferRef> sourceBufferRef;
auto extractHeaderContent =
[&](const SwiftBinaryModuleDependencyStorage &binaryMod)
-> std::unique_ptr<llvm::MemoryBuffer> {
auto header = binaryMod.headerImport;
// Check to see if the header input exists on disk.
auto FS = ScanASTContext.SourceMgr.getFileSystem();
if (FS->exists(header))
return nullptr;
auto moduleBuf = FS->getBufferForFile(binaryMod.compiledModulePath);
if (!moduleBuf)
return nullptr;
auto content = extractEmbeddedBridgingHeaderContent(std::move(*moduleBuf),
ScanASTContext);
if (content.empty())
return nullptr;
return llvm::MemoryBuffer::getMemBufferCopy(content, header);
};
if (isBinaryModuleWithHeaderInput) {
auto &binaryMod = *moduleDependencyInfo.getAsSwiftBinaryModule();
if (auto embeddedHeader = extractHeaderContent(binaryMod)) {
sourceBuffer = std::move(embeddedHeader);
sourceBufferRef = sourceBuffer->getMemBufferRef();
} else
headerPath = binaryMod.headerImport;
} else
headerPath = *moduleDependencyInfo.getBridgingHeader();
withDependencyScanningWorker(
[&](ModuleDependencyScanningWorker *ScanningWorker) {
auto clangImporter = static_cast<ClangImporter *>(
@@ -1072,12 +1109,9 @@ void ModuleDependencyScanner::resolveHeaderDependenciesForModule(
std::optional<std::string> includeTreeID;
std::vector<std::string> bridgingHeaderCommandLine;
auto headerScan = clangImporter->getHeaderDependencies(
moduleID,
isTextualModuleWithABridgingHeader
? *moduleDependencyInfo.getBridgingHeader()
: moduleDependencyInfo.getAsSwiftBinaryModule()->headerImport,
/*sourceBuffer=*/std::nullopt, ScanningWorker->clangScanningTool,
cache, headerClangModuleDependencies, headerFileInputs,
moduleID, headerPath, sourceBufferRef,
ScanningWorker->clangScanningTool, cache,
headerClangModuleDependencies, headerFileInputs,
bridgingHeaderCommandLine, includeTreeID);
if (!headerScan) {
// Record direct header Clang dependencies

View File

@@ -0,0 +1,86 @@
// REQUIRES: objc_interop
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module -module-name Test -module-cache-path %t/clang-module-cache -O \
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
// RUN: %t/test.swift -o %t/Test.swiftmodule \
// RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -import-objc-header %t/Bridging.h
// 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.json \
// RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -I %t
/// Remove bridging header from disk and rescan
// RUN: rm -rf %t/Bridging.h %t/Foo.h %t/Foo2.h
// 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/deps2.json \
// RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -I %t
// RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps.json swiftPrebuiltExternal:Test headerModuleDependencies | %FileCheck %s --check-prefix=MODULE
// RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps.json swiftPrebuiltExternal:Test headerDependenciesSourceFiles | %FileCheck %s --check-prefix=FILE
// RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps2.json swiftPrebuiltExternal:Test headerModuleDependencies | %FileCheck %s --check-prefix=MODULE
// RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps2.json swiftPrebuiltExternal:Test headerDependenciesSourceFiles | %FileCheck %s --check-prefix=FILE
// MODULE: "A"
// FILE: Bridging.h
//--- test.swift
public func test() {
b()
}
public class TestB: B {}
//--- user.swift
import Test
func user() {
var b: TestB
test()
}
extension A {
public func testA() {}
}
//--- Bridging.h
#include "Foo.h"
#include "Foo2.h"
//--- Foo.h
#import "a.h"
#ifndef IMPORT_FOO
#define IMPORT_FOO
int Foo = 0;
#endif
//--- Foo2.h
#ifndef IMPORT_FOO2
#define IMPORT_FOO2
int Foo2 = 0;
#endif
//--- a.h
#include "b.h"
struct A {
int a;
};
//--- b.h
void b(void);
@interface B
@end
//--- a.modulemap
module A {
header "a.h"
export *
}
//--- b.modulemap
module B {
header "b.h"
export *
}