import Foundation public struct BuildSettings: Sendable { private static let customBinDir = // this is the same option used by SwiftPM itself for dev builds ProcessInfo.processInfo.environment["SWIFTPM_CUSTOM_BIN_DIR"].map { URL(fileURLWithPath: $0) } private static let envURL = URL(fileURLWithPath: "/usr/bin/env") public let packagePath: String public let configuration: BuildConfiguration public let triple: String public let sdkOptions: [String] public let options: [String] public init( configuration: BuildConfiguration, triple: String, packagePath: String = ".", options: [String] = [] ) async throws { self.packagePath = packagePath self.configuration = configuration self.options = options self.triple = triple // on macOS we don't explicitly install a Swift SDK but // SwiftPM vends "implicit" Darwin SDKs as of Swift 6.1, // i.e. we can pass `--swift-sdk arm64-apple-ios` and it // just works. See: // https://github.com/swiftlang/swift-package-manager/pull/6828 self.sdkOptions = ["--swift-sdk", triple] } #if os(macOS) private static func xcrun(_ arguments: [String]) async throws -> String { let xcrun = Process() let pipe = Pipe() xcrun.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun") xcrun.arguments = arguments xcrun.standardOutput = pipe try await xcrun.runUntilExit() return String(decoding: pipe.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self) .trimmingCharacters(in: .whitespacesAndNewlines) } private static let swiftURL = Task { try await URL(fileURLWithPath: xcrun(["-f", "swift"])) } #endif public func swiftPMInvocation( forTool tool: String, arguments: [String], packagePathOverride: String? = nil ) async throws -> Process { let process = Process() let baseArguments: [String] if let customBinDir = Self.customBinDir { process.executableURL = customBinDir.appendingPathComponent("swift-\(tool)") baseArguments = [] } else { #if os(macOS) // xcrun/libxcrun (via the /usr/bin/swift trampoline) is very trigger-happy // to add SDKROOT=.../MacOSX.sdk to our invocations. We avoid this by // 1) invoking the real swift executable (located with `xcrun -f`) and // 2) explicitly removing SDKROOT from the env, as it may be inherited // through the `swift run pack` invocation. process.executableURL = try await Self.swiftURL.value #else process.executableURL = try await ToolRegistry.locate("swift") #endif baseArguments = [tool] } let extraArguments: [String] = [ "--package-path", packagePathOverride ?? packagePath, "--configuration", configuration.rawValue, ] var env = ProcessInfo.processInfo.environment env.removeValue(forKey: "SDKROOT") process.environment = env process.arguments = baseArguments + extraArguments + sdkOptions + options + arguments return process } } public enum BuildConfiguration: String, CaseIterable, Sendable { case debug case release }