From 70e9a6cdea70b8870fc6cf42f38fe0863f655f12 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Mon, 15 Sep 2025 15:08:12 -0700 Subject: [PATCH 1/2] [Swift Build] Default to the package's declared deployment target if none is explicitly specified --- .../RequiresOlderDeploymentTarget/Foo.swift | 8 +++ .../Package.swift | 13 +++++ Sources/Basics/Triple+Basics.swift | 13 ++++- .../PackageCommands/AuditBinaryArtifact.swift | 2 +- Sources/PackageModel/UserToolchain.swift | 6 +- .../BuildParameters/BuildParameters.swift | 3 +- .../SwiftSDKCommand/ConfigureSwiftSDK.swift | 2 +- .../SwiftSDKCommand/SwiftSDKSubcommand.swift | 2 +- Tests/CommandsTests/BuildCommandTests.swift | 57 +++++++++++++++++++ 9 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Foo.swift create mode 100644 Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Package.swift diff --git a/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Foo.swift b/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Foo.swift new file mode 100644 index 00000000000..aeeb2c29b21 --- /dev/null +++ b/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Foo.swift @@ -0,0 +1,8 @@ +@available(macOS, obsoleted: 13.0) +func foo() { + +} + +func bar() { + foo() +} diff --git a/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Package.swift b/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Package.swift new file mode 100644 index 00000000000..220ca90d3a9 --- /dev/null +++ b/Fixtures/Miscellaneous/RequiresOlderDeploymentTarget/Package.swift @@ -0,0 +1,13 @@ +// swift-tools-version:6.1 +import PackageDescription + +let package = Package( + name: "Foo", + platforms: [.macOS(.v12)], + products: [ + .library(name: "Foo", targets: ["Foo"]), + ], + targets: [ + .target(name: "Foo", path: "./"), + ] +) diff --git a/Sources/Basics/Triple+Basics.swift b/Sources/Basics/Triple+Basics.swift index 9773457033b..710d9900404 100644 --- a/Sources/Basics/Triple+Basics.swift +++ b/Sources/Basics/Triple+Basics.swift @@ -85,7 +85,16 @@ extension Triple { } /// Determine the versioned host triple using the Swift compiler. - public static func getHostTriple(usingSwiftCompiler swiftCompiler: AbsolutePath) throws -> Triple { + public static func getVersionedHostTriple(usingSwiftCompiler swiftCompiler: AbsolutePath) throws -> Triple { + try Self.getHostTriple(usingSwiftCompiler: swiftCompiler, versioned: true) + } + + /// Determine the unversioned host triple using the Swift compiler. + public static func getUnversionedHostTriple(usingSwiftCompiler swiftCompiler: AbsolutePath) throws -> Triple { + try Self.getHostTriple(usingSwiftCompiler: swiftCompiler, versioned: false) + } + + private static func getHostTriple(usingSwiftCompiler swiftCompiler: AbsolutePath, versioned: Bool) throws -> Triple { // Call the compiler to get the target info JSON. let compilerOutput: String do { @@ -106,7 +115,7 @@ extension Triple { // Get the triple string from the parsed JSON. let tripleString: String do { - tripleString = try parsedTargetInfo.get("target").get("triple") + tripleString = try parsedTargetInfo.get("target").get(versioned ? "triple" : "unversionedTriple") } catch { throw InternalError( "Target info does not contain a triple string (\(error.interpolationDescription)).\nTarget info: \(parsedTargetInfo)" diff --git a/Sources/Commands/PackageCommands/AuditBinaryArtifact.swift b/Sources/Commands/PackageCommands/AuditBinaryArtifact.swift index 0b5119fe733..ac290eea4c0 100644 --- a/Sources/Commands/PackageCommands/AuditBinaryArtifact.swift +++ b/Sources/Commands/PackageCommands/AuditBinaryArtifact.swift @@ -37,7 +37,7 @@ struct AuditBinaryArtifact: AsyncSwiftCommand { let hostToolchain = try swiftCommandState.getHostToolchain() let clang = try hostToolchain.getClangCompiler() let objdump = try hostToolchain.getLLVMObjdump() - let hostTriple = try Triple.getHostTriple( + let hostTriple = try Triple.getVersionedHostTriple( usingSwiftCompiler: hostToolchain.swiftCompilerPath) let fileSystem = swiftCommandState.fileSystem diff --git a/Sources/PackageModel/UserToolchain.swift b/Sources/PackageModel/UserToolchain.swift index 3d9bf6797e8..86149e2d997 100644 --- a/Sources/PackageModel/UserToolchain.swift +++ b/Sources/PackageModel/UserToolchain.swift @@ -200,11 +200,11 @@ public final class UserToolchain: Toolchain { } } - private static func getHostTriple(targetInfo: JSON) throws -> Basics.Triple { + private static func getHostTriple(targetInfo: JSON, versioned: Bool) throws -> Basics.Triple { // Get the triple string from the target info. let tripleString: String do { - tripleString = try targetInfo.get("target").get("triple") + tripleString = try targetInfo.get("target").get(versioned ? "triple" : "unversionedTriple") } catch { throw InternalError( "Target info does not contain a triple string (\(error.interpolationDescription)).\nTarget info: \(targetInfo)" @@ -741,7 +741,7 @@ public final class UserToolchain: Toolchain { // targetInfo from the compiler let targetInfo = try customTargetInfo ?? Self.getTargetInfo(swiftCompiler: swiftCompilers.compile) self._targetInfo = targetInfo - triple = try swiftSDK.targetTriple ?? Self.getHostTriple(targetInfo: targetInfo) + triple = try swiftSDK.targetTriple ?? Self.getHostTriple(targetInfo: targetInfo, versioned: false) } // Change the triple to the specified arch if there's exactly one of them. diff --git a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift index 6bd5b8804c1..56bc1254984 100644 --- a/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift +++ b/Sources/SPMBuildCore/BuildParameters/BuildParameters.swift @@ -173,7 +173,8 @@ public struct BuildParameters: Encodable { testingParameters: Testing = .init(), apiDigesterMode: APIDigesterMode? = nil ) throws { - let triple = try triple ?? .getHostTriple(usingSwiftCompiler: toolchain.swiftCompilerPath) + // Default to the unversioned triple if none is provided so that we defer to the package's requested deployment target. + let triple = try triple ?? .getUnversionedHostTriple(usingSwiftCompiler: toolchain.swiftCompilerPath) self.debuggingParameters = debuggingParameters ?? .init( triple: triple, shouldEnableDebuggingEntitlement: configuration == .debug, diff --git a/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift index 1c6f9512e93..7000b52b166 100644 --- a/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift @@ -117,7 +117,7 @@ struct ConfigureSwiftSDK: AsyncParsableCommand { let swiftSDKsDirectory = try self.getOrCreateSwiftSDKsDirectory() let hostToolchain = try UserToolchain(swiftSDK: SwiftSDK.hostSwiftSDK()) - let triple = try Triple.getHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) + let triple = try Triple.getVersionedHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) var commandError: Error? = nil do { diff --git a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift index c1a62ad35a8..00ee520cc06 100644 --- a/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift +++ b/Sources/SwiftSDKCommand/SwiftSDKSubcommand.swift @@ -68,7 +68,7 @@ extension SwiftSDKSubcommand { ), environment: environment ) - let triple = try Triple.getHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) + let triple = try Triple.getVersionedHostTriple(usingSwiftCompiler: hostToolchain.swiftCompilerPath) var commandError: Error? = nil do { diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 1ee9ed8c50d..dad5117d54e 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -1440,6 +1440,63 @@ struct BuildCommandTestCases { } } } + + @Test(.requireHostOS(.macOS), arguments: SupportedBuildSystemOnPlatform) + func buildingPackageWhichRequiresOlderDeploymentTarget(buildSystem: BuildSystemProvider.Kind) async throws { + // This fixture specifies a deployment target of macOS 12, and uses API obsoleted in macOS 13. The goal + // of this test is to ensure that SwiftPM respects the deployment target specified in the package manifest + // when passed no triple of an unversioned triple, rather than using the latests deployment target. + + // No triple - build should pass + try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in + try await executeSwiftBuild( + path, + buildSystem: buildSystem, + throwIfCommandFails: true + ) + } + + let hostArch: String + #if arch(arm64) + hostArch = "arm64" + #else + hostArch = "x86_64" + #endif + + // Unversioned triple - build should pass + try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in + try await executeSwiftBuild( + path, + extraArgs: ["--triple", "\(hostArch)-apple-macosx"], + buildSystem: buildSystem, + throwIfCommandFails: true + ) + } + + // Versioned triple with supported deployment target - build should pass + try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in + try await executeSwiftBuild( + path, + extraArgs: ["--triple", "\(hostArch)-apple-macosx12.0"], + buildSystem: buildSystem, + throwIfCommandFails: true + ) + } + + if buildSystem == .swiftbuild { + // Versioned triple with unsupported deployment target - build should fail + try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in + await #expect(throws: Error.self) { + try await executeSwiftBuild( + path, + extraArgs: ["--triple", "\(hostArch)-apple-macosx14.0"], + buildSystem: buildSystem, + throwIfCommandFails: true + ) + } + } + } + } } extension Triple { From 0bf305f47ca4eaea70116a53f37d4968cda53465 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Fri, 19 Sep 2025 17:23:38 -0700 Subject: [PATCH 2/2] Remove improper stdlib overrides on macOS when running plugins --- .../Plugins/DefaultPluginScriptRunner.swift | 7 +++++++ Sources/SwiftBuildSupport/PIFBuilder.swift | 7 +++++++ Tests/CommandsTests/BuildCommandTests.swift | 14 ++++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift b/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift index f8a98a664e5..c1f487c7bba 100644 --- a/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift +++ b/Sources/SPMBuildCore/Plugins/DefaultPluginScriptRunner.swift @@ -466,12 +466,19 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable { var env = Environment.current + // FIXME: This is largely a workaround for improper rpath setup on Linux. It should be + // removed once the Swift Build backend switches to use swiftc as the linker driver + // for targets with Swift sources. For now, limit the scope to non-macOS, so that + // plugins do not inadvertently use the toolchain stdlib instead of the OS stdlib + // when built with a Swift.org toolchain. + #if !os(macOS) // Update the environment for any runtime library paths that tools compiled // for the command plugin might require after they have been built. let runtimeLibPaths = self.toolchain.runtimeLibraryPaths for libPath in runtimeLibPaths { env.appendPath(key: .libraryPath, value: libPath.pathString) } + #endif #if os(Windows) let pluginLibraryPath = self.toolchain.swiftPMLibrariesLocation.pluginLibraryPath.pathString diff --git a/Sources/SwiftBuildSupport/PIFBuilder.swift b/Sources/SwiftBuildSupport/PIFBuilder.swift index 24bc550218b..42dbd653da8 100644 --- a/Sources/SwiftBuildSupport/PIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PIFBuilder.swift @@ -333,12 +333,19 @@ public final class PIFBuilder { buildCommands.append(contentsOf: result.buildCommands.map( { buildCommand in var newEnv: Environment = buildCommand.configuration.environment + // FIXME: This is largely a workaround for improper rpath setup on Linux. It should be + // removed once the Swift Build backend switches to use swiftc as the linker driver + // for targets with Swift sources. For now, limit the scope to non-macOS, so that + // plugins do not inadvertently use the toolchain stdlib instead of the OS stdlib + // when built with a Swift.org toolchain. + #if !os(macOS) let runtimeLibPaths = buildParameters.toolchain.runtimeLibraryPaths // Add paths to swift standard runtime libraries to the library path so that they can be found at runtime for libPath in runtimeLibPaths { newEnv.appendPath(key: .libraryPath, value: libPath.pathString) } + #endif // Append the system path at the end so that necessary system tool paths can be found if let pathValue = Environment.current[EnvironmentKey.path] { diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index dad5117d54e..1a5adcd384c 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -1459,8 +1459,11 @@ struct BuildCommandTestCases { let hostArch: String #if arch(arm64) hostArch = "arm64" - #else + #elseif arch(x86_64) hostArch = "x86_64" + #else + Issue.record("test is not supported on host arch") + return #endif // Unversioned triple - build should pass @@ -1483,9 +1486,9 @@ struct BuildCommandTestCases { ) } - if buildSystem == .swiftbuild { - // Versioned triple with unsupported deployment target - build should fail - try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in + // Versioned triple with unsupported deployment target - build should fail + try await withKnownIssue { + _ = try await fixture(name: "Miscellaneous/RequiresOlderDeploymentTarget") { path in await #expect(throws: Error.self) { try await executeSwiftBuild( path, @@ -1495,6 +1498,9 @@ struct BuildCommandTestCases { ) } } + } when: { + // The native build system does not correctly pass the elevated deployment target + buildSystem != .swiftbuild } } }