Skip to content

Commit 331fa5c

Browse files
committed
[Build] Fix debugging for products built by SwiftPM
This implements proper debugging support on both Darwin and Linux. On Darwin, this means SwiftPM will pass the swiftmodule file to the linker and on Linux, it'll link the wrapped module obtained using Swift compiler's modulewrap subtool. <rdar://problem/29228963> <rdar://problem/52118932> https://bugs.swift.org/browse/SR-3280 This is a cherry-pick commit for <rdar://problem/56673855>
1 parent 790a0c9 commit 331fa5c

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

Sources/Build/BuildPlan.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,29 @@ public struct BuildParameters {
223223
fileprivate func createScope(for target: ResolvedTarget) -> BuildSettings.Scope {
224224
return BuildSettings.Scope(target.underlyingTarget.buildSettings, boundCondition: (currentPlatform, configuration))
225225
}
226+
227+
/// Represents the debugging strategy.
228+
///
229+
/// Swift binaries requires the swiftmodule files in order for lldb to work.
230+
/// On Darwin, linker can directly take the swiftmodule file path using the
231+
/// -add_ast_path flag. On other platforms, we convert the swiftmodule into
232+
/// an object file using Swift's modulewrap tool.
233+
enum DebuggingStrategy {
234+
case swiftAST
235+
case modulewrap
236+
}
237+
238+
/// The debugging strategy according to the current build parameters.
239+
var debuggingStrategy: DebuggingStrategy? {
240+
guard configuration == .debug else {
241+
return nil
242+
}
243+
244+
if triple.isDarwin() {
245+
return .swiftAST
246+
}
247+
return .modulewrap
248+
}
226249
}
227250

228251
/// A target description which can either be for a Swift or Clang target.
@@ -458,6 +481,13 @@ public final class SwiftTargetBuildDescription {
458481
return buildParameters.buildPath.appending(component: target.c99name + ".swiftmodule")
459482
}
460483

