From db96787b08e86b6e9f7c3ab5fedfbf997245679c Mon Sep 17 00:00:00 2001 From: svmkr Date: Thu, 13 Nov 2025 13:11:35 +0100 Subject: [PATCH 1/2] Implement enable/disable-get-task-allow for swift build (#8378) Rewrite BuildCommandTests.getTaskAllowEntitlement to check for entitlements using codesign. Add debugging entitlement during build process using swift build. Fix wording in warning related to get-task-allow command line options. --- Sources/CoreCommands/SwiftCommandState.swift | 2 +- .../SwiftBuildSupport/SwiftBuildSystem.swift | 46 ++++- Tests/CommandsTests/BuildCommandTests.swift | 184 ++++++++++-------- 3 files changed, 144 insertions(+), 88 deletions(-) diff --git a/Sources/CoreCommands/SwiftCommandState.swift b/Sources/CoreCommands/SwiftCommandState.swift index 970e3e19d2c..aa768f938c0 100644 --- a/Sources/CoreCommands/SwiftCommandState.swift +++ b/Sources/CoreCommands/SwiftCommandState.swift @@ -900,7 +900,7 @@ public final class SwiftCommandState { } static let entitlementsMacOSWarning = """ - `--disable-get-task-allow-entitlement` and `--disable-get-task-allow-entitlement` only have an effect \ + `--enable-get-task-allow-entitlement` and `--disable-get-task-allow-entitlement` only have an effect \ when building on macOS. """ diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index e5f4204aebd..e6a35edc2e6 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -119,17 +119,35 @@ func withSession( } package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperationDelegate, SWBIndexingDelegate, Sendable { - package init() {} + private let shouldEnableDebuggingEntitlement: Bool + + package init(shouldEnableDebuggingEntitlement: Bool) { + self.shouldEnableDebuggingEntitlement = shouldEnableDebuggingEntitlement + } public func provisioningTaskInputs( targetGUID: String, provisioningSourceData: SWBProvisioningTaskInputsSourceData ) async -> SWBProvisioningTaskInputs { let identity = provisioningSourceData.signingCertificateIdentifier - if identity == "-" { - let signedEntitlements = provisioningSourceData.entitlementsDestination == "Signature" - ? provisioningSourceData.productTypeEntitlements.merging( - ["application-identifier": .plString(provisioningSourceData.bundleIdentifier)], + + if identity == "-" || identity.isEmpty { + let getTaskAllowEntitlementKey: String + let applicationIdentifierEntitlementKey: String + + if provisioningSourceData.sdkRoot.contains("macos") || provisioningSourceData.sdkRoot + .contains("simulator") + { + getTaskAllowEntitlementKey = "com.apple.security.get-task-allow" + applicationIdentifierEntitlementKey = "com.apple.application-identifier" + } else { + getTaskAllowEntitlementKey = "get-task-allow" + applicationIdentifierEntitlementKey = "application-identifier" + } + + let signedEntitlements = provisioningSourceData + .entitlementsDestination == "Signature" ? provisioningSourceData.productTypeEntitlements.merging( + [applicationIdentifierEntitlementKey: .plString(provisioningSourceData.bundleIdentifier)], uniquingKeysWith: { _, new in new } ).merging(provisioningSourceData.projectEntitlements ?? [:], uniquingKeysWith: { _, new in new }) : [:] @@ -141,6 +159,12 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat ).merging(provisioningSourceData.projectEntitlements ?? [:], uniquingKeysWith: { _, new in new }) : [:] + var additionalEntitlements: [String: SWBPropertyListItem] = [:] + + if shouldEnableDebuggingEntitlement { + additionalEntitlements[getTaskAllowEntitlementKey] = .plBool(true) + } + return SWBProvisioningTaskInputs( identityHash: "-", identityName: "-", @@ -149,7 +173,7 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat profilePath: nil, designatedRequirements: nil, signedEntitlements: signedEntitlements.merging( - provisioningSourceData.sdkRoot.contains("simulator") ? ["get-task-allow": .plBool(true)] : [:], + additionalEntitlements, uniquingKeysWith: { _, new in new } ), simulatedEntitlements: simulatedEntitlements, @@ -160,8 +184,6 @@ package final class SwiftBuildSystemPlanningOperationDelegate: SWBPlanningOperat errors: [], warnings: [] ) - } else if identity.isEmpty { - return SWBProvisioningTaskInputs() } else { return SWBProvisioningTaskInputs( identityHash: "-", @@ -599,7 +621,9 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { let operation = try await session.createBuildOperation( request: request, - delegate: SwiftBuildSystemPlanningOperationDelegate(), + delegate: SwiftBuildSystemPlanningOperationDelegate(shouldEnableDebuggingEntitlement: self.buildParameters + .debuggingParameters.shouldEnableDebuggingEntitlement + ), retainBuildDescription: true ) @@ -995,7 +1019,9 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { private static func constructDebuggingSettingsOverrides(from parameters: BuildParameters.Debugging) -> [String: String] { var settings: [String: String] = [:] // TODO: debugInfoFormat: https://github.com/swiftlang/swift-build/issues/560 - // TODO: shouldEnableDebuggingEntitlement: Enable/Disable get-task-allow + if parameters.shouldEnableDebuggingEntitlement { + settings["ENTITLEMENTS_DONT_REMOVE_GET_TASK_ALLOW"] = "YES" + } // TODO: omitFramePointer: https://github.com/swiftlang/swift-build/issues/561 return settings } diff --git a/Tests/CommandsTests/BuildCommandTests.swift b/Tests/CommandsTests/BuildCommandTests.swift index 18c88d11bbd..f7cef95721d 100644 --- a/Tests/CommandsTests/BuildCommandTests.swift +++ b/Tests/CommandsTests/BuildCommandTests.swift @@ -10,19 +10,21 @@ // //===----------------------------------------------------------------------===// -import Foundation +import _InternalTestSupport import Basics @testable import Commands @testable import CoreCommands +import Foundation import PackageGraph import PackageLoading import PackageModel import enum PackageModel.BuildConfiguration import SPMBuildCore -import _InternalTestSupport +import enum SWBUtil.PropertyList +import enum SWBUtil.PropertyListItem +import Testing import TSCTestSupport import Workspace -import Testing struct BuildResult { let binPath: AbsolutePath @@ -1298,7 +1300,6 @@ struct BuildCommandTestCases { } @Test( - .SWBINTTODO("Test failed because swiftbuild doesn't output precis codesign commands. Once swift run works with swiftbuild the test can be investigated."), .tags( .Feature.CommandLineArguments.DisableGetTaskAllowEntitlement, .Feature.CommandLineArguments.EnableGetTaskAllowEntitlement, @@ -1307,98 +1308,127 @@ struct BuildCommandTestCases { .tags( .Feature.CommandLineArguments.BuildSystem, ), - arguments: SupportedBuildSystemOnPlatform, + arguments: getBuildData(for: SupportedBuildSystemOnAllPlatforms), ) func getTaskAllowEntitlement( - buildSystem: BuildSystemProvider.Kind, + data: BuildData, ) async throws { - try await withKnownIssue(isIntermittent: true) { - try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in - #if os(macOS) - // try await building with default parameters. This should succeed. We build verbosely so we get full command - // lines. - var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,) - - // TODO verification of the ad-hoc code signing can be done by `swift run` of the executable in these cases once swiftbuild build system is working with that - #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) - - buildResult = try await build(["-v"], packagePath: fixturePath, configuration:.debug, buildSystem: buildSystem,) - - #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) - - // Build with different combinations of the entitlement flag and debug/release build configurations. - - buildResult = try await build( - ["--enable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .release, - buildSystem: buildSystem, - ) + let buildSystem = data.buildSystem + let buildConfiguration = data.config + try await fixture(name: "ValidLayouts/SingleModule/ExecutableNew") { fixturePath in + #if os(macOS) + func codesignDisplay(execPath: AbsolutePath) async throws + -> (AsyncProcessResult.ExitStatus, PropertyListItem?) + { + let args = ["codesign", "-d", "--entitlements", "-", "--xml", execPath.pathString] + let result = try await AsyncProcess.popen(arguments: args) + let entitlements: PropertyListItem? = if case .success(let output) = result.output, + !output.isEmpty + { + try PropertyList.fromBytes(output) + } else { + nil + } - #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) + return (result.exitStatus, entitlements) + } - buildResult = try await build( - ["--enable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .debug, - buildSystem: buildSystem, - ) + func verify(entitlements: PropertyListItem?, getTaskAllowRequired: Bool) { + guard let entitlements, case .plDict(let dict) = entitlements else { + if getTaskAllowRequired { + Issue.record("Missing expected entitlements") + } + return + } - #expect(buildResult.stdout.contains("codesign --force --sign - --entitlements")) + if getTaskAllowRequired { + #expect(dict["com.apple.security.get-task-allow"] == .plBool(true)) + } + } - buildResult = try await build( - ["--disable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .debug, - buildSystem: buildSystem, - ) + let execName = "ExecutableNew" - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) + var buildResult = try await build( + ["-v"], + packagePath: fixturePath, + configuration: buildConfiguration, + cleanAfterward: false, + buildSystem: buildSystem + ) + var ( + exitStatus, + entitlements + ) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName)) - buildResult = try await build( - ["--disable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .release, - buildSystem: buildSystem, - ) + // codesign performs basic verification in display mode, which is enough to confirm ad-hoc signature + // if verification fails (eg. no signature) termination code will be 1 + // though on Apple Silicon binary will always be signed because linker signs it by default + #expect(exitStatus == .terminated(code: 0)) + verify(entitlements: entitlements, getTaskAllowRequired: buildConfiguration == .debug) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - #else - var buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .debug, buildSystem: buildSystem,) + try await executeSwiftPackage(fixturePath, extraArgs: ["clean"], buildSystem: buildSystem) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) + buildResult = try await build( + ["--enable-get-task-allow-entitlement"], + packagePath: fixturePath, + configuration: buildConfiguration, + cleanAfterward: false, + buildSystem: buildSystem + ) + ( + exitStatus, + entitlements + ) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName)) - buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release,buildSystem: buildSystem,) + #expect(exitStatus == .terminated(code: 0)) + verify(entitlements: entitlements, getTaskAllowRequired: true) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) + try await executeSwiftPackage(fixturePath, extraArgs: ["clean"], buildSystem: buildSystem) - buildResult = try await build( - ["--disable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .release, - buildSystem: buildSystem, - ) + buildResult = try await build( + ["--disable-get-task-allow-entitlement"], + packagePath: fixturePath, + configuration: buildConfiguration, + cleanAfterward: false, + buildSystem: buildSystem + ) + ( + exitStatus, + entitlements + ) = try await codesignDisplay(execPath: buildResult.binPath.appending(execName)) + + #expect(exitStatus == .terminated(code: 0)) + verify(entitlements: entitlements, getTaskAllowRequired: false) + #else + var buildResult = try await build( + ["-v"], + packagePath: fixturePath, + configuration: buildConfiguration, + buildSystem: buildSystem + ) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) + #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - buildResult = try await build( - ["--enable-get-task-allow-entitlement", "-v"], - packagePath: fixturePath, - configuration: .release, - buildSystem: buildSystem, - ) + buildResult = try await build( + ["--disable-get-task-allow-entitlement", "-v"], + packagePath: fixturePath, + configuration: buildConfiguration, + buildSystem: buildSystem, + ) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) - #endif + #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) + #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) - buildResult = try await build(["-v"], packagePath: fixturePath, configuration: .release, buildSystem: buildSystem) + buildResult = try await build( + ["--enable-get-task-allow-entitlement", "-v"], + packagePath: fixturePath, + configuration: buildConfiguration, + buildSystem: buildSystem, + ) - #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) - } - } when: { - [.swiftbuild, .xcode].contains(buildSystem) && ProcessInfo.hostOperatingSystem != .linux + #expect(!buildResult.stdout.contains("codesign --force --sign - --entitlements")) + #expect(buildResult.stderr.contains(SwiftCommandState.entitlementsMacOSWarning)) + #endif } } From 099d761d33552bde82350e442f27718222ccc0ba Mon Sep 17 00:00:00 2001 From: svmkr Date: Wed, 17 Dec 2025 11:43:01 +0100 Subject: [PATCH 2/2] Set DEPLOYMENT_POSTPROCESSING for release builds (#8378) Use DEPLOYMENT_POSTPROCESSING set to NO instead of ENTITLEMENTS_DONT_REMOVE_GET_TASK_ALLOW when get-task-allow entitlement is requested to ensure release builds keep entitlement when needed. --- Sources/SwiftBuildSupport/PackagePIFBuilder.swift | 1 + Sources/SwiftBuildSupport/SwiftBuildSystem.swift | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift index 6e1abe5ac9d..47d6a71f15c 100644 --- a/Sources/SwiftBuildSupport/PackagePIFBuilder.swift +++ b/Sources/SwiftBuildSupport/PackagePIFBuilder.swift @@ -633,6 +633,7 @@ public final class PackagePIFBuilder { releaseSettings[.DEBUG_INFORMATION_FORMAT] = "dwarf-with-dsym" releaseSettings[.GCC_OPTIMIZATION_LEVEL] = "s" releaseSettings[.SWIFT_OPTIMIZATION_LEVEL] = "-Owholemodule" + releaseSettings[.DEPLOYMENT_POSTPROCESSING] = "YES" builder.project.addBuildConfig { id in BuildConfig(id: id, name: "Release", settings: releaseSettings) } } diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index e6a35edc2e6..4f7e61bba1e 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -1020,7 +1020,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { var settings: [String: String] = [:] // TODO: debugInfoFormat: https://github.com/swiftlang/swift-build/issues/560 if parameters.shouldEnableDebuggingEntitlement { - settings["ENTITLEMENTS_DONT_REMOVE_GET_TASK_ALLOW"] = "YES" + settings["DEPLOYMENT_POSTPROCESSING"] = "NO" } // TODO: omitFramePointer: https://github.com/swiftlang/swift-build/issues/561 return settings