mirror of
https://github.com/xtool-org/xtool.git
synced 2026-10-12 23:49:20 +02:00
## What does this PR do? When building the SDK, derives the clang resource dir from the installed Swift toolchain (`swiftc -print-target-info`) instead of `clang -print-resource-dir`. Speculatively fixes #273. ## How was it tested? Validated that I can still build on Linux ## AI tool usage How much of this PR was AI-assisted? (check one) - [ ] **0** - No AI was used to write code - [x] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved detection of the host Clang resource directory by using Swift compiler target information, helping ensure more reliable compiler setup across supported environments. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
133 lines
5.1 KiB
Swift
133 lines
5.1 KiB
Swift
import Foundation
|
|
import XUtils
|
|
import Subprocess
|
|
import Superutils
|
|
|
|
public struct DarwinSDK {
|
|
package struct TemporaryBundle: ~Copyable {
|
|
package let url: URL
|
|
|
|
fileprivate init(url: URL) {
|
|
self.url = url
|
|
}
|
|
|
|
package consuming func install() async throws {
|
|
guard DarwinSDK(bundle: url) != nil else {
|
|
throw StringError("Invalid Darwin SDK at '\(url.path)'")
|
|
}
|
|
|
|
try await DarwinSDK.addHostClangResourceDir(to: url)
|
|
|
|
let destination = url.deletingLastPathComponent()
|
|
.appendingPathComponent("darwin.artifactbundle", isDirectory: true)
|
|
try FileManager.default.moveItem(at: url, to: destination)
|
|
}
|
|
|
|
deinit {
|
|
try? FileManager.default.removeItem(at: url)
|
|
}
|
|
}
|
|
|
|
public enum Flavor {
|
|
// can't be updated in place
|
|
case slim
|
|
// can be updated in place, includes a whole copy of Xcode.app
|
|
case normal
|
|
// from before the slim/normal split existed (version "develop")
|
|
case legacy
|
|
}
|
|
|
|
public let bundle: URL
|
|
public let version: String
|
|
public let flavor: Flavor
|
|
|
|
static func swiftPMDirectory(
|
|
environment: [String: String] = ProcessInfo.processInfo.environment,
|
|
homeDirectory: URL = FileManager.default.homeDirectoryForCurrentUser
|
|
) throws -> URL {
|
|
if let configurationDirectory = environment["XDG_CONFIG_HOME"] {
|
|
guard (configurationDirectory as NSString).isAbsolutePath else {
|
|
throw StringError("XDG_CONFIG_HOME must be an absolute path: '\(configurationDirectory)'")
|
|
}
|
|
return URL(fileURLWithPath: configurationDirectory, isDirectory: true)
|
|
.appendingPathComponent("swiftpm", isDirectory: true)
|
|
} else {
|
|
return homeDirectory.appendingPathComponent(".swiftpm", isDirectory: true)
|
|
}
|
|
}
|
|
|
|
private static func swiftSDKsDirectory(
|
|
environment: [String: String] = ProcessInfo.processInfo.environment,
|
|
homeDirectory: URL = FileManager.default.homeDirectoryForCurrentUser
|
|
) throws -> URL {
|
|
try swiftPMDirectory(environment: environment, homeDirectory: homeDirectory)
|
|
.appendingPathComponent("swift-sdks", isDirectory: true)
|
|
}
|
|
|
|
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", "darwin.artifactbundle.tmp"].contains(bundle.lastPathComponent) {
|
|
self.version = "develop"
|
|
} else {
|
|
return nil
|
|
}
|
|
|
|
if version == "develop" {
|
|
self.flavor = .legacy
|
|
} else if bundle.appendingPathComponent("Xcode.app").dirExists {
|
|
self.flavor = .normal
|
|
} else {
|
|
self.flavor = .slim
|
|
}
|
|
}
|
|
|
|
package static func prepareTemporaryBundle(
|
|
environment: [String: String] = ProcessInfo.processInfo.environment,
|
|
homeDirectory: URL = FileManager.default.homeDirectoryForCurrentUser
|
|
) throws -> TemporaryBundle {
|
|
let sdksDirectory = try swiftSDKsDirectory(environment: environment, homeDirectory: homeDirectory)
|
|
try FileManager.default.createDirectory(at: sdksDirectory, withIntermediateDirectories: true)
|
|
let bundle = sdksDirectory.appendingPathComponent("darwin.artifactbundle.tmp", isDirectory: true)
|
|
if FileManager.default.fileExists(atPath: bundle.path) {
|
|
try FileManager.default.removeItem(at: bundle)
|
|
}
|
|
return TemporaryBundle(url: bundle)
|
|
}
|
|
|
|
private static func addHostClangResourceDir(to sdk: URL) async throws {
|
|
struct SwiftTargetInfo: Decodable {
|
|
struct Paths: Decodable {
|
|
let runtimeResourcePath: String
|
|
}
|
|
let paths: Paths
|
|
}
|
|
|
|
let process = try await Subprocess.run(
|
|
.path(try await BuildSettings.swiftcURL()),
|
|
arguments: ["-print-target-info"],
|
|
output: .data(limit: .max)
|
|
).checkSuccess()
|
|
let targetInfo = try JSONDecoder().decode(
|
|
SwiftTargetInfo.self,
|
|
from: process.standardOutput
|
|
)
|
|
let hostClangResources = URL(filePath: targetInfo.paths.runtimeResourcePath).appending(path: "clang")
|
|
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() throws -> DarwinSDK? {
|
|
let bundle = try swiftSDKsDirectory().appendingPathComponent("darwin.artifactbundle", isDirectory: true)
|
|
guard bundle.dirExists else { return nil }
|
|
return DarwinSDK(bundle: bundle)
|
|
}
|
|
|
|
public func remove() throws {
|
|
try FileManager.default.removeItem(at: bundle)
|
|
}
|
|
}
|