484+
/// The path to the wrapped swift module which is created using the modulewrap tool. This is required
485+
/// for supporting debugging on non-Darwin platforms (On Darwin, we just pass the swiftmodule to the linker
486+
/// using the `-add_ast_path` flag).
487+
var wrappedModuleOutputPath: AbsolutePath {
488+
return tempsPath.appending(component: target.c99name + ".swiftmodule.o")
489+
}
490+
461491
/// The path to the swifinterface file after compilation.
462492
var parseableModuleInterfaceOutputPath: AbsolutePath {
463493
return buildParameters.buildPath.appending(component: target.c99name + ".swiftinterface")
@@ -668,6 +698,9 @@ public final class ProductBuildDescription {
668698
/// The list of targets that are going to be linked statically in this product.
669699
fileprivate var staticTargets: [ResolvedTarget] = []
670700

701+
/// The list of Swift modules that should be passed to the linker. This is required for debugging to work.
702+
fileprivate var swiftASTs: [AbsolutePath] = []
703+
671704
/// Path to the temporary directory for this product.
672705
var tempsPath: AbsolutePath {
673706
return buildParameters.buildPath.appending(component: product.name + ".product")
@@ -790,6 +823,10 @@ public final class ProductBuildDescription {
790823
// Add arguments from declared build settings.
791824
args += self.buildSettingsFlags()
792825

826+
// Add AST paths to make the product debuggable. This array is only populated when we're
827+
// building for Darwin in debug configuration.
828+
args += swiftASTs.flatMap{ ["-Xlinker", "-add_ast_path", "-Xlinker", $0.pathString] }
829+
793830
// User arguments (from -Xlinker and -Xswiftc) should follow generated arguments to allow user overrides
794831
args += buildParameters.linkerFlags
795832
args += stripInvalidArguments(buildParameters.swiftCompilerFlags)
@@ -1096,6 +1133,28 @@ public class BuildPlan {
10961133
}
10971134
}
10981135

1136+
for target in dependencies.staticTargets {
1137+
switch target.underlyingTarget {
1138+
case is SwiftTarget:
1139+
// Swift targets are guaranteed to have a corresponding Swift description.
1140+
guard case .swift(let description) = targetMap[target]! else { fatalError() }
1141+
1142+
// Based on the debugging strategy, we either need to pass swiftmodule paths to the
1143+
// product or link in the wrapped module object. This is required for properly debugging
1144+
// Swift products. Debugging statergy is computed based on the current platform we're
1145+
// building for and is nil for the release configuration.
1146+
switch buildParameters.debuggingStrategy {
1147+
case .swiftAST:
1148+
buildProduct.swiftASTs.append(description.moduleOutputPath)
1149+
case .modulewrap:
1150+
buildProduct.objects += [description.wrappedModuleOutputPath]
1151+
case nil:
1152+
break
1153+
}
1154+
default: break
1155+
}
1156+
}
1157+
10991158
buildProduct.staticTargets = dependencies.staticTargets
11001159
buildProduct.dylibs = dependencies.dylibs.map({ productMap[$0]! })
11011160
buildProduct.objects += dependencies.staticTargets.flatMap({ targetMap[$0]!.objects })

Sources/Build/llbuild.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,20 @@ public final class LLBuildManifestGenerator {
256256
buildTarget.outputs.insert(target.moduleOutputPath.pathString)
257257
let tool = SwiftCompilerTool(target: target, inputs: inputs.values)
258258
buildTarget.cmds.insert(Command(name: target.target.getCommandName(config: buildConfig), tool: tool))
259+
260+
// Add commands to perform the module wrapping Swift modules when debugging statergy is `modulewrap`.
261+
if plan.buildParameters.debuggingStrategy == .modulewrap {
262+
let modulewrapTool = ShellTool(
263+
description: "Wrapping AST for \(target.target.name) for debugging",
264+
inputs: [target.moduleOutputPath.pathString],
265+
outputs: [target.wrappedModuleOutputPath.pathString],
266+
args: [target.buildParameters.toolchain.swiftCompiler.pathString,
267+
"-modulewrap", target.moduleOutputPath.pathString, "-o",
268+
target.wrappedModuleOutputPath.pathString],
269+
allowMissingInputs: false)
270+
buildTarget.cmds.insert(Command(name: target.wrappedModuleOutputPath.pathString, tool: modulewrapTool))
271+
}
272+
259273
return buildTarget
260274
}
261275

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ final class BuildPlanTests: XCTestCase {
114114
"-emit-executable",
115115
"@/path/to/build/debug/exe.product/Objects.LinkFileList",
116116
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
117-
"-target", "x86_64-apple-macosx10.10",
117+
"-target", "x86_64-apple-macosx10.10", "-Xlinker", "-add_ast_path",
118+
"-Xlinker", "/path/to/build/debug/exe.swiftmodule", "-Xlinker", "-add_ast_path",
119+
"-Xlinker", "/path/to/build/debug/lib.swiftmodule",
118120
]
119121
#else
120122
let linkArguments = [
@@ -457,6 +459,7 @@ final class BuildPlanTests: XCTestCase {
457459
"@/path/to/build/debug/exe.product/Objects.LinkFileList",
458460
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
459461
"-target", "x86_64-apple-macosx10.10",
462+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.swiftmodule",
460463
])
461464
#else
462465
XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [
@@ -600,6 +603,8 @@ final class BuildPlanTests: XCTestCase {
600603
"@/path/to/build/debug/PkgPackageTests.product/Objects.LinkFileList",
601604
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
602605
"-target", "x86_64-apple-macosx10.10",
606+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/FooTests.swiftmodule",
607+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/Foo.swiftmodule",
603608
])
604609
#else
605610
XCTAssertEqual(try result.buildProduct(for: "PkgPackageTests").linkArguments(), [
@@ -652,6 +657,7 @@ final class BuildPlanTests: XCTestCase {
652657
"@/path/to/build/debug/exe.product/Objects.LinkFileList",
653658
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
654659
"-target", "x86_64-apple-macosx10.10",
660+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/exe.swiftmodule",
655661
])
656662
#else
657663
XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [
@@ -745,6 +751,7 @@ final class BuildPlanTests: XCTestCase {
745751
"@/path/to/build/debug/Foo.product/Objects.LinkFileList",
746752
"-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift/macosx",
747753
"-target", "x86_64-apple-macosx10.10",
754+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/Foo.swiftmodule"
748755
])
749756

750757
XCTAssertEqual(barLinkArgs, [
@@ -753,6 +760,7 @@ final class BuildPlanTests: XCTestCase {
753760
"-module-name", "Bar_Baz", "-emit-library",
754761
"@/path/to/build/debug/Bar-Baz.product/Objects.LinkFileList",
755762
"-target", "x86_64-apple-macosx10.10",
763+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/Bar.swiftmodule"
756764
])
757765
#else
758766
XCTAssertEqual(fooLinkArgs, [
@@ -820,6 +828,7 @@ final class BuildPlanTests: XCTestCase {
820828
"-emit-library",
821829
"@/path/to/build/debug/lib.product/Objects.LinkFileList",
822830
"-target", "x86_64-apple-macosx10.10",
831+
"-Xlinker", "-add_ast_path", "-Xlinker", "/path/to/build/debug/lib.swiftmodule",
823832
]
824833
#else
825834
let linkArguments = [
@@ -1384,7 +1393,7 @@ final class BuildPlanTests: XCTestCase {
13841393
XCTAssertMatch(exe, [.anySequence, "-DFOO", "-framework", "CoreData", .end])
13851394

13861395
let linkExe = try result.buildProduct(for: "exe").linkArguments()
1387-
XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-framework", "CoreData", "-Ilfoo", "-L", "lbar", .end])
1396+
XCTAssertMatch(linkExe, [.anySequence, "-lsqlite3", "-llibz", "-framework", "CoreData", "-Ilfoo", "-L", "lbar", .anySequence])
13881397
}
13891398
}
13901399

0 commit comments

Comments
 (0)