Skip to content

Commit 2a722d6

Browse files
committed
Allow the "generic Unix" platforms as cross-compile targets
This allows Linux, FreeBSD, and OpenBSD to be used with Swift SDKs for cross compilation, not only as part of the toolchain on the same host as the target.
1 parent 2e1cb56 commit 2a722d6

File tree

7 files changed

+409
-96
lines changed

7 files changed

+409
-96
lines changed

Sources/SWBCore/SwiftSDK.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ public struct SwiftSDK: Sendable {
8585
}
8686

8787
/// Find Swift SDKs installed by SwiftPM.
88-
public static func findSDKs(targetTriples: [String], fs: any FSProxy, hostOperatingSystem: OperatingSystem) throws -> [SwiftSDK] {
88+
public static func findSDKs(targetTriples: [String]?, fs: any FSProxy, hostOperatingSystem: OperatingSystem) throws -> [SwiftSDK] {
8989
return try findSDKs(swiftSDKsDirectory: defaultSwiftSDKsDirectory(hostOperatingSystem: hostOperatingSystem), targetTriples: targetTriples, fs: fs)
9090
}
9191

92-
private static func findSDKs(swiftSDKsDirectory: Path, targetTriples: [String], fs: any FSProxy) throws -> [SwiftSDK] {
92+
private static func findSDKs(swiftSDKsDirectory: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] {
9393
var sdks: [SwiftSDK] = []
9494
// Find .artifactbundle in the SDK directory (e.g. ~/Library/org.swift.swiftpm/swift-sdks)
9595
for artifactBundle in try fs.listdir(swiftSDKsDirectory) {
@@ -118,7 +118,7 @@ public struct SwiftSDK: Sendable {
118118
}
119119

120120
/// Find Swift SDKs in an artifact bundle supporting one of the given targets.
121-
private static func findSDKs(artifactBundle: Path, targetTriples: [String], fs: any FSProxy) throws -> [SwiftSDK] {
121+
private static func findSDKs(artifactBundle: Path, targetTriples: [String]?, fs: any FSProxy) throws -> [SwiftSDK] {
122122
// Load info.json from the artifact bundle
123123
let infoPath = artifactBundle.join("info.json")
124124
guard try fs.isFile(infoPath) else { return [] }
@@ -145,7 +145,9 @@ public struct SwiftSDK: Sendable {
145145

146146
guard let sdk = try SwiftSDK(identifier: identifier, version: artifact.version, path: sdkPath, fs: fs) else { continue }
147147
// Filter out SDKs that don't support any of the target triples.
148-
guard targetTriples.contains(where: { sdk.targetTriples[$0] != nil }) else { continue }
148+
if let targetTriples {
149+
guard targetTriples.contains(where: { sdk.targetTriples[$0] != nil }) else { continue }
150+
}
149151
sdks.append(sdk)
150152
}
151153
}

Sources/SWBGenericUnixPlatform/Plugin.swift

Lines changed: 149 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,6 @@ struct SwiftTargetInfo: Decodable {
4949
let target: TargetInfo
5050
}
5151

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-
}
56-
}
57-
5852
struct GenericUnixDeveloperDirectoryExtension: DeveloperDirectoryExtension {
5953
func fallbackDeveloperDirectory(hostOperatingSystem: OperatingSystem) async throws -> Core.DeveloperPath? {
6054
if hostOperatingSystem == .windows || hostOperatingSystem == .macOS {
@@ -82,13 +76,12 @@ struct GenericUnixPlatformSpecsExtension: SpecificationsExtension {
8276

8377
struct GenericUnixPlatformInfoExtension: PlatformInfoExtension {
8478
func additionalPlatforms(context: any PlatformInfoExtensionAdditionalPlatformsContext) throws -> [(path: Path, data: [String: PropertyListItem])] {
85-
let operatingSystem = context.hostOperatingSystem
86-
guard operatingSystem.createFallbackSystemToolchain else {
87-
return []
88-
}
89-
90-
return try [
91-
(.root, [
79+
return try OperatingSystem.createFallbackSystemToolchains.compactMap { operatingSystem in
80+
// Only create platforms if the host OS allows a fallback toolchain, or we're cross compiling.
81+
guard operatingSystem.createFallbackSystemToolchain || operatingSystem != context.hostOperatingSystem else {
82+
return nil
83+
}
84+
return try (.root, [
9285
"Type": .plString("Platform"),
9386
"Name": .plString(operatingSystem.xcodePlatformName),
9487
"Identifier": .plString(operatingSystem.xcodePlatformName),
@@ -97,78 +90,156 @@ struct GenericUnixPlatformInfoExtension: PlatformInfoExtension {
9790
"FamilyIdentifier": .plString(operatingSystem.xcodePlatformName),
9891
"IsDeploymentPlatform": .plString("YES"),
9992
])
100-
]
93+
}
10194
}
10295
}
10396

10497
struct GenericUnixSDKRegistryExtension: SDKRegistryExtension {
10598
let plugin: GenericUnixPlugin
10699

107100
func additionalSDKs(context: any SDKRegistryExtensionAdditionalSDKsContext) async throws -> [(path: Path, platform: SWBCore.Platform?, data: [String: PropertyListItem])] {
108-
let operatingSystem = context.hostOperatingSystem
109-
guard operatingSystem.createFallbackSystemToolchain, let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName), let swift = plugin.swiftExecutablePath(fs: context.fs) else {
110-
return []
111-
}
101+
return try await OperatingSystem.createFallbackSystemToolchains.asyncMap { operatingSystem in
102+
// Only create SDKs if the host OS allows a fallback toolchain, or we're cross compiling.
103+
guard operatingSystem.createFallbackSystemToolchain || operatingSystem != context.hostOperatingSystem else {
104+
return nil
105+
}
112106

113-
let defaultProperties: [String: PropertyListItem]
114-
switch operatingSystem {
115-
case .linux, .freebsd:
116-
defaultProperties = [
117-
// Workaround to avoid `-dependency_info`.
118-
"LD_DEPENDENCY_INFO_FILE": .plString(""),
119-
120-
"GENERATE_TEXT_BASED_STUBS": "NO",
121-
"GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO",
122-
123-
"CHOWN": "/usr/bin/chown",
124-
"AR": "llvm-ar",
125-
]
126-
default:
127-
defaultProperties = [:]
128-
}
107+
// Don't create any SDKs for the platform if the platform itself isn't registered.
108+
guard let platform = try context.platformRegistry.lookup(name: operatingSystem.xcodePlatformName) else {
109+
return nil
110+
}
129111

130-
let tripleEnvironment: String
131-
switch operatingSystem {
132-
case .linux:
133-
tripleEnvironment = "gnu"
134-
default:
135-
tripleEnvironment = ""
136-
}
112+
var defaultProperties: [String: PropertyListItem]
113+
switch operatingSystem {
114+
case .linux, .freebsd:
115+
defaultProperties = [
116+
// Workaround to avoid `-dependency_info`.
117+
"LD_DEPENDENCY_INFO_FILE": .plString(""),
137118

138-
let swiftTargetInfo = try await plugin.swiftTargetInfo(swiftExecutablePath: swift)
119+
"GENERATE_TEXT_BASED_STUBS": "NO",
120+
"GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO",
139121

140-
let deploymentTargetSettings: [String: PropertyListItem]
141-
if operatingSystem == .freebsd {
142-
guard let tripleVersion = swiftTargetInfo.target.tripleVersion else {
143-
throw StubError.error("Unknown FreeBSD triple version")
122+
"CHOWN": "/usr/bin/chown",
123+
"AR": "llvm-ar",
124+
]
125+
default:
126+
defaultProperties = [:]
144127
}
145-
deploymentTargetSettings = [
146-
"DeploymentTargetSettingName": .plString("FREEBSD_DEPLOYMENT_TARGET"),
147-
"DefaultDeploymentTarget": .plString(tripleVersion),
148-
"MinimumDeploymentTarget": .plString(tripleVersion),
149-
"MaximumDeploymentTarget": .plString(tripleVersion),
150-
]
151-
} else {
152-
deploymentTargetSettings = [:]
153-
}
154128

155-
return try [(.root, platform, [
156-
"Type": .plString("SDK"),
157-
"Version": .plString(Version(ProcessInfo.processInfo.operatingSystemVersion).zeroTrimmed.description),
158-
"CanonicalName": .plString(operatingSystem.xcodePlatformName),
159-
"IsBaseSDK": .plBool(true),
160-
"DefaultProperties": .plDict([
161-
"PLATFORM_NAME": .plString(operatingSystem.xcodePlatformName),
162-
].merging(defaultProperties, uniquingKeysWith: { _, new in new })),
163-
"SupportedTargets": .plDict([
164-
operatingSystem.xcodePlatformName: .plDict([
165-
"Archs": .plArray([.plString(Architecture.hostStringValue ?? "unknown")]),
166-
"LLVMTargetTripleEnvironment": .plString(tripleEnvironment),
167-
"LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName),
168-
"LLVMTargetTripleVendor": .plString("unknown"),
169-
].merging(deploymentTargetSettings, uniquingKeysWith: { _, new in new }))
170-
]),
171-
])]
129+
if operatingSystem == .freebsd || operatingSystem != context.hostOperatingSystem {
130+
// FreeBSD is always LLVM-based, and if we're cross-compiling, use lld
131+
defaultProperties["ALTERNATE_LINKER"] = "lld"
132+
}
133+
134+
let tripleEnvironment: String
135+
switch operatingSystem {
136+
case .linux:
137+
tripleEnvironment = "gnu"
138+
default:
139+
tripleEnvironment = ""
140+
}
141+
142+
let swiftSDK: SwiftSDK?
143+
let sysroot: Path
144+
let architectures: [String]
145+
let tripleVersion: String?
146+
let customProperties: [String: PropertyListItem]
147+
if operatingSystem == context.hostOperatingSystem {
148+
swiftSDK = nil
149+
sysroot = .root
150+
architectures = [Architecture.hostStringValue ?? "unknown"]
151+
tripleVersion = nil
152+
customProperties = [:]
153+
} else {
154+
do {
155+
let swiftSDKs = try SwiftSDK.findSDKs(
156+
targetTriples: nil,
157+
fs: context.fs,
158+
hostOperatingSystem: context.hostOperatingSystem
159+
).filter { sdk in
160+
try sdk.targetTriples.keys.map {
161+
try LLVMTriple($0)
162+
}.contains {
163+
switch operatingSystem {
164+
case .linux:
165+
$0.system == "linux" && $0.environment?.hasPrefix("gnu") == true
166+
case .freebsd:
167+
$0.system == "freebsd"
168+
case .openbsd:
169+
$0.system == "openbsd"
170+
default:
171+
throw StubError.error("Unhandled operating system: \(operatingSystem)")
172+
}
173+
}
174+
}
175+
// FIXME: Do something better than just skipping the platform if more than one SDK matches
176+
swiftSDK = swiftSDKs.only
177+
guard let swiftSDK else {
178+
return nil
179+
}
180+
sysroot = swiftSDK.path
181+
architectures = try swiftSDK.targetTriples.keys.map { try LLVMTriple($0).arch }.sorted()
182+
tripleVersion = try Set(swiftSDK.targetTriples.keys.compactMap { try LLVMTriple($0).systemVersion }).only?.description
183+
customProperties = try Dictionary(uniqueKeysWithValues: swiftSDK.targetTriples.map { targetTriple in
184+
try ("__SYSROOT_\(LLVMTriple(targetTriple.key).arch)", .plString(swiftSDK.path.join(targetTriple.value.sdkRootPath).str))
185+
}).merging([
186+
"SYSROOT": "$(__SYSROOT_$(CURRENT_ARCH))",
187+
188+
// ld.lld: error: -r and --export-dynamic (-rdynamic) may not be used together
189+
"LD_EXPORT_GLOBAL_SYMBOLS": "YES",
190+
], uniquingKeysWith: { _, new in new })
191+
} catch {
192+
// FIXME: Handle errors?
193+
return nil
194+
}
195+
}
196+
197+
let deploymentTargetSettings: [String: PropertyListItem]
198+
if operatingSystem == .freebsd {
199+
let realTripleVersion: String
200+
if context.hostOperatingSystem == operatingSystem {
201+
guard let swift = plugin.swiftExecutablePath(fs: context.fs) else {
202+
throw StubError.error("Cannot locate swift executable path for determining the FreeBSD triple version")
203+
}
204+
let swiftTargetInfo = try await plugin.swiftTargetInfo(swiftExecutablePath: swift)
205+
guard let foundTripleVersion = try swiftTargetInfo.target.triple.version?.description else {
206+
throw StubError.error("Unknown FreeBSD triple version")
207+
}
208+
realTripleVersion = foundTripleVersion
209+
} else if let tripleVersion {
210+
realTripleVersion = tripleVersion
211+
} else {
212+
return nil // couldn't compute triple version for FreeBSD
213+
}
214+
deploymentTargetSettings = [
215+
"DeploymentTargetSettingName": .plString("FREEBSD_DEPLOYMENT_TARGET"),
216+
"DefaultDeploymentTarget": .plString(realTripleVersion),
217+
"MinimumDeploymentTarget": .plString(realTripleVersion),
218+
"MaximumDeploymentTarget": .plString(realTripleVersion),
219+
]
220+
} else {
221+
deploymentTargetSettings = [:]
222+
}
223+
224+
return try (sysroot, platform, [
225+
"Type": .plString("SDK"),
226+
"Version": .plString(Version(ProcessInfo.processInfo.operatingSystemVersion).zeroTrimmed.description),
227+
"CanonicalName": .plString(operatingSystem.xcodePlatformName),
228+
"IsBaseSDK": .plBool(true),
229+
"DefaultProperties": .plDict([
230+
"PLATFORM_NAME": .plString(operatingSystem.xcodePlatformName),
231+
].merging(defaultProperties, uniquingKeysWith: { _, new in new })),
232+
"CustomProperties": .plDict(customProperties),
233+
"SupportedTargets": .plDict([
234+
operatingSystem.xcodePlatformName: .plDict([
235+
"Archs": .plArray(architectures.map { .plString($0) }),
236+
"LLVMTargetTripleEnvironment": .plString(tripleEnvironment),
237+
"LLVMTargetTripleSys": .plString(operatingSystem.xcodePlatformName),
238+
"LLVMTargetTripleVendor": .plString("unknown"),
239+
].merging(deploymentTargetSettings, uniquingKeysWith: { _, new in new }))
240+
]),
241+
])
242+
}.compactMap { $0 }
172243
}
173244
}
174245

@@ -218,8 +289,12 @@ struct GenericUnixToolchainRegistryExtension: ToolchainRegistryExtension {
218289
}
219290

220291
extension OperatingSystem {
292+
static var createFallbackSystemToolchains: [OperatingSystem] {
293+
[.linux, .freebsd, .openbsd]
294+
}
295+
221296
/// Whether the Core is allowed to create a fallback toolchain, SDK, and platform for this operating system in cases where no others have been provided.
222-
var createFallbackSystemToolchain: Bool {
223-
return self == .linux || self == .freebsd || self == .openbsd
297+
fileprivate var createFallbackSystemToolchain: Bool {
298+
return Self.createFallbackSystemToolchains.contains(self)
224299
}
225300
}

Sources/SWBGenericUnixPlatform/Specs/UnixCompile.xcspec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@
3838
{
3939
Name = SDKROOT;
4040
Type = Path;
41+
CommandLineArgs = ();
42+
},
43+
{
44+
Name = SYSROOT;
45+
DefaultValue = "$(SDKROOT)";
46+
Type = Path;
4147
CommandLineFlag = "--sysroot";
4248
},
4349
{
@@ -91,6 +97,12 @@
9197
{
9298
Name = SDKROOT;
9399
Type = Path;
100+
CommandLineArgs = ();
101+
},
102+
{
103+
Name = SYSROOT;
104+
DefaultValue = "$(SDKROOT)";
105+
Type = Path;
94106
CommandLineFlag = "--sysroot";
95107
},
96108
);
@@ -106,6 +118,12 @@
106118
{
107119
Name = SDKROOT;
108120
Type = Path;
121+
CommandLineArgs = ();
122+
},
123+
{
124+
Name = SYSROOT;
125+
DefaultValue = "$(SDKROOT)";
126+
Type = Path;
109127
CommandLineFlag = "--sysroot";
110128
},
111129
);

Sources/SWBGenericUnixPlatform/Specs/UnixLd.xcspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@
5353
{
5454
Name = CLANG_SDKROOT_LINKER_INPUT;
5555
Type = Path;
56-
DefaultValue = "$(SDKROOT)";
56+
DefaultValue = "$(SYSROOT:default=$(SDKROOT))";
5757
Condition = "$(LINKER_DRIVER) == clang";
5858
CommandLineFlag = "--sysroot";
5959
IsInputDependency = Yes;
6060
},
6161
{
6262
Name = SWIFTC_SDKROOT_LINKER_INPUT;
6363
Type = Path;
64-
DefaultValue = "$(SDKROOT)";
64+
DefaultValue = "$(SYSROOT:default=$(SDKROOT))";
6565
Condition = "$(LINKER_DRIVER) == swiftc";
6666
CommandLineFlag = "-sysroot";
6767
IsInputDependency = Yes;

0 commit comments

Comments
 (0)