Skip to content

Commit 7e60510

Browse files
committed
Handle the OS version in the triple for FreeBSD
We're going to keep the versioned triple, so it needs to be passed through after all.
1 parent d1656be commit 7e60510

File tree

7 files changed

+153
-79
lines changed

7 files changed

+153
-79
lines changed

Sources/SWBAndroidPlatform/AndroidSDK.swift

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,6 @@ public import Foundation
134134
case bits64 = 64
135135
}
136136

137-
@_spi(Testing) public struct LLVMTriple: Codable, Equatable, Sendable {
138-
public var arch: String
139-
public var vendor: String
140-
public var system: String
141-
public var environment: String
142-
143-
var description: String {
144-
"\(arch)-\(vendor)-\(system)-\(environment)"
145-
}
146-
147-
public init(from decoder: any Decoder) throws {
148-
let container = try decoder.singleValueContainer()
149-
let triple = try container.decode(String.self)
150-
if let match = try #/(?<arch>.+)-(?<vendor>.+)-(?<system>.+)-(?<environment>.+)/#.wholeMatch(in: triple) {
151-
self.arch = String(match.output.arch)
152-
self.vendor = String(match.output.vendor)
153-
self.system = String(match.output.system)
154-
self.environment = String(match.output.environment)
155-
} else {
156-
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid triple string: \(triple)")
157-
}
158-
}
159-
}
160-
161137
public let bitness: Bitness
162138
public let `default`: Bool
163139
public let deprecated: Bool

