From fa30cd0a710295c2fea9d0ab44766888a6bc8db0 Mon Sep 17 00:00:00 2001 From: Kabir Oberai Date: Thu, 10 Sep 2026 17:39:03 -0400 Subject: [PATCH] sdk: move DarwinSDK into PackLib --- Sources/PackLib/DarwinSDK.swift | 91 +++++++++++++++++++++++++ Sources/XToolSupport/SDKCommand.swift | 88 +----------------------- Sources/XToolSupport/SetupCommand.swift | 1 + 3 files changed, 93 insertions(+), 87 deletions(-) create mode 100644 Sources/PackLib/DarwinSDK.swift diff --git a/Sources/PackLib/DarwinSDK.swift b/Sources/PackLib/DarwinSDK.swift new file mode 100644 index 00000000..2386e8b2 --- /dev/null +++ b/Sources/PackLib/DarwinSDK.swift @@ -0,0 +1,91 @@ +import Foundation +import XUtils +import Subprocess + +public struct DarwinSDK { + public let bundle: URL + public let version: String + + public init?(bundle: URL) { + self.bundle = bundle + if let version = try? Data(contentsOf: bundle.appendingPathComponent("darwin-sdk-version.txt")) { + self.version = String(decoding: version, as: UTF8.self) + .trimmingCharacters(in: .whitespacesAndNewlines) + } else if ["darwin.xtoolsdk", "darwin.artifactbundle"].contains(bundle.lastPathComponent) { + self.version = "unknown" + } else { + return nil + } + } + + public static func install(from path: String) async throws { + // we can't just move into ~/.swiftpm/swift-sdks because the swiftpm directory + // location depends on factors like $XDG_CONFIG_HOME. Rather than replicating + // SwiftPM's logic, which may change, it's more reliable to directly invoke + // `swift sdk install`. See: https://github.com/xtool-org/xtool/pull/40 + + let url = URL(fileURLWithPath: path) + guard DarwinSDK(bundle: url) != nil else { throw StringError("Invalid Darwin SDK at '\(path)'")} + + try await addHostClangResourceDir(to: url) + + try await Subprocess.run( + .name("swift"), + arguments: ["sdk", "install", url.path], + output: .discarded + ) + .checkSuccess() + } + + private static func addHostClangResourceDir(to sdk: URL) async throws { + let clangURL = try await ToolRegistry.locate("clang") + let process = try await Subprocess.run( + .path(FilePath(clangURL.path)), + arguments: ["-print-resource-dir"], + output: .string(limit: .max) + ).checkSuccess() + let output = process.standardOutput ?? "" + let hostClangResources = URL(filePath: output.trimmingCharacters(in: .whitespacesAndNewlines)) + let hostInclude = hostClangResources.appending(path: "include") + let sdkInclude = sdk.appending(path: "Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/clang/include") + try await FileManager.default.copyItem(at: hostInclude, to: sdkInclude, preserveOwner: false) + } + + public static func current() async throws -> DarwinSDK? { + let outputString: String + do { + outputString = try await Subprocess.run( + .name("swift"), + arguments: ["sdk", "configure", "darwin", "arm64-apple-ios", "--show-configuration"], + output: .string(limit: .max) + ) + .checkSuccess() + .standardOutput + ?? "" + } catch SubprocessFailure.exited { + return nil + } + + // should be something like + // swiftResourcesPath: /home/user/.swiftpm/swift-sdks/darwin.artifactbundle/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift + // swiftlint:disable:previous line_length + let resourcesPathPrefix = "swiftResourcesPath: " + + guard let resourcesPath = outputString + .split(separator: "\n") + .first(where: { $0.hasPrefix(resourcesPathPrefix) })? + .dropFirst(resourcesPathPrefix.count) + else { return nil } + + var resourcesURL = URL(fileURLWithPath: String(resourcesPath)) + for _ in 0..<6 { + resourcesURL = resourcesURL.deletingLastPathComponent() + } + + return DarwinSDK(bundle: resourcesURL) + } + + public func remove() throws { + try FileManager.default.removeItem(at: bundle) + } +} diff --git a/Sources/XToolSupport/SDKCommand.swift b/Sources/XToolSupport/SDKCommand.swift index 2ac54845..3b0b7202 100644 --- a/Sources/XToolSupport/SDKCommand.swift +++ b/Sources/XToolSupport/SDKCommand.swift @@ -125,96 +125,10 @@ struct DevSDKStatusCommand: AsyncParsableCommand { } } -struct DarwinSDK { - let bundle: URL - let version: String - - init?(bundle: URL) { - self.bundle = bundle - if let version = try? Data(contentsOf: bundle.appendingPathComponent("darwin-sdk-version.txt")) { - self.version = String(decoding: version, as: UTF8.self) - .trimmingCharacters(in: .whitespacesAndNewlines) - } else if ["darwin.xtoolsdk", "darwin.artifactbundle"].contains(bundle.lastPathComponent) { - self.version = "unknown" - } else { - return nil - } - } - - static func install(from path: String) async throws { - // we can't just move into ~/.swiftpm/swift-sdks because the swiftpm directory - // location depends on factors like $XDG_CONFIG_HOME. Rather than replicating - // SwiftPM's logic, which may change, it's more reliable to directly invoke - // `swift sdk install`. See: https://github.com/xtool-org/xtool/pull/40 - - let url = URL(fileURLWithPath: path) - guard DarwinSDK(bundle: url) != nil else { throw Console.Error("Invalid Darwin SDK at '\(path)'")} - - try await addHostClangResourceDir(to: url) - - try await Subprocess.run( - .name("swift"), - arguments: ["sdk", "install", url.path], - output: .discarded - ) - .checkSuccess() - } - - private static func addHostClangResourceDir(to sdk: URL) async throws { - let clangURL = try await ToolRegistry.locate("clang") - let process = try await Subprocess.run( - .path(FilePath(clangURL.path)), - arguments: ["-print-resource-dir"], - output: .string(limit: .max) - ).checkSuccess() - let output = process.standardOutput ?? "" - let hostClangResources = URL(filePath: output.trimmingCharacters(in: .whitespacesAndNewlines)) - let hostInclude = hostClangResources.appending(path: "include") - let sdkInclude = sdk.appending(path: "Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/clang/include") - try await FileManager.default.copyItem(at: hostInclude, to: sdkInclude, preserveOwner: false) - } - - static func current() async throws -> DarwinSDK? { - let outputString: String - do { - outputString = try await Subprocess.run( - .name("swift"), - arguments: ["sdk", "configure", "darwin", "arm64-apple-ios", "--show-configuration"], - output: .string(limit: .max) - ) - .checkSuccess() - .standardOutput - ?? "" - } catch SubprocessFailure.exited { - return nil - } - - // should be something like - // swiftResourcesPath: /home/user/.swiftpm/swift-sdks/darwin.artifactbundle/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift - // swiftlint:disable:previous line_length - let resourcesPathPrefix = "swiftResourcesPath: " - - guard let resourcesPath = outputString - .split(separator: "\n") - .first(where: { $0.hasPrefix(resourcesPathPrefix) })? - .dropFirst(resourcesPathPrefix.count) - else { return nil } - - var resourcesURL = URL(fileURLWithPath: String(resourcesPath)) - for _ in 0..<6 { - resourcesURL = resourcesURL.deletingLastPathComponent() - } - - return DarwinSDK(bundle: resourcesURL) - } - +extension DarwinSDK { func isUpToDate() -> Bool { true } - - func remove() throws { - try FileManager.default.removeItem(at: bundle) - } } private enum SwiftVersion {} diff --git a/Sources/XToolSupport/SetupCommand.swift b/Sources/XToolSupport/SetupCommand.swift index 3546712d..cff7c770 100644 --- a/Sources/XToolSupport/SetupCommand.swift +++ b/Sources/XToolSupport/SetupCommand.swift @@ -1,6 +1,7 @@ import ArgumentParser import Foundation import XKit +import PackLib struct SetupCommand: AsyncParsableCommand { static let configuration = CommandConfiguration(