Skip to content

Commit 300e900

Browse files
authored
Merge pull request #845 from owenv/owenv/rpath-dupe
Avoid duplicate OS rpaths when linking with swiftc and targeting older macOS
2 parents ed44f83 + 43880e9 commit 300e900

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

Sources/SWBCore/SpecImplementations/Tools/LinkerTools.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,18 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
334334
return usedTools.keys.map({ type(of: $0) }).contains(where: { $0 == SwiftCompilerSpec.self })
335335
}
336336

337-
static public func computeRPaths(_ cbc: CommandBuildContext,_ delegate: any TaskGenerationDelegate, inputRunpathSearchPaths: [String], isUsingSwift: Bool ) async -> [String] {
337+
public struct RuntimeSearchPaths {
338+
// The rpaths themselves
339+
let paths: [String]
340+
// Whether we should suppress rpaths the linker driver might otherwise insert
341+
let suppressDriverStdlibPaths: Bool
342+
}
343+
344+
static public func computeRPaths(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate, inputRunpathSearchPaths: [String], isUsingSwift: Bool ) async -> RuntimeSearchPaths {
338345
// Product types can provide their own set of rpath values, we need to ensure that our rpath flags for Swift in the OS appear before those. Also, due to the fact that we are staging this rollout, we need to specifically override any Swift libraries that may be in the bundle _when_ the Swift ABI version matches on the system with that in which the tool was built with.
339346

340347
var runpathSearchPaths = inputRunpathSearchPaths
348+
var suppressDriverStdlibPaths = false
341349
// NOTE: For swift.org toolchains, we always add the search paths to the Swift SDK location as the overlays do not have the install name set. This also works when `SWIFT_USE_DEVELOPMENT_TOOLCHAIN_RUNTIME=YES` as `DYLD_LIBRARY_PATH` is used to override these settings during debug time. If users wish to use the development runtime while not debugging, they need to manually set their rpaths as this is not a supported configuration.
342350
// Also, if the deployment target does not support Swift in the OS, the rpath entries need to be added as well.
343351
// And, if the deployment target does not support Swift Concurrency natively, then the rpath needs to be added as well so that the shim library can find the real implementation. Note that we assume `true` in the case where `supportsSwiftInTheOS` is `nil` as we don't have the platform data to make the correct choice; so fallback to existing behavior.
@@ -357,9 +365,11 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
357365
// NOTE: For swift.org toolchains, this is fine as `DYLD_LIBRARY_PATH` is used to override these settings.
358366
let swiftABIVersion = await (cbc.producer.swiftCompilerSpec.discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate) as? DiscoveredSwiftCompilerToolSpecInfo)?.swiftABIVersion
359367
runpathSearchPaths.insert( swiftABIVersion.flatMap { "/usr/lib/swift-\($0)" } ?? "/usr/lib/swift", at: 0)
368+
// Ensure the linker driver does not insert a duplicate rpath (if linking using swiftc)
369+
suppressDriverStdlibPaths = true
360370
}
361371

362-
return runpathSearchPaths
372+
return .init(paths: runpathSearchPaths, suppressDriverStdlibPaths: suppressDriverStdlibPaths)
363373
}
364374

