Skip to content

Commit 9e78be1

Browse files
authored
Merge pull request #1375 from artemcm/TransitivePCHDepsOfBinaryModules
[Explicit Module Builds] Add support for header dependencies of binary Swift module dependencies
2 parents 80b467f + 754672e commit 9e78be1

File tree

8 files changed

+137
-18
lines changed

8 files changed

+137
-18
lines changed

Sources/CSwiftScan/include/swiftscan_header.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ typedef struct {
132132
(*swiftscan_swift_binary_detail_get_module_doc_path)(swiftscan_module_details_t);
133133
swiftscan_string_ref_t
134134
(*swiftscan_swift_binary_detail_get_module_source_info_path)(swiftscan_module_details_t);
135+
swiftscan_string_set_t *
136+
(*swiftscan_swift_binary_detail_get_header_dependencies)(swiftscan_module_details_t);
135137
bool
136138
(*swiftscan_swift_binary_detail_get_is_framework)(swiftscan_module_details_t);
137139

Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
234234
for dependencyModule in swiftDependencyArtifacts {
235235
inputs.append(TypedVirtualPath(file: dependencyModule.modulePath.path,
236236
type: .swiftModule))
237+
238+
for headerDep in dependencyModule.prebuiltHeaderDependencyPaths ?? [] {
239+
commandLine.appendFlags(["-Xcc", "-include-pch", "-Xcc"])
240+
commandLine.appendPath(VirtualPath.lookup(headerDep.path))
241+
inputs.append(TypedVirtualPath(file: headerDep.path, type: .pch))
242+
}
237243
}
238244

239245
// Clang module dependencies are specified on the command line explicitly
@@ -301,6 +307,7 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT
301307
swiftDependencyArtifacts.append(
302308
SwiftModuleArtifactInfo(name: dependencyId.moduleName,
303309
modulePath: TextualVirtualPath(path: swiftModulePath.fileHandle),
310+
headerDependencies: prebuiltModuleDetails.headerDependencyPaths,
304311
isFramework: isFramework))
305312
case .swiftPlaceholder:
306313
fatalError("Unresolved placeholder dependencies at planning stage: \(dependencyId) of \(moduleId)")

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyGraph.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,21 @@ public struct SwiftPrebuiltExternalModuleDetails: Codable {
146146
/// The path to the .swiftSourceInfo file.
147147
public var moduleSourceInfoPath: TextualVirtualPath?
148148

149+
/// The paths to the binary module's header dependencies
150+
public var headerDependencyPaths: [TextualVirtualPath]?
151+
149152
/// A flag to indicate whether or not this module is a framework.
150153
public var isFramework: Bool?
151154

152155
public init(compiledModulePath: TextualVirtualPath,
153156
moduleDocPath: TextualVirtualPath? = nil,
154157
moduleSourceInfoPath: TextualVirtualPath? = nil,
158+
headerDependencies: [TextualVirtualPath]? = nil,
155159
isFramework: Bool) throws {
156160
self.compiledModulePath = compiledModulePath
157161
self.moduleDocPath = moduleDocPath
158162
self.moduleSourceInfoPath = moduleSourceInfoPath
163+
self.headerDependencyPaths = headerDependencies
159164
self.isFramework = isFramework
160165
}
161166
}

Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,23 @@ public class InterModuleDependencyOracle {
132132

133133
@_spi(Testing) public func supportsScannerDiagnostics() throws -> Bool {
134134
guard let swiftScan = swiftScanLibInstance else {
135-
fatalError("Attempting to reset scanner cache with no scanner instance.")
135+
fatalError("Attempting to query supported scanner API with no scanner instance.")
136+
}
137+
return swiftScan.supportsScannerDiagnostics
138+
}
139+
140+
@_spi(Testing) public func supportsBinaryModuleHeaderDependencies() throws -> Bool {
141+
guard let swiftScan = swiftScanLibInstance else {
142+
fatalError("Attempting to query supported scanner API with no scanner instance.")
136143
}
137-
return swiftScan.supportsScannerDiagnostics()
144+
return swiftScan.supportsBinaryModuleHeaderDependencies
138145
}
139146

140147
@_spi(Testing) public func getScannerDiagnostics() throws -> [ScannerDiagnosticPayload]? {
141148
guard let swiftScan = swiftScanLibInstance else {
142149
fatalError("Attempting to reset scanner cache with no scanner instance.")
143150
}
144-
guard swiftScan.supportsScannerDiagnostics() else {
151+
guard swiftScan.supportsScannerDiagnostics else {
145152
return nil
146153
}
147154
let diags = try swiftScan.queryScannerDiagnostics()

Sources/SwiftDriver/ExplicitModuleBuilds/SerializableModuleArtifacts.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,19 @@
2424
public let docPath: TextualVirtualPath?
2525
/// The path for the module's .swiftsourceinfo file
2626
public let sourceInfoPath: TextualVirtualPath?
27+
/// Header dependencies of this module
28+
public let prebuiltHeaderDependencyPaths: [TextualVirtualPath]?
2729
/// A flag to indicate whether this module is a framework
2830
public let isFramework: Bool
2931

3032
init(name: String, modulePath: TextualVirtualPath, docPath: TextualVirtualPath? = nil,
31-
sourceInfoPath: TextualVirtualPath? = nil, isFramework: Bool = false) {
33+
sourceInfoPath: TextualVirtualPath? = nil, headerDependencies: [TextualVirtualPath]? = nil,
34+
isFramework: Bool = false) {
3235
self.moduleName = name
3336
self.modulePath = modulePath
3437
self.docPath = docPath
3538
self.sourceInfoPath = sourceInfoPath
39+
self.prebuiltHeaderDependencyPaths = headerDependencies
3640
self.isFramework = isFramework
3741
}
3842
}

Sources/SwiftDriver/SwiftScan/DependencyGraphBuilder.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ private extension SwiftScan {
195195

196196
// Decode all dependencies of this module
197197
let swiftOverlayDependencies: [ModuleDependencyId]?
198-
if supportsSeparateSwiftOverlayDependencies(),
198+
if supportsSeparateSwiftOverlayDependencies,
199199
let encodedOverlayDepsRef = api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies(moduleDetailsRef) {
200200
let encodedOverlayDependencies = try toSwiftStringArray(encodedOverlayDepsRef.pointee)
201201
swiftOverlayDependencies =
@@ -228,6 +228,14 @@ private extension SwiftScan {
228228
try getOptionalPathDetail(from: moduleDetailsRef,
229229
using: api.swiftscan_swift_binary_detail_get_module_source_info_path)
230230

231+
let headerDependencies: [TextualVirtualPath]?
232+
if supportsBinaryModuleHeaderDependencies {
233+
headerDependencies = try getOptionalPathArrayDetail(from: moduleDetailsRef,
234+
using: api.swiftscan_swift_binary_detail_get_header_dependencies)
235+
} else {
236+
headerDependencies = nil
237+
}
238+
231239
let isFramework: Bool
232240
if hasBinarySwiftModuleIsFramework {
233241
isFramework = api.swiftscan_swift_binary_detail_get_is_framework(moduleDetailsRef)
@@ -238,6 +246,7 @@ private extension SwiftScan {
238246
return try SwiftPrebuiltExternalModuleDetails(compiledModulePath: compiledModulePath,
239247
moduleDocPath: moduleDocPath,
240248
moduleSourceInfoPath: moduleSourceInfoPath,
249+
headerDependencies: headerDependencies,
241250
isFramework: isFramework)
242251
}
243252

Sources/SwiftDriver/SwiftScan/SwiftScan.swift

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -250,34 +250,39 @@ internal extension swiftscan_diagnostic_severity_t {
250250
api.swiftscan_clang_detail_get_captured_pcm_args != nil
251251
}
252252

253-
func serializeScannerCache(to path: AbsolutePath) {
254-
api.swiftscan_scanner_cache_serialize(scanner,
255-
path.description.cString(using: String.Encoding.utf8))
253+
@_spi(Testing) public var supportsBinaryModuleHeaderDependencies : Bool {
254+
return api.swiftscan_swift_binary_detail_get_header_dependencies != nil
256255
}
257256

258-
func loadScannerCache(from path: AbsolutePath) -> Bool {
259-
return api.swiftscan_scanner_cache_load(scanner,
260-
path.description.cString(using: String.Encoding.utf8))
257+
@_spi(Testing) public var supportsStringDispose : Bool {
258+
return api.swiftscan_string_dispose != nil
261259
}
262260

263-
func resetScannerCache() {
264-
api.swiftscan_scanner_cache_reset(scanner)
265-
}
266261

267-
@_spi(Testing) public func supportsSeparateSwiftOverlayDependencies() -> Bool {
262+
@_spi(Testing) public var supportsSeparateSwiftOverlayDependencies : Bool {
268263
return api.swiftscan_swift_textual_detail_get_swift_overlay_dependencies != nil
269264
}
270265

271-
@_spi(Testing) public func supportsScannerDiagnostics() -> Bool {
266+
@_spi(Testing) public var supportsScannerDiagnostics : Bool {
272267
return api.swiftscan_scanner_diagnostics_query != nil &&
273268
api.swiftscan_scanner_diagnostics_reset != nil &&
274269
api.swiftscan_diagnostic_get_message != nil &&
275270
api.swiftscan_diagnostic_get_severity != nil &&
276271
api.swiftscan_diagnostics_set_dispose != nil
277272
}
278273

279-
@_spi(Testing) public func supportsStringDispose() -> Bool {
280-
return api.swiftscan_string_dispose != nil
274+
func serializeScannerCache(to path: AbsolutePath) {
275+
api.swiftscan_scanner_cache_serialize(scanner,
276+
path.description.cString(using: String.Encoding.utf8))
277+
}
278+
279+
func loadScannerCache(from path: AbsolutePath) -> Bool {
280+
return api.swiftscan_scanner_cache_load(scanner,
281+
path.description.cString(using: String.Encoding.utf8))
282+
}
283+
284+
func resetScannerCache() {
285+
api.swiftscan_scanner_cache_reset(scanner)
281286
}
282287

283288
@_spi(Testing) public func queryScannerDiagnostics() throws -> [ScannerDiagnosticPayload] {
@@ -427,6 +432,10 @@ private extension swiftscan_functions_t {
427432
self.swiftscan_swift_textual_detail_get_swift_overlay_dependencies =
428433
try loadOptional("swiftscan_swift_textual_detail_get_swift_overlay_dependencies")
429434

435+
// Header dependencies of binary modules
436+
self.swiftscan_swift_binary_detail_get_header_dependencies =
437+
try loadOptional("swiftscan_swift_binary_detail_get_header_dependencies")
438+
430439
// MARK: Required Methods
431440
func loadRequired<T>(_ symbol: String) throws -> T {
432441
guard let sym: T = Loader.lookup(symbol: symbol, in: swiftscan) else {

Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,82 @@ final class ExplicitModuleBuildTests: XCTestCase {
905905
}
906906
}
907907

908+
func testExplicitModuleBuildEndToEndWithBinaryHeaderDeps() throws {
909+
try withTemporaryDirectory { path in
910+
try localFileSystem.changeCurrentWorkingDirectory(to: path)
911+
let moduleCachePath = path.appending(component: "ModuleCache")
912+
try localFileSystem.createDirectory(moduleCachePath)
913+
let PCHPath = path.appending(component: "PCH")
914+
try localFileSystem.createDirectory(PCHPath)
915+
let FooInstallPath = path.appending(component: "Foo")
916+
try localFileSystem.createDirectory(FooInstallPath)
917+
let foo = path.appending(component: "foo.swift")
918+
try localFileSystem.writeFileContents(foo) {
919+
$0 <<< "extension Profiler {"
920+
$0 <<< " public static let count: Int = 42"
921+
$0 <<< "}"
922+
}
923+
let fooHeader = path.appending(component: "foo.h")
924+
try localFileSystem.writeFileContents(fooHeader) {
925+
$0 <<< "struct Profiler { void* ptr; };"
926+
}
927+
let main = path.appending(component: "main.swift")
928+
try localFileSystem.writeFileContents(main) {
929+
$0 <<< "import Foo"
930+
}
931+
let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? []
932+
933+
var fooBuildDriver = try Driver(args: ["swiftc",
934+
"-explicit-module-build",
935+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
936+
"-working-directory", path.nativePathString(escaped: true),
937+
foo.nativePathString(escaped: true),
938+
"-emit-module", "-wmo", "-module-name", "Foo",
939+
"-emit-module-path", FooInstallPath.nativePathString(escaped: true),
940+
"-import-objc-header", fooHeader.nativePathString(escaped: true),
941+
"-pch-output-dir", PCHPath.nativePathString(escaped: true),
942+
FooInstallPath.appending(component: "Foo.swiftmodule").nativePathString(escaped: true)]
943+
+ sdkArgumentsForTesting,
944+
env: ProcessEnv.vars)
945+
946+
// Ensure this tooling supports this functionality
947+
let dependencyOracle = InterModuleDependencyOracle()
948+
let scanLibPath = try XCTUnwrap(fooBuildDriver.toolchain.lookupSwiftScanLib())
949+
guard try dependencyOracle
950+
.verifyOrCreateScannerInstance(fileSystem: localFileSystem,
951+
swiftScanLibPath: scanLibPath) else {
952+
XCTFail("Dependency scanner library not found")
953+
return
954+
}
955+
guard try dependencyOracle.supportsBinaryModuleHeaderDependencies() else {
956+
throw XCTSkip("libSwiftScan does not support binary module header dependencies.")
957+
}
958+
959+
let fooJobs = try fooBuildDriver.planBuild()
960+
try fooBuildDriver.run(jobs: fooJobs)
961+
XCTAssertFalse(fooBuildDriver.diagnosticEngine.hasErrors)
962+
963+
var driver = try Driver(args: ["swiftc",
964+
"-I", FooInstallPath.nativePathString(escaped: true),
965+
"-explicit-module-build", "-emit-module", "-emit-module-path",
966+
path.appending(component: "testEMBETEWBHD.swiftmodule").nativePathString(escaped: true),
967+
"-module-cache-path", moduleCachePath.nativePathString(escaped: true),
968+
"-working-directory", path.nativePathString(escaped: true),
969+
main.nativePathString(escaped: true)] + sdkArgumentsForTesting,
970+
env: ProcessEnv.vars)
971+
let jobs = try driver.planBuild()
972+
let compileJob = try XCTUnwrap(jobs.first(where: { $0.description == "Compiling main main.swift" }))
973+
974+
// Ensure the header dependency of Foo shows up on client compile commands
975+
XCTAssertTrue(compileJob.commandLine.contains(subsequence: [.flag("-Xcc"),
976+
.flag("-include-pch"),
977+
.flag("-Xcc"),
978+
.path(.absolute(PCHPath.appending(component: "foo.pch")))]))
979+
try driver.run(jobs: jobs)
980+
XCTAssertFalse(driver.diagnosticEngine.hasErrors)
981+
}
982+
}
983+
908984
func testExplicitModuleBuildEndToEnd() throws {
909985
try withTemporaryDirectory { path in
910986
try localFileSystem.changeCurrentWorkingDirectory(to: path)

0 commit comments

Comments
 (0)