From 461ae493269437ed325f06499c73f872b68144e8 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Sat, 30 Aug 2025 09:24:34 +0200 Subject: [PATCH] Add request to get preferred run destination for a platform This can be useful when implementing a system that needs to configure the active run destination of a build request but only knows the platform that the user is requesting a build for, but no architecture etc. --- Sources/SWBBuildService/Messages.swift | 26 +++++++++ Sources/SWBProtocol/Message.swift | 32 ++++++++++- .../SwiftBuild/SWBBuildServiceSession.swift | 13 +++++ .../PreferredRunDestinationTests.swift | 53 +++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 Tests/SwiftBuildTests/PreferredRunDestinationTests.swift diff --git a/Sources/SWBBuildService/Messages.swift b/Sources/SWBBuildService/Messages.swift index ab5b43b8..3b93a870 100644 --- a/Sources/SWBBuildService/Messages.swift +++ b/Sources/SWBBuildService/Messages.swift @@ -1528,6 +1528,31 @@ private struct ClearAllCaches: MessageHandler { } } +private struct GetPlatformPreferredRunDestinationMsg: MessageHandler { + struct PlatformNotFoundError: Error, CustomStringConvertible { + let platform: String + var description: String { "Unknown platform '\(platform)'" } + } + struct UnknownPreferredArchError: Error, CustomStringConvertible { + let platform: String + var description: String { "Platform '\(platform)' does not specify a preferred architecture" } + } + + func handle(request: Request, message: GetPlatformPreferredRunDestinationRequest) async throws -> GetPlatformPreferredRunDestinationResponse { + let session = try request.session(for: message) + guard let platform = session.core.platformRegistry.lookup(name: message.platform) else { + throw PlatformNotFoundError(platform: message.platform) + } + // All relevant platforms define a preferredArch, so the undefined_arch fallback case should never happen + // in practice, and indicates a serious issue occurred during plugin loading. + guard let targetArchitecture = platform.preferredArch else { + throw UnknownPreferredArchError(platform: message.platform) + } + let runDestination = RunDestinationInfo(platform: platform.name, sdk: platform.name, sdkVariant: nil, targetArchitecture: targetArchitecture, supportedArchitectures: [targetArchitecture], disableOnlyActiveArch: false, hostTargetedPlatform: nil) + return GetPlatformPreferredRunDestinationResponse(info: runDestination) + } +} + // MARK: ServiceExtension Support public struct ServiceSessionMessageHandlers: ServiceExtension { @@ -1593,6 +1618,7 @@ package struct ServiceMessageHandlers: ServiceExtension { service.registerMessageHandler(GetStatisticsDumpMsg.self) service.registerMessageHandler(GetBuildSettingsDescriptionDumpMsg.self) service.registerMessageHandler(ExecuteCommandLineToolMsg.self) + service.registerMessageHandler(GetPlatformPreferredRunDestinationMsg.self) service.registerMessageHandler(CreateXCFrameworkHandler.self) diff --git a/Sources/SWBProtocol/Message.swift b/Sources/SWBProtocol/Message.swift index f640c602..7b5cc4e1 100644 --- a/Sources/SWBProtocol/Message.swift +++ b/Sources/SWBProtocol/Message.swift @@ -65,7 +65,7 @@ public struct PingRequest: RequestMessage, Equatable { public struct SetConfigItemRequest: RequestMessage, Equatable { public typealias ResponseMessage = VoidResponse - + public static let name = "SET_CONFIG_ITEM" public let key: String @@ -286,6 +286,33 @@ public struct GetBuildSettingsDescriptionRequest: SessionMessage, RequestMessage } } +/// Get the preferred run destination for a given platform +public struct GetPlatformPreferredRunDestinationRequest: SessionMessage, RequestMessage, Equatable, SerializableCodable { + public typealias ResponseMessage = GetPlatformPreferredRunDestinationResponse + + public static let name = "GET_PLATFORM_PREFERRED_RUN_DESTINATION_REQUEST" + + public let sessionHandle: String + + /// The name of the platform to get the run destination for, eg. `macosx`, `iphoneos`, `iphonesimulator` + public let platform: String + + public init(sessionHandle: String, platform: String) { + self.sessionHandle = sessionHandle + self.platform = platform + } +} + +public struct GetPlatformPreferredRunDestinationResponse: Message, Equatable, SerializableCodable { + public static let name = "GET_PLATFORM_PREFERRED_RUN_DESTINATION_RESPONSE" + + public let info: RunDestinationInfo + + public init(info: RunDestinationInfo) { + self.info = info + } +} + public struct CreateXCFrameworkRequest: RequestMessage, Equatable, SerializableCodable { public typealias ResponseMessage = StringResponse @@ -1171,6 +1198,9 @@ public struct IPCMessage: Serializable, Sendable { GetBuildSettingsDescriptionRequest.self, ExecuteCommandLineToolRequest.self, + GetPlatformPreferredRunDestinationRequest.self, + GetPlatformPreferredRunDestinationResponse.self, + CreateSessionRequest.self, CreateSessionResponse.self, SetSessionSystemInfoRequest.self, diff --git a/Sources/SwiftBuild/SWBBuildServiceSession.swift b/Sources/SwiftBuild/SWBBuildServiceSession.swift index c96d6c7e..92c3d43f 100644 --- a/Sources/SwiftBuild/SWBBuildServiceSession.swift +++ b/Sources/SwiftBuild/SWBBuildServiceSession.swift @@ -611,6 +611,19 @@ public final class SWBBuildServiceSession: Sendable { try await service.send(request: DeveloperPathRequest(sessionHandle: uid)).value } + public func preferredRunDestination(forPlatform platformName: String) async throws -> SWBRunDestinationInfo { + let runDestination = try await service.send(request: GetPlatformPreferredRunDestinationRequest(sessionHandle: uid, platform: platformName)).info + return SWBRunDestinationInfo( + platform: runDestination.platform, + sdk: runDestination.sdk, + sdkVariant: runDestination.sdkVariant, + targetArchitecture: runDestination.targetArchitecture, + supportedArchitectures: runDestination.supportedArchitectures.elements, + disableOnlyActiveArch: runDestination.disableOnlyActiveArch, + hostTargetedPlatform: runDestination.hostTargetedPlatform + ) + } + /// Set the session system information. public func setSystemInfo(_ systemInfo: SWBSystemInfo) async throws { _ = try await service.send(request: SetSessionSystemInfoRequest(sessionHandle: uid, operatingSystemVersion: Version(systemInfo.operatingSystemVersion), productBuildVersion: systemInfo.productBuildVersion, nativeArchitecture: systemInfo.nativeArchitecture)) diff --git a/Tests/SwiftBuildTests/PreferredRunDestinationTests.swift b/Tests/SwiftBuildTests/PreferredRunDestinationTests.swift new file mode 100644 index 00000000..bc08591b --- /dev/null +++ b/Tests/SwiftBuildTests/PreferredRunDestinationTests.swift @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Testing +import SwiftBuild +import SwiftBuildTestSupport +import SWBTestSupport +@_spi(Testing) import SWBUtil + +@Suite +fileprivate struct PreferredRunDestinationTests { + @Test(.requireSDKs(.macOS)) + func platformExists() async throws { + try await withTemporaryDirectory { (temporaryDirectory: NamedTemporaryDirectory) in + try await withAsyncDeferrable { deferrable in + let testSession = try await TestSWBSession(temporaryDirectory: temporaryDirectory) + await deferrable.addBlock { + await #expect(throws: Never.self) { + try await testSession.close() + } + } + let runDestination = try await testSession.session.preferredRunDestination(forPlatform: "macosx") + #expect(runDestination.sdk == "macosx") + } + } + } + + @Test + func unknownPlatform() async throws { + try await withTemporaryDirectory { (temporaryDirectory: NamedTemporaryDirectory) in + try await withAsyncDeferrable { deferrable in + let testSession = try await TestSWBSession(temporaryDirectory: temporaryDirectory) + await deferrable.addBlock { + await #expect(throws: Never.self) { + try await testSession.close() + } + } + await #expect(throws: (any Error).self) { + try await testSession.session.preferredRunDestination(forPlatform: "unknown_platform") + } + } + } + } +}