Skip to content

Commit 6750c3b

Browse files
committed
Fix linker swiftmodule registration in incremental builds
rdar://149248232
1 parent 4c10712 commit 6750c3b

File tree

3 files changed

+136
-7
lines changed

3 files changed

+136
-7
lines changed

Sources/SWBCore/LibSwiftDriver/LibSwiftDriver.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,38 @@ public final class SwiftModuleDependencyGraph: SwiftGlobalExplicitDependencyGrap
177177
}
178178
}
179179

180+
public func querySwiftmodulesNeedingRegistrationForDebugging(for key: String) throws -> [String] {
181+
let graph = try registryQueue.blocking_sync {
182+
guard let driver = registry[key] else {
183+
throw StubError.error("Unable to find jobs for key \(key). Be sure to plan the build ahead of fetching results.")
184+
}
185+
return driver.intermoduleDependencyGraph
186+
}
187+
guard let graph else { return [] }
188+
let directDependencies = graph.mainModule.directDependencies ?? []
189+
let transitiveDependencies = directDependencies + SWBUtil.transitiveClosure(directDependencies, successors: { moduleID in graph.modules[moduleID]?.directDependencies ?? [] }).0
190+
var swiftmodulePaths: [String] = []
191+
swiftmodulePaths.reserveCapacity(transitiveDependencies.count)
192+
for dependency in transitiveDependencies {
193+
guard let moduleInfo = graph.modules[dependency] else {
194+
continue
195+
}
196+
switch moduleInfo.details {
197+
case .swift:
198+
if let modulePath = VirtualPath.lookup(moduleInfo.modulePath.path).absolutePath {
199+
swiftmodulePaths.append(modulePath.pathString)
200+
}
201+
case .swiftPrebuiltExternal(let details):
202+
if let modulePath = VirtualPath.lookup(details.compiledModulePath.path).absolutePath {
203+
swiftmodulePaths.append(modulePath.pathString)
204+
}
205+
case .clang, .swiftPlaceholder:
206+
break
207+
}
208+
}
209+
return swiftmodulePaths
210+
}
211+
180212
public func queryPlanningDependencies(for key: String) throws -> [String] {
181213
let graph = try registryQueue.blocking_sync {
182214
guard let driver = registry[key] else {

Sources/SWBTaskExecution/TaskActions/SwiftDriverTaskAction.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,9 @@ final public class SwiftDriverTaskAction: TaskAction, BuildValueValidatingTaskAc
109109

110110
if let linkerResponseFilePath = driverPayload.linkerResponseFilePath {
111111
var responseFileCommandLine: [String] = []
112-
let plannedBuild = try dependencyGraph.queryPlannedBuild(for: driverPayload.uniqueID)
113112
if driverPayload.explicitModulesEnabled {
114-
for job in plannedBuild.explicitModulesPlannedDriverJobs() {
115-
for output in job.driverJob.outputs {
116-
if output.fileExtension == "swiftmodule" {
117-
responseFileCommandLine.append(contentsOf: ["-Xlinker", "-add_ast_path", "-Xlinker", "\(output.str)"])
118-
}
119-
}
113+
for swiftmodulePath in try dependencyGraph.querySwiftmodulesNeedingRegistrationForDebugging(for: driverPayload.uniqueID) {
114+
responseFileCommandLine.append(contentsOf: ["-Xlinker", "-add_ast_path", "-Xlinker", "\(swiftmodulePath)"])
120115
}
121116
}
122117
let contents = ByteString(encodingAsUTF8: ResponseFiles.responseFileContents(args: responseFileCommandLine))

Tests/SWBBuildSystemTests/SwiftDriverTests.swift

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5041,4 +5041,106 @@ fileprivate struct SwiftDriverTests: CoreBasedTests {
50415041
}
50425042
}
50435043
}
5044+
5045+
@Test(.requireSDKs(.macOS))
5046+
func incrementalExplicitModulesLinkerSwiftmoduleRegistration() async throws {
5047+
try await withTemporaryDirectory { tmpDirPath async throws -> Void in
5048+
let testWorkspace = try await TestWorkspace(
5049+
"Test",
5050+
sourceRoot: tmpDirPath.join("Test"),
5051+
projects: [
5052+
TestProject(
5053+
"aProject",
5054+
groupTree: TestGroup(
5055+
"Sources",
5056+
path: "Sources",
5057+
children: [
5058+
TestFile("fileA1.swift"),
5059+
]),
5060+
buildConfigurations: [
5061+
TestBuildConfiguration(
5062+
"Debug",
5063+
buildSettings: [
5064+
"PRODUCT_NAME": "$(TARGET_NAME)",
5065+
"SWIFT_VERSION": swiftVersion,
5066+
"BUILD_VARIANTS": "normal",
5067+
"SWIFT_USE_INTEGRATED_DRIVER": "YES",
5068+
"SWIFT_ENABLE_EXPLICIT_MODULES": "YES",
5069+
])
5070+
],
5071+
targets: [
5072+
TestStandardTarget(
5073+
"TargetA",
5074+
type: .framework,
5075+
buildPhases: [
5076+
TestSourcesBuildPhase([
5077+
"fileA1.swift",
5078+
]),
5079+
]),
5080+
])
5081+
])
5082+
5083+
let tester = try await BuildOperationTester(getCore(), testWorkspace, simulated: false)
5084+
tester.userInfo = tester.userInfo.withAdditionalEnvironment(environment: ["SWIFT_FORCE_MODULE_LOADING": "only-interface"])
5085+
let parameters = BuildParameters(configuration: "Debug", overrides: [
5086+
// Redirect the prebuilt cache so we always build modules from source
5087+
"SWIFT_OVERLOAD_PREBUILT_MODULE_CACHE_PATH": tmpDirPath.str
5088+
])
5089+
let buildRequest = BuildRequest(parameters: parameters, buildTargets: tester.workspace.projects[0].targets.map({ BuildRequest.BuildTargetInfo(parameters: parameters, target: $0) }), continueBuildingAfterErrors: false, useParallelTargets: true, useImplicitDependencies: false, useDryRun: false)
5090+
let SRCROOT = testWorkspace.sourceRoot.join("aProject")
5091+
5092+
// Create the source files.
5093+
try await tester.fs.writeFileContents(SRCROOT.join("Sources/fileA1.swift")) { file in
5094+
file <<<
5095+
"""
5096+
public struct A {
5097+
public init() { }
5098+
}
5099+
"""
5100+
}
5101+
5102+
var cleanResponseFileContents: ByteString? = nil
5103+
try await tester.checkBuild(runDestination: .macOS, buildRequest: buildRequest, persistent: true) { results in
5104+
var responseFile: Path? = nil
5105+
results.checkTask(.matchRuleType("SwiftDriver Compilation Requirements")) { driverTask in
5106+
responseFile = driverTask.outputPaths.filter { $0.str.hasSuffix("-linker-args.resp") }.only
5107+
}
5108+
try results.checkTask(.matchRuleType("Ld")) { linkTask in
5109+
linkTask.checkCommandLineContains("@\(try #require(responseFile).str)")
5110+
}
5111+
let responseFileContents = try tester.fs.read(try #require(responseFile))
5112+
#expect(!responseFileContents.isEmpty)
5113+
cleanResponseFileContents = responseFileContents
5114+
}
5115+
5116+
try await tester.fs.writeFileContents(SRCROOT.join("Sources/fileA1.swift")) { file in
5117+
file <<<
5118+
"""
5119+
public struct A {
5120+
public init() { }
5121+
}
5122+
5123+
public func foo() { }
5124+
"""
5125+
}
5126+
5127+
var incrementalResponseFileContents: ByteString? = nil
5128+
try await tester.checkBuild(runDestination: .macOS, buildRequest: buildRequest, persistent: true) { results in
5129+
var responseFile: Path? = nil
5130+
results.checkTask(.matchRuleType("SwiftDriver Compilation Requirements")) { driverTask in
5131+
responseFile = driverTask.outputPaths.filter { $0.str.hasSuffix("-linker-args.resp") }.only
5132+
}
5133+
try results.checkTask(.matchRuleType("Ld")) { linkTask in
5134+
linkTask.checkCommandLineContains("@\(try #require(responseFile).str)")
5135+
}
5136+
let responseFileContents = try tester.fs.read(try #require(responseFile))
5137+
#expect(!responseFileContents.isEmpty)
5138+
incrementalResponseFileContents = responseFileContents
5139+
}
5140+
5141+
let cleanContents = try #require(cleanResponseFileContents)
5142+
let incrementalContents = try #require(incrementalResponseFileContents)
5143+
#expect(cleanContents == incrementalContents)
5144+
}
5145+
}
50445146
}

0 commit comments

Comments
 (0)