Sources/SWBAndroidPlatform/Plugin.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,14 @@ struct AndroidPlatformExtension: PlatformInfoExtension {
131131
let abis = androidNdk.abis
132132
let deploymentTargetRange = androidNdk.deploymentTargetRange
133133

134-
let allPossibleTriples = abis.values.flatMap { abi in
135-
(max(deploymentTargetRange.min, abi.min_os_version)...deploymentTargetRange.max).map { deploymentTarget in
134+
let allPossibleTriples = try abis.values.flatMap { abi in
135+
try (max(deploymentTargetRange.min, abi.min_os_version)...deploymentTargetRange.max).map { deploymentTarget in
136136
var triple = abi.llvm_triple
137137
triple.vendor = "unknown" // Android NDK uses "none", Swift SDKs use "unknown"
138-
triple.environment += "\(deploymentTarget)"
138+
guard let env = triple.environment else {
139+
throw StubError.error("Android triples must have an environment")
140+
}
141+
triple.environment = "\(env)\(deploymentTarget)"
139142
return triple
140143
}
141144
}.map(\.description)
@@ -173,8 +176,8 @@ struct AndroidPlatformExtension: PlatformInfoExtension {
173176
"CustomProperties": .plDict([
174177
// Unlike most platforms, the Android version goes on the environment field rather than the system field
175178
// FIXME: Make this configurable in a better way so we don't need to push build settings at the SDK definition level
176-
"LLVM_TARGET_TRIPLE_OS_VERSION": .plString("linux"),
177-
"LLVM_TARGET_TRIPLE_SUFFIX": .plString("-android$(ANDROID_DEPLOYMENT_TARGET)"),
179+
"LLVM_TARGET_TRIPLE_OS_VERSION": .plString("$(SWIFT_PLATFORM_TARGET_PREFIX)"),
180+
"LLVM_TARGET_TRIPLE_SUFFIX": .plString("-android$($(DEPLOYMENT_TARGET_SETTING_NAME))"),
178181
].merging(swiftSettings, uniquingKeysWith: { _, new in new })),
179182
"SupportedTargets": .plDict([
180183
"android": .plDict([

Sources/SWBGenericUnixPlatform/Plugin.swift

Lines changed: 91 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,44 @@ import SWBCore
1515
import Foundation
1616

1717
@PluginExtensionSystemActor public func initializePlugin(_ manager: PluginManager) {
18+
let plugin = GenericUnixPlugin()
1819
manager.register(GenericUnixDeveloperDirectoryExtension(), type: DeveloperDirectoryExtensionPoint.self)
1920
manager.register(GenericUnixPlatformSpecsExtension(), type: SpecificationsExtensionPoint.self)
2021
manager.register(GenericUnixPlatformInfoExtension(), type: PlatformInfoExtensionPoint.self)
21-
manager.register(GenericUnixSDKRegistryExtension(), type: SDKRegistryExtensionPoint.self)
22-
manager.register(GenericUnixToolchainRegistryExtension(), type: ToolchainRegistryExtensionPoint.self)
22+
manager.register(GenericUnixSDKRegistryExtension(plugin: plugin), type: SDKRegistryExtensionPoint.self)
23+
manager.register(GenericUnixToolchainRegistryExtension(plugin: plugin), type: ToolchainRegistryExtensionPoint.self)
24+
}
25+
26+
final class GenericUnixPlugin: Sendable {
27+
func swiftExecutablePath(fs: any FSProxy) -> Path? {
28+
[
29+
Environment.current["SWIFT_EXEC"].map(Path.init),
30+
StackedSearchPath(environment: .current, fs: fs).lookup(Path("swift"))
31+
].compactMap { $0 }.first(where: fs.exists)
32+
}
33+
34+
func swiftTargetInfo(swiftExecutablePath: Path) async throws -> SwiftTargetInfo {
35+
let args = ["-print-target-info"]
36+
let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: swiftExecutablePath.str), arguments: args)
37+
guard executionResult.exitStatus.isSuccess else {
38+
throw RunProcessNonZeroExitError(args: [swiftExecutablePath.str] + args, workingDirectory: nil, environment: [:], status: executionResult.exitStatus, stdout: ByteString(executionResult.stdout), stderr: ByteString(executionResult.stderr))
39+
}
40+
return try JSONDecoder().decode(SwiftTargetInfo.self, from: executionResult.stdout)
41+
}
42+
}
43+
44+
struct SwiftTargetInfo: Decodable {
45+
struct TargetInfo: Decodable {
46+
let triple: LLVMTriple
47+
let unversionedTriple: LLVMTriple
48+
}
49+
let target: TargetInfo
50+
}
51+
52+
extension SwiftTargetInfo.TargetInfo {
53+
var tripleVersion: String? {
54+
triple != unversionedTriple && triple.system.hasPrefix(unversionedTriple.system) ? String(triple.system.dropFirst(unversionedTriple.system.count)).nilIfEmpty : nil
55+
}
2356
}
2457

2558
struct GenericUnixDeveloperDirectoryExtension: DeveloperDirectoryExtension {
@@ -65,9 +98,11 @@ struct GenericUnixPlatformInfoExtension: PlatformInfoExtension {
6598
}
6699

67100
struct GenericUnixSDKRegistryExtension: SDKRegistryExtension {
101+
let plugin: GenericUnixPlugin
102+
68103
func additionalSDKs(context: any SDKRegistryExtensionAdditionalSDKsContext) async throws -> [(path: Path, platform: SWBCore.Platform?, data: [String: PropertyListItem])] {
69104
let operatingSystem = context.hostOperatingSystem
70-
guard operatingSystem.createFallbackSystemToolchain, let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName) else {
105+
guard operatingSystem.createFallbackSystemToolchain, let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName), let swift = plugin.swiftExecutablePath(fs: context.fs) else {
71106
return []
72107
}
73108

@@ -96,6 +131,23 @@ struct GenericUnixSDKRegistryExtension: SDKRegistryExtension {
96131
tripleEnvironment = ""
97132
}
98133

134+
let swiftTargetInfo = try await plugin.swiftTargetInfo(swiftExecutablePath: swift)
135+
136+
let deploymentTargetSettings: [String: PropertyListItem]
137+
if operatingSystem == .freebsd {
138+
guard let tripleVersion = swiftTargetInfo.target.tripleVersion else {
139+
throw StubError.error("Unknown FreeBSD triple version")
140+
}
141+
deploymentTargetSettings = [
142+
"DeploymentTargetSettingName": .plString("FREEBSD_DEPLOYMENT_TARGET"),
143+
"DefaultDeploymentTarget": .plString(tripleVersion),
144+
"MinimumDeploymentTarget": .plString(tripleVersion),
145+
"MaximumDeploymentTarget": .plString(tripleVersion),
146+
]
147+
} else {
148+
deploymentTargetSettings = [:]
149+
}
150+
99151
return try [(.root, platform, [
100152
"Type": .plString("SDK"),
101153
"Version": .plString(Version(ProcessInfo.processInfo.operatingSystemVersion).zeroTrimmed.description),
@@ -110,62 +162,54 @@ struct GenericUnixSDKRegistryExtension: SDKRegistryExtension {
110162
"LLVMTargetTripleEnvironment": .plString(tripleEnvironment),
111163
"LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName),
112164
"LLVMTargetTripleVendor": .plString("unknown"),
113-
])
165+
].merging(deploymentTargetSettings, uniquingKeysWith: { _, new in new }))
114166
]),
115167
])]
116168
}
117169
}
118170

119171
struct GenericUnixToolchainRegistryExtension: ToolchainRegistryExtension {
172+
let plugin: GenericUnixPlugin
173+
120174
func additionalToolchains(context: any ToolchainRegistryExtensionAdditionalToolchainsContext) async throws -> [Toolchain] {
121175
let operatingSystem = context.hostOperatingSystem
122-
guard operatingSystem.createFallbackSystemToolchain else {
176+
let fs = context.fs
177+
guard operatingSystem.createFallbackSystemToolchain, let swift = plugin.swiftExecutablePath(fs: fs) else {
123178
return []
124179
}
125180

126-
let fs = context.fs
127-
128-
for swift in [
129-
Environment.current["SWIFT_EXEC"].map(Path.init),
130-
StackedSearchPath(environment: .current, fs: fs).lookup(Path("swift"))
131-
].compactMap(\.self) {
132-
if fs.exists(swift) {
133-
let realSwiftPath = try fs.realpath(swift).dirname.normalize()
134-
let hasUsrBin = realSwiftPath.str.hasSuffix("/usr/bin")
135-
let hasUsrLocalBin = realSwiftPath.str.hasSuffix("/usr/local/bin")
136-
let path: Path
137-
switch (hasUsrBin, hasUsrLocalBin) {
138-
case (true, false):
139-
path = realSwiftPath.dirname.dirname
140-
case (false, true):
141-
path = realSwiftPath.dirname.dirname.dirname
142-
case (false, false):
143-
throw StubError.error("Unexpected toolchain layout for Swift installation path: \(realSwiftPath)")
144-
case (true, true):
145-
preconditionFailure()
146-
}
147-
let llvmDirectories = try Array(fs.listdir(Path("/usr/lib")).filter { $0.hasPrefix("llvm-") }.sorted().reversed())
148-
let llvmDirectoriesLocal = try Array(fs.listdir(Path("/usr/local")).filter { $0.hasPrefix("llvm") }.sorted().reversed())
149-
return [
150-
Toolchain(
151-
identifier: ToolchainRegistry.defaultToolchainIdentifier,
152-
displayName: "Default",
153-
version: Version(),
154-
aliases: ["default"],
155-
path: path,
156-
frameworkPaths: [],
157-
libraryPaths: llvmDirectories.map { "/usr/lib/\($0)/lib" } + llvmDirectoriesLocal.map { "/usr/local/\($0)/lib" } + ["/usr/lib64"],
158-
defaultSettings: [:],
159-
overrideSettings: [:],
160-
defaultSettingsWhenPrimary: [:],
161-
executableSearchPaths: realSwiftPath.dirname.relativeSubpath(from: path).map { [path.join($0).join("bin")] } ?? [],
162-
testingLibraryPlatformNames: [],
163-
fs: fs)
164-
]
165-
}
181+
let realSwiftPath = try fs.realpath(swift).dirname.normalize()
182+
let hasUsrBin = realSwiftPath.str.hasSuffix("/usr/bin")
183+
let hasUsrLocalBin = realSwiftPath.str.hasSuffix("/usr/local/bin")
184+
let path: Path
185+
switch (hasUsrBin, hasUsrLocalBin) {
186+
case (true, false):
187+
path = realSwiftPath.dirname.dirname
188+
case (false, true):
189+
path = realSwiftPath.dirname.dirname.dirname
190+
case (false, false):
191+
throw StubError.error("Unexpected toolchain layout for Swift installation path: \(realSwiftPath)")
192+
case (true, true):
193+
preconditionFailure()
166194
}
167-
168-
return []
195+
let llvmDirectories = try Array(fs.listdir(Path("/usr/lib")).filter { $0.hasPrefix("llvm-") }.sorted().reversed())
196+
let llvmDirectoriesLocal = try Array(fs.listdir(Path("/usr/local")).filter { $0.hasPrefix("llvm") }.sorted().reversed())
197+
return [
198+
Toolchain(
199+
identifier: ToolchainRegistry.defaultToolchainIdentifier,
200+
displayName: "Default",
201+
version: Version(),
202+
aliases: ["default"],
203+
path: path,
204+
frameworkPaths: [],
205+
libraryPaths: llvmDirectories.map { "/usr/lib/\($0)/lib" } + llvmDirectoriesLocal.map { "/usr/local/\($0)/lib" } + ["/usr/lib64"],
206+
defaultSettings: [:],
207+
overrideSettings: [:],
208+
defaultSettingsWhenPrimary: [:],
209+
executableSearchPaths: realSwiftPath.dirname.relativeSubpath(from: path).map { [path.join($0).join("bin")] } ?? [],
210+
testingLibraryPlatformNames: [],
211+
fs: fs)
212+
]
169213
}
170214
}
171215

Sources/SWBTestSupport/CoreTestSupport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension Core {
5656
developerPath = .xcode(xcodeDeveloperDirPath)
5757
} else {
5858
// In the context of auto-generated package schemes, try to infer the active Xcode.
59-
let potentialDeveloperPath = getEnvironmentVariable("PATH")?.components(separatedBy: String(Path.pathEnvironmentSeparator)).first.map(Path.init)?.dirname.dirname
59+
let potentialDeveloperPath = getEnvironmentVariable(.path)?.components(separatedBy: String(Path.pathEnvironmentSeparator)).first.map(Path.init)?.dirname.dirname
6060
let versionInfo = potentialDeveloperPath?.dirname.join("version.plist")
6161
if let versionInfo = versionInfo, (try? PropertyList.fromPath(versionInfo, fs: localFS))?.dictValue?["ProjectName"] == "IDEApplication" {
6262
developerPath = potentialDeveloperPath.map { .xcode($0) }

Sources/SWBUtil/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ add_library(SWBUtil
5454
LazyCache.swift
5555
Library.swift
5656
LineReader.swift
57+
LLVMTriple.swift
5758
Lock.swift
5859
MachO.swift
5960
Math.swift

Sources/SWBUtil/LLVMTriple.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public struct LLVMTriple: Decodable, Equatable, Sendable, CustomStringConvertible {
14+
public var arch: String
15+
public var vendor: String
16+
public var system: String
17+
public var environment: String?
18+
19+
public var description: String {
20+
if let environment {
21+
return "\(arch)-\(vendor)-\(system)-\(environment)"
22+
}
23+
return "\(arch)-\(vendor)-\(system)"
24+
}
25+
26+
public init(_ string: String) throws {
27+
guard let match = try #/(?<arch>[^-]+)-(?<vendor>[^-]+)-(?<system>[^-]+)(-(?<environment>[^-]+))?/#.wholeMatch(in: string) else {
28+
throw LLVMTripleError.invalidTripleStringFormat(string)
29+
}
30+
self.arch = String(match.output.arch)
31+
self.vendor = String(match.output.vendor)
32+
self.system = String(match.output.system)
33+
self.environment = match.output.environment.map { String($0) }
34+
}
35+
36+
public init(from decoder: any Swift.Decoder) throws {
37+
self = try Self(decoder.singleValueContainer().decode(String.self))
38+
}
39+
}
40+
41+
enum LLVMTripleError: Error, CustomStringConvertible {
42+
case invalidTripleStringFormat(String)
43+
44+
var description: String {
45+
switch self {
46+
case let .invalidTripleStringFormat(tripleString):
47+
"Invalid triple string format: \(tripleString)"
48+
}
49+
}
50+
}

Sources/SWBWindowsPlatform/VSInstallation.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ public struct VSInstallation: Decodable, Sendable {
5757
]
5858
let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: vswhere.str), arguments: args)
5959
guard executionResult.exitStatus.isSuccess else {
60-
throw RunProcessNonZeroExitError(args: args, workingDirectory: nil, environment: [:], status: executionResult.exitStatus, stdout: ByteString(executionResult.stdout), stderr: ByteString(executionResult.stderr))
60+
throw RunProcessNonZeroExitError(args: [vswhere.str] + args, workingDirectory: nil, environment: [:], status: executionResult.exitStatus, stdout: ByteString(executionResult.stdout), stderr: ByteString(executionResult.stderr))
6161
}
6262
return try JSONDecoder().decode([VSInstallation].self, from: executionResult.stdout)
6363
}
6464

6565
private static func vswherePath(fs: any FSProxy) throws -> Path? {
6666
var paths: [Path] = []
67-
if let path = try POSIX.getenv("PATH") {
67+
if let path = getEnvironmentVariable(.path) {
6868
paths.append(contentsOf: path.split(separator: Path.pathEnvironmentSeparator).map(Path.init).filter {
6969
// PATH may contain unexpanded shell variable references
7070
$0.isAbsolute

0 commit comments

Comments
 (0)