365375
private static func swiftcSupportsLinkingMachOType(_ type: String) -> Bool {
@@ -545,8 +555,13 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
545555

546556
// See related:
547557
var runpathSearchPaths: [String] = []
558+
var stdlibRPathArgs: [String] = []
548559
if machOTypeString != "mh_object" {
549-
runpathSearchPaths = await LdLinkerSpec.computeRPaths(cbc, delegate, inputRunpathSearchPaths: cbc.scope.evaluate(BuiltinMacros.LD_RUNPATH_SEARCH_PATHS), isUsingSwift: isLinkUsingSwift)
560+
let rpaths = await LdLinkerSpec.computeRPaths(cbc, delegate, inputRunpathSearchPaths: cbc.scope.evaluate(BuiltinMacros.LD_RUNPATH_SEARCH_PATHS), isUsingSwift: isLinkUsingSwift)
561+
runpathSearchPaths = rpaths.paths
562+
if rpaths.suppressDriverStdlibPaths && cbc.scope.evaluate(BuiltinMacros.LINKER_DRIVER, lookup: linkerDriverLookup) == .swiftc {
563+
stdlibRPathArgs.append("-no-stdlib-rpath")
564+
}
550565

551566
// If we're merging libraries and we're reexporting any libraries, then add an rpath to the ReexportedBinaries directory.
552567
// This coordinates with the logic in SourcesTaskProducer which copies content to that directory.
@@ -658,6 +673,8 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
658673
// Generate the command line.
659674
var commandLine = await commandLineFromTemplate(cbc, delegate, optionContext: optionContext, specialArgs: specialArgs, lookup: lookup).map(\.asString)
660675

676+
commandLine.append(contentsOf: stdlibRPathArgs)
677+
661678
// Add flags to emit SDK imports info.
662679
let sdkImportsInfoFile = cbc.scope.evaluate(BuiltinMacros.LD_SDK_IMPORTS_FILE)
663680
let supportsSDKImportsFeature = (try? optionContext?.toolVersion >= .init("1164")) == true

Sources/SWBCore/SpecImplementations/Tools/TAPITools.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public final class TAPIToolSpec : GenericCommandLineToolSpec, GCCCompatibleCompi
7575
let scope = cbc.scope
7676
let useOnlyFilelist = scope.evaluate(BuiltinMacros.TAPI_ENABLE_PROJECT_HEADERS) || scope.evaluate(BuiltinMacros.TAPI_USE_SRCROOT)
7777

78-
let runpathSearchPaths = await LdLinkerSpec.computeRPaths(cbc, delegate, inputRunpathSearchPaths: scope.evaluate(BuiltinMacros.TAPI_RUNPATH_SEARCH_PATHS), isUsingSwift: !generatedTBDFiles.isEmpty)
78+
let runpathSearchPaths = await LdLinkerSpec.computeRPaths(cbc, delegate, inputRunpathSearchPaths: scope.evaluate(BuiltinMacros.TAPI_RUNPATH_SEARCH_PATHS), isUsingSwift: !generatedTBDFiles.isEmpty).paths
7979
let runpathSearchPathsExpr = scope.namespace.parseStringList(runpathSearchPaths)
8080

8181
// Create a lookup closure for build setting overrides.

Tests/SWBTaskConstructionTests/LinkerTaskConstructionTests.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,51 @@ fileprivate struct LinkerTaskConstructionTests: CoreBasedTests {
9494
}
9595
}
9696
}
97+
98+
@Test(.requireSDKs(.macOS))
99+
func stdlibRpathSuppression() async throws {
100+
let testProject = TestProject(
101+
"aProject",
102+
groupTree: TestGroup(
103+
"SomeFiles",
104+
children: [
105+
TestFile("s.swift"),
106+
]),
107+
buildConfigurations: [
108+
TestBuildConfiguration("Debug", buildSettings: [
109+
"PRODUCT_NAME": "$(TARGET_NAME)",
110+
"SWIFT_EXEC": try await swiftCompilerPath.str,
111+
"SWIFT_VERSION": try await swiftVersion,
112+
"MACOSX_DEPLOYMENT_TARGET": "10.13"
113+
]),
114+
],
115+
targets: [
116+
TestStandardTarget(
117+
"Library",
118+
type: .dynamicLibrary,
119+
buildConfigurations: [
120+
TestBuildConfiguration("Debug", buildSettings: [:]),
121+
],
122+
buildPhases: [
123+
TestSourcesBuildPhase(["s.swift"]),
124+
]
125+
),
126+
])
127+
let core = try await getCore()
128+
let tester = try TaskConstructionTester(core, testProject)
129+
130+
await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: ["LINKER_DRIVER": "swiftc"]), runDestination: .macOS) { results in
131+
results.checkNoDiagnostics()
132+
results.checkTask(.matchRuleType("Ld")) { task in
133+
task.checkCommandLineContains(["-no-stdlib-rpath"])
134+
}
135+
}
136+
137+
await tester.checkBuild(BuildParameters(configuration: "Debug", overrides: ["LINKER_DRIVER": "clang"]), runDestination: .macOS) { results in
138+
results.checkNoDiagnostics()
139+
results.checkTask(.matchRuleType("Ld")) { task in
140+
task.checkCommandLineDoesNotContain("-no-stdlib-rpath")
141+
}
142+
}
143+
}
97144
}

0 commit comments

Comments
 (0)