Skip to content

Commit a022c4a

Browse files
committed
Do not add rpath to swift-testing for CLT or custom toolchains
This was added so that test binaries could find swift-testing, but we should insert those paths into `DYLD_*_PATH` rather than add to the rpath. The current behavior adds an absolute rpath to *all* binaries, not even just test binaries.
1 parent a86e9f9 commit a022c4a

File tree

5 files changed

+114
-126
lines changed

5 files changed

+114
-126
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
import XCTest
22

33
final class CheckTestLibraryEnvironmentVariableTests: XCTestCase {
4-
func testEnvironmentVariable() throws {
5-
let envvar = ProcessInfo.processInfo.environment["SWIFT_TESTING_ENABLED"]
6-
XCTAssertEqual(envvar, "0")
4+
func testEnvironmentVariables() throws {
5+
#if !os(macOS)
6+
try XCTSkipIf(true, "Test is macOS specific")
7+
#endif
8+
9+
let testingEnabled = ProcessInfo.processInfo.environment["SWIFT_TESTING_ENABLED"]
10+
XCTAssertEqual(testingEnabled, "0")
11+
12+
if ProcessInfo.processInfo.environment["CONTAINS_SWIFT_TESTING"] != nil {
13+
let frameworkPath = try XCTUnwrap(ProcessInfo.processInfo.environment["DYLD_FRAMEWORK_PATH"])
14+
let libraryPath = try XCTUnwrap(ProcessInfo.processInfo.environment["DYLD_LIBRARY_PATH"])
15+
XCTAssertTrue(
16+
frameworkPath.contains("testing") || libraryPath.contains("testing"),
17+
"Expected 'testing' in '\(frameworkPath)' or '\(libraryPath)'"
18+
)
19+
}
720
}
821
}

Sources/Commands/Utilities/TestingSupport.swift

+10-1
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,21 @@ enum TestingSupport {
209209
if let xctestLocation = toolchain.xctestPath {
210210
env.prependPath(key: .path, value: xctestLocation.pathString)
211211
}
212-
if let swiftTestingLocation = toolchain.swiftTestingPathOnWindows {
212+
if let swiftTestingLocation = toolchain.swiftTestingPath {
213213
env.prependPath(key: .path, value: swiftTestingLocation.pathString)
214214
}
215215
#endif
216216
return env
217217
#else
218+
// Add path to swift-testing override if there is one
219+
if let swiftTestingPath = toolchain.swiftTestingPath {
220+
if swiftTestingPath.extension == "framework" {
221+
env.appendPath(key: "DYLD_FRAMEWORK_PATH", value: swiftTestingPath.pathString)
222+
} else {
223+
env.appendPath(key: "DYLD_LIBRARY_PATH", value: swiftTestingPath.pathString)
224+
}
225+
}
226+
218227
// Add the sdk platform path if we have it.
219228
// Since XCTestHelper targets macOS, we need the macOS platform paths here.
220229
if let sdkPlatformPaths = try? SwiftSDK.sdkPlatformPaths(for: .macOS) {

Sources/PackageModel/UserToolchain.swift

+82-106
Original file line numberDiff line numberDiff line change
@@ -404,53 +404,6 @@ public final class UserToolchain: Toolchain {
404404
}
405405
#endif
406406

407-
/// On MacOS toolchain can shadow SDK content. This method is intended
408-
/// to locate and include swift-testing library from a toolchain before
409-
/// sdk content which to sure that builds that use a custom toolchain
410-
/// always get a custom swift-testing library as well.
411-
static func deriveMacOSSpecificSwiftTestingFlags(
412-
derivedSwiftCompiler: AbsolutePath,
413-
fileSystem: any FileSystem
414-
) -> (swiftCFlags: [String], linkerFlags: [String]) {
415-
// If this is CommandLineTools all we need to add is a frameworks path.
416-
if let frameworksPath = try? AbsolutePath(
417-
validating: "../../Library/Developer/Frameworks",
418-
relativeTo: resolveSymlinks(derivedSwiftCompiler).parentDirectory
419-
), fileSystem.exists(frameworksPath.appending("Testing.framework")) {
420-
return (swiftCFlags: [
421-
"-F", frameworksPath.pathString
422-
], linkerFlags: [
423-
"-rpath", frameworksPath.pathString
424-
])
425-
}
426-
427-
guard let toolchainLibDir = try? toolchainLibDir(
428-
swiftCompilerPath: derivedSwiftCompiler
429-
) else {
430-
return (swiftCFlags: [], linkerFlags: [])
431-
}
432-
433-
let testingLibDir = toolchainLibDir.appending(
434-
components: ["swift", "macosx", "testing"]
435-
)
436-
437-
let testingPluginsDir = toolchainLibDir.appending(
438-
components: ["swift", "host", "plugins", "testing"]
439-
)
440-
441-
guard fileSystem.exists(testingLibDir), fileSystem.exists(testingPluginsDir) else {
442-
return (swiftCFlags: [], linkerFlags: [])
443-
}
444-
445-
return (swiftCFlags: [
446-
"-I", testingLibDir.pathString,
447-
"-L", testingLibDir.pathString,
448-
"-plugin-path", testingPluginsDir.pathString
449-
], linkerFlags: [
450-
"-rpath", testingLibDir.pathString
451-
])
452-
}
453-
454407
internal static func deriveSwiftCFlags(
455408
triple: Triple,
456409
swiftSDK: SwiftSDK,
@@ -673,14 +626,33 @@ public final class UserToolchain: Toolchain {
673626
var swiftCompilerFlags: [String] = []
674627
var extraLinkerFlags: [String] = []
675628

676-
if triple.isMacOSX {
677-
let (swiftCFlags, linkerFlags) = Self.deriveMacOSSpecificSwiftTestingFlags(
678-
derivedSwiftCompiler: swiftCompilers.compile,
679-
fileSystem: fileSystem
680-
)
629+
let swiftTestingPath: AbsolutePath? = try Self.deriveSwiftTestingPath(
630+
derivedSwiftCompiler: swiftCompilers.compile,
631+
swiftSDK: self.swiftSDK,
632+
triple: triple,
633+
environment: environment,
634+
fileSystem: fileSystem
635+
)
636+
637+
if triple.isMacOSX, let swiftTestingPath {
638+
// swift-testing in CommandLineTools, needs extra frameworks search path
639+
if swiftTestingPath.extension == "framework" {
640+
swiftCompilerFlags += ["-F", swiftTestingPath.pathString]
641+
}
681642

682-
swiftCompilerFlags += swiftCFlags
683-
extraLinkerFlags += linkerFlags
643+
// Otherwise we must have a custom toolchain, add overrides to find its swift-testing ahead of any in the
644+
// SDK. We expect the library to be in `lib/swift/macosx/testing` and the plugin in
645+
// `lib/swift/host/plugins/testing`
646+
if let pluginsPath = try? AbsolutePath(
647+
validating: "../../host/plugins/testing",
648+
relativeTo: swiftTestingPath
649+
) {
650+
swiftCompilerFlags += [
651+
"-I", swiftTestingPath.pathString,
652+
"-L", swiftTestingPath.pathString,
653+
"-plugin-path", pluginsPath.pathString,
654+
]
655+
}
684656
}
685657

686658
swiftCompilerFlags += try Self.deriveSwiftCFlags(
@@ -774,19 +746,6 @@ public final class UserToolchain: Toolchain {
774746
)
775747
}
776748

777-
let swiftTestingPath: AbsolutePath?
778-
if case .custom(_, let useXcrun) = searchStrategy, !useXcrun {
779-
swiftTestingPath = nil
780-
} else {
781-
swiftTestingPath = try Self.deriveSwiftTestingPath(
782-
swiftSDK: self.swiftSDK,
783-
triple: triple,
784-
environment: environment,
785-
fileSystem: fileSystem
786-
)
787-
}
788-
789-
790749
self.configuration = .init(
791750
librarianPath: librarianPath,
792751
swiftCompilerPath: swiftCompilers.manifest,
@@ -1006,58 +965,75 @@ public final class UserToolchain: Toolchain {
1006965
.appending("bin")
1007966
}
1008967
}
1009-
return .none
968+
return nil
1010969
}
1011970

971+
/// Find the swift-testing path if it is within a path that will need extra search paths.
1012972
private static func deriveSwiftTestingPath(
973+
derivedSwiftCompiler: AbsolutePath,
1013974
swiftSDK: SwiftSDK,
1014975
triple: Triple,
1015976
environment: Environment,
1016977
fileSystem: any FileSystem
1017978
) throws -> AbsolutePath? {
1018-
guard triple.isWindows() else {
1019-
return nil
1020-
}
979+
if triple.isDarwin() {
980+
// If this is CommandLineTools all we need to add is a frameworks path.
981+
if let frameworksPath = try? AbsolutePath(
982+
validating: "../../Library/Developer/Frameworks",
983+
relativeTo: resolveSymlinks(derivedSwiftCompiler).parentDirectory
984+
), fileSystem.exists(frameworksPath.appending("Testing.framework")) {
985+
return frameworksPath
986+
}
1021987

1022-
guard let (platform, info) = getWindowsPlatformInfo(
1023-
swiftSDK: swiftSDK,
1024-
environment: environment,
1025-
fileSystem: fileSystem
1026-
) else {
1027-
return nil
1028-
}
988+
guard let toolchainLibDir = try? toolchainLibDir(swiftCompilerPath: derivedSwiftCompiler) else {
989+
return nil
990+
}
1029991

1030-
guard let swiftTestingVersion = info.defaults.swiftTestingVersion else {
1031-
return nil
1032-
}
992+
let testingLibDir = toolchainLibDir.appending(components: ["swift", "macosx", "testing"])
993+
if fileSystem.exists(testingLibDir) {
994+
return testingLibDir
995+
}
996+
} else if triple.isWindows() {
997+
guard let (platform, info) = getWindowsPlatformInfo(
998+
swiftSDK: swiftSDK,
999+
environment: environment,
1000+
fileSystem: fileSystem
1001+
) else {
1002+
return nil
1003+
}
10331004

1034-
let swiftTesting: AbsolutePath =
1035-
platform.appending("Developer")
1036-
.appending("Library")
1037-
.appending("Testing-\(swiftTestingVersion)")
1038-
1039-
let binPath: AbsolutePath? = switch triple.arch {
1040-
case .x86_64: // amd64 x86_64 x86_64h
1041-
swiftTesting.appending("usr")
1042-
.appending("bin64")
1043-
case .x86: // i386 i486 i586 i686 i786 i886 i986
1044-
swiftTesting.appending("usr")
1045-
.appending("bin32")
1046-
case .arm: // armv7 and many more
1047-
swiftTesting.appending("usr")
1048-
.appending("bin32a")
1049-
case .aarch64: // aarch6 arm64
1050-
swiftTesting.appending("usr")
1051-
.appending("bin64a")
1052-
default:
1053-
nil
1054-
}
1005+
guard let swiftTestingVersion = info.defaults.swiftTestingVersion else {
1006+
return nil
1007+
}
10551008

1056-
guard let path = binPath, fileSystem.exists(path) else {
1057-
return nil
1009+
let swiftTesting: AbsolutePath =
1010+
platform.appending("Developer")
1011+
.appending("Library")
1012+
.appending("Testing-\(swiftTestingVersion)")
1013+
1014+
let binPath: AbsolutePath? = switch triple.arch {
1015+
case .x86_64: // amd64 x86_64 x86_64h
1016+
swiftTesting.appending("usr")
1017+
.appending("bin64")
1018+
case .x86: // i386 i486 i586 i686 i786 i886 i986
1019+
swiftTesting.appending("usr")
1020+
.appending("bin32")
1021+
case .arm: // armv7 and many more
1022+
swiftTesting.appending("usr")
1023+
.appending("bin32a")
1024+
case .aarch64: // aarch6 arm64
1025+
swiftTesting.appending("usr")
1026+
.appending("bin64a")
1027+
default:
1028+
nil
1029+
}
1030+
1031+
if let path = binPath, fileSystem.exists(path) {
1032+
return path
1033+
}
10581034
}
10591035

1060-
return path
1036+
return nil
10611037
}
10621038

10631039
public var sdkRootPath: AbsolutePath? {
@@ -1084,7 +1060,7 @@ public final class UserToolchain: Toolchain {
10841060
configuration.xctestPath
10851061
}
10861062

1087-
public var swiftTestingPathOnWindows: AbsolutePath? {
1063+
public var swiftTestingPath: AbsolutePath? {
10881064
configuration.swiftTestingPath
10891065
}
10901066

Tests/BuildTests/BuildPlanTests.swift

+1-12
Original file line numberDiff line numberDiff line change
@@ -4815,10 +4815,7 @@ final class BuildPlanTests: XCTestCase {
48154815
"-sdk", "/fake/sdk",
48164816
]
48174817
)
4818-
XCTAssertEqual(
4819-
mockToolchain.extraFlags.linkerFlags,
4820-
["-rpath", "/fake/path/lib/swift/macosx/testing"]
4821-
)
4818+
XCTAssertNoMatch(mockToolchain.extraFlags.linkerFlags, ["-rpath"])
48224819

48234820
let observability = ObservabilitySystem.makeForTesting()
48244821
let graph = try loadModulesGraph(
@@ -4853,31 +4850,23 @@ final class BuildPlanTests: XCTestCase {
48534850

48544851
let testProductLinkArgs = try result.buildProduct(for: "Lib").linkArguments()
48554852
XCTAssertMatch(testProductLinkArgs, [
4856-
.anySequence,
48574853
"-I", "/fake/path/lib/swift/macosx/testing",
48584854
"-L", "/fake/path/lib/swift/macosx/testing",
4859-
.anySequence,
4860-
"-Xlinker", "-rpath",
4861-
"-Xlinker", "/fake/path/lib/swift/macosx/testing",
48624855
])
48634856

48644857
let libModuleArgs = try result.moduleBuildDescription(for: "Lib").swift().compileArguments()
48654858
XCTAssertMatch(libModuleArgs, [
4866-
.anySequence,
48674859
"-I", "/fake/path/lib/swift/macosx/testing",
48684860
"-L", "/fake/path/lib/swift/macosx/testing",
48694861
"-plugin-path", "/fake/path/lib/swift/host/plugins/testing",
4870-
.anySequence,
48714862
])
48724863
XCTAssertNoMatch(libModuleArgs, ["-Xlinker"])
48734864

48744865
let testModuleArgs = try result.moduleBuildDescription(for: "LibTest").swift().compileArguments()
48754866
XCTAssertMatch(testModuleArgs, [
4876-
.anySequence,
48774867
"-I", "/fake/path/lib/swift/macosx/testing",
48784868
"-L", "/fake/path/lib/swift/macosx/testing",
48794869
"-plugin-path", "/fake/path/lib/swift/host/plugins/testing",
4880-
.anySequence,
48814870
])
48824871
XCTAssertNoMatch(testModuleArgs, ["-Xlinker"])
48834872
}

Tests/CommandsTests/TestCommandTests.swift

+5-4
Original file line numberDiff line numberDiff line change
@@ -559,14 +559,15 @@ final class TestCommandTests: CommandsTestCase {
559559
}
560560
#endif
561561

562-
#if os(macOS)
563-
// "SWIFT_TESTING_ENABLED" is set only on macOS, skip the check on other platforms.
564562
func testLibraryEnvironmentVariable() async throws {
565563
try await fixture(name: "Miscellaneous/CheckTestLibraryEnvironmentVariable") { fixturePath in
566-
await XCTAssertAsyncNoThrow(try await SwiftPM.Test.execute(packagePath: fixturePath))
564+
var extraEnv = Environment()
565+
if try UserToolchain.default.swiftTestingPath != nil {
566+
extraEnv["CONTAINS_SWIFT_TESTING"] = "1"
567+
}
568+
await XCTAssertAsyncNoThrow(try await SwiftPM.Test.execute(packagePath: fixturePath, env: extraEnv))
567569
}
568570
}
569-
#endif
570571

571572
func testXCTestOnlyDoesNotLogAboutNoMatchingTests() async throws {
572573
try await fixture(name: "Miscellaneous/TestDiscovery/Simple") { fixturePath in

0 commit comments

Comments
 (0)