Skip to content

Commit 414a184

Browse files
committed
Add support for the libswiftCompatibilitySpan.dylib backward deployment library
1 parent aaa84a0 commit 414a184

File tree

6 files changed

+65
-10
lines changed

6 files changed

+65
-10
lines changed

Sources/SWBCore/PlatformRegistry.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ public final class Platform: Sendable {
7373
/// Minimum OS version for Swift-in-the-OS support. If this is `nil`, the platform does not support Swift-in-the-OS at all.
7474
fileprivate(set) var minimumOSForSwiftInTheOS: Version? = nil
7575

76-
/// Minimum OS version for built-in Swift concurrency support. If this is `nil`, the platform does not support Swift concurrency at all.
76+
/// Minimum OS version for Swift concurrency (Swift 5.5). If this is `nil`, the platform does not support Swift concurrency at all.
7777
fileprivate(set) var minimumOSForSwiftConcurrency: Version? = nil
7878

79+
/// Minimum OS version for Span in the standard library (Swift 6.2). If this is `nil`, the platform does not support Swift concurrency at all.
80+
fileprivate(set) var minimumOSForSwiftSpan: Version? = nil
81+
7982
/// The canonical name of the public SDK for this platform.
8083
/// - remark: This does not mean that this SDK exists, just that this is its canonical name if it does exist.
8184
@_spi(Testing) public let sdkCanonicalName: String?
@@ -244,6 +247,11 @@ extension Platform {
244247
return self.minimumOSForSwiftConcurrency ?? self.correspondingDevicePlatform?.minimumOSForSwiftConcurrency ?? nil
245248
}
246249

250+
/// Determines the deployment version to use for Swift Span support.
251+
fileprivate func swiftOSSpanVersion(_ deploymentTarget: StringMacroDeclaration) -> Version? {
252+
return self.minimumOSForSwiftSpan ?? self.correspondingDevicePlatform?.minimumOSForSwiftSpan ?? nil
253+
}
254+
247255
/// Determines if the platform supports Swift in the OS.
248256
public func supportsSwiftInTheOS(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool {
249257
guard let deploymentTarget = self.deploymentTargetMacro else { return false }
@@ -265,7 +273,7 @@ extension Platform {
265273
return version >= minimumSwiftInTheOSVersion
266274
}
267275

268-
/// Determines if the platform natively supports Swift concurrency. If `false`, then the Swift back-compat concurrency libs needs to be copied into the app/framework's bundle.
276+
/// Determines if the platform natively supports Swift concurrency. If `false`, then the Swift concurrency back-compat concurrency libs needs to be copied into the app/framework's bundle.
269277
public func supportsSwiftConcurrencyNatively(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool? {
270278
guard let deploymentTarget = self.deploymentTargetMacro else { return false }
271279

@@ -287,6 +295,29 @@ extension Platform {
287295

288296
return version >= minimumSwiftConcurrencyVersion
289297
}
298+
299+
/// Determines if the platform natively supports Swift 6.2's Span type. If `false`, then the Swift Span back-compat concurrency libs needs to be copied into the app/framework's bundle.
300+
public func supportsSwiftSpanNatively(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool? {
301+
guard let deploymentTarget = self.deploymentTargetMacro else { return false }
302+
303+
// If we have target device info and its platform matches the build platform, compare the device OS version
304+
let targetDeviceVersion: Version?
305+
if considerTargetDeviceOSVersion && scope.evaluate(BuiltinMacros.TARGET_DEVICE_PLATFORM_NAME) == self.name {
306+
targetDeviceVersion = try? Version(scope.evaluate(BuiltinMacros.TARGET_DEVICE_OS_VERSION))
307+
} else {
308+
targetDeviceVersion = nil
309+
}
310+
311+
// Otherwise fall back to comparing the minimum deployment target
312+
let deploymentTargetVersion = try? Version(scope.evaluate(deploymentTarget))
313+
314+
guard let version = targetDeviceVersion ?? deploymentTargetVersion else { return false }
315+
316+
// Return `nil` here as there is no metadata for the platform to allow downstream clients to be aware of this.
317+
guard let minimumSwiftSpanVersion = swiftOSSpanVersion(deploymentTarget) else { return nil }
318+
319+
return version >= minimumSwiftSpanVersion
320+
}
290321
}
291322

292323
extension Platform: CustomStringConvertible {
@@ -676,6 +707,7 @@ public final class PlatformRegistry {
676707
if let variant = platform.defaultSDKVariant {
677708
platform.minimumOSForSwiftInTheOS = variant.minimumOSForSwiftInTheOS
678709
platform.minimumOSForSwiftConcurrency = variant.minimumOSForSwiftConcurrency
710+
platform.minimumOSForSwiftSpan = variant.minimumOSForSwiftSpan
679711
}
680712
}
681713

Sources/SWBCore/SDKRegistry.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,12 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {
313313
/// Minimum OS version for Swift-in-the-OS support. If this is `nil`, the platform does not support Swift-in-the-OS at all.
314314
public let minimumOSForSwiftInTheOS: Version?
315315

316-
/// Minimum OS version for built-in Swift concurrency support. If this is `nil`, the platform does not support Swift concurrency at all.
316+
/// Minimum OS version for built-in Swift concurrency support (Swift 5.5). If this is `nil`, the platform does not support Swift concurrency at all.
317317
public let minimumOSForSwiftConcurrency: Version?
318318

319+
/// Minimum OS version for built-in Swift Span support (Swift 6.2). If this is `nil`, the platform does not support Swift Span at all.
320+
public let minimumOSForSwiftSpan: Version?
321+
319322
/// The path prefix under which all built content produced by this SDK variant should be installed, relative to the system root.
320323
///
321324
/// Empty string if content should be installed directly into the system root (default).
@@ -392,9 +395,10 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {
392395

393396
self.clangRuntimeLibraryPlatformName = supportedTargetDict["ClangRuntimeLibraryPlatformName"]?.stringValue ?? Self.fallbackClangRuntimeLibraryPlatformName(variantName: name)
394397

395-
let (os, concurrency) = Self.fallbackSwiftVersions(variantName: name)
398+
let (os, concurrency, span) = Self.fallbackSwiftVersions(variantName: name)
396399
self.minimumOSForSwiftInTheOS = try (supportedTargetDict["SwiftOSRuntimeMinimumDeploymentTarget"]?.stringValue ?? os).map { try Version($0) }
397400
self.minimumOSForSwiftConcurrency = try (supportedTargetDict["SwiftConcurrencyMinimumDeploymentTarget"]?.stringValue ?? concurrency).map { try Version($0) }
401+
self.minimumOSForSwiftSpan = try (supportedTargetDict["SwiftSpanMinimumDeploymentTarget"]?.stringValue ?? span).map { try Version($0) }
398402

399403
self.systemPrefix = supportedTargetDict["SystemPrefix"]?.stringValue ?? {
400404
switch name {
@@ -445,12 +449,12 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {
445449
}
446450
}
447451

448-
private static func fallbackSwiftVersions(variantName name: String) -> (String?, String?) {
452+
private static func fallbackSwiftVersions(variantName name: String) -> (os: String?, concurrency: String?, span: String?) {
449453
switch name {
450454
case "macos", "macosx":
451-
return ("10.14.4", "12.0")
455+
return ("10.14.4", "12.0", nil)
452456
default:
453-
return (nil, nil)
457+
return (nil, nil, nil)
454458
}
455459
}
456460

Sources/SWBCore/SpecImplementations/Tools/SwiftStdLibTool.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public final class SwiftStdLibToolSpec : GenericCommandLineToolSpec, SpecIdentif
2222
}
2323

2424
/// Construct a new task to run the Swift standard library tool.
25-
public func constructSwiftStdLibraryToolTask(_ cbc:CommandBuildContext, _ delegate: any TaskGenerationDelegate, foldersToScan: MacroStringListExpression?, filterForSwiftOS: Bool, backDeploySwiftConcurrency: Bool) async {
25+
public func constructSwiftStdLibraryToolTask(_ cbc:CommandBuildContext, _ delegate: any TaskGenerationDelegate, foldersToScan: MacroStringListExpression?, filterForSwiftOS: Bool, backDeploySwiftConcurrency: Bool, backDeploySwiftSpan: Bool) async {
2626
precondition(cbc.outputs.isEmpty, "Unexpected output paths \(cbc.outputs.map { "'\($0.str)'" }) passed to \(type(of: self)).")
2727

2828
let input = cbc.input
@@ -85,6 +85,10 @@ public final class SwiftStdLibToolSpec : GenericCommandLineToolSpec, SpecIdentif
8585
commandLine.append("--back-deploy-swift-concurrency")
8686
}
8787

88+
if backDeploySwiftSpan {
89+
commandLine.append("--back-deploy-swift-span")
90+
}
91+
8892
let outputs = [delegate.createVirtualNode("CopySwiftStdlib \(wrapperPathString.str)")]
8993

9094
delegate.createTask(type: self, dependencyData: .dependencyInfo(dependencyInfoFilePath), ruleInfo: ruleInfo, commandLine: commandLine, environment: EnvironmentBindings(environment.map { ($0, $1) }), workingDirectory: cbc.producer.defaultWorkingDirectory, inputs: [ delegate.createNode(input.absolutePath) ], outputs: outputs, mustPrecede: [], action: action, execDescription: resolveExecutionDescription(cbc, delegate, lookup: lookup), enableSandboxing: enableSandboxing)

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/SwiftStandardLibrariesTaskProducer.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,13 @@ final class SwiftStandardLibrariesTaskProducer: PhasedTaskProducer, TaskProducer
104104
let supportsConcurrencyNatively = context.platform?.supportsSwiftConcurrencyNatively(scope)
105105
let backDeploySwiftConcurrency = supportsConcurrencyNatively != nil && supportsConcurrencyNatively != true
106106

107+
let supportsSpanNatively = context.platform?.supportsSwiftSpanNatively(scope)
108+
let backDeploySwiftSpan = supportsSpanNatively != nil && supportsSpanNatively != true
109+
107110
let cbc = CommandBuildContext(producer: context, scope: scope, inputs: [ input ])
108111
let foldersToScanExpr: MacroStringListExpression? = foldersToScan.count > 0 ? scope.namespace.parseLiteralStringList(foldersToScan): nil
109112
await appendGeneratedTasks(&tasks) { delegate in
110-
await context.swiftStdlibToolSpec.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: foldersToScanExpr, filterForSwiftOS: filterForSwiftOS, backDeploySwiftConcurrency: backDeploySwiftConcurrency)
113+
await context.swiftStdlibToolSpec.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: foldersToScanExpr, filterForSwiftOS: filterForSwiftOS, backDeploySwiftConcurrency: backDeploySwiftConcurrency, backDeploySwiftSpan: backDeploySwiftSpan)
111114
}
112115
}
113116

Sources/SWBTaskExecution/TaskActions/EmbedSwiftStdLibTaskAction.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,18 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
160160
// If true, then the Swift concurrency dylibs should be copied into the app/framework's bundles.
161161
var backDeploySwiftConcurrency = false
162162

163+
// If true, then the Swift Span dylibs should be copied into the app/framework's bundles.
164+
var backDeploySwiftSpan = false
165+
163166
// The allowed list of libraries that should *not* be filtered when `filterForSwiftOS=true`.
164167
let allowedLibsForSwiftOS = ["libswiftXCTest" ]
165168

166169
// The allowed list of libraries that should *not* be filtered when `backDeploySwiftConcurrency=true`.
167170
let allowedLibsForSwiftConcurrency = ["libswift_Concurrency"]
168171

172+
// The allowed list of libraries that should *not* be filtered when `backDeploySwiftSpan=true`.
173+
let allowedLibsForSwiftSpan = ["libswiftCompatibilitySpan"]
174+
169175
func absolutePath(_ path: Path) -> Path {
170176
return path.isAbsolute ? path : task.workingDirectory.join(path)
171177
}
@@ -368,6 +374,9 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
368374
case "--back-deploy-swift-concurrency":
369375
self.backDeploySwiftConcurrency = true
370376

377+
case "--back-deploy-swift-span":
378+
self.backDeploySwiftSpan = true
379+
371380
default:
372381
throw StubError.error("unrecognized argument: \(arg)")
373382
}
@@ -787,6 +796,9 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
787796
if backDeploySwiftConcurrency && allowedLibsForSwiftConcurrency.contains(item) {
788797
shouldInclude = true
789798
}
799+
if backDeploySwiftSpan && allowedLibsForSwiftSpan.contains(item) {
800+
shouldInclude = true
801+
}
790802

791803
return shouldInclude
792804
}

Tests/SWBCoreTests/CommandLineSpecTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ import SWBMacro
681681
let cbc = CommandBuildContext(producer: producer, scope: mockScope, inputs: [FileToBuild(absolutePath: Path.root.join("tmp/input"), fileType: mockFileType)], output: nil)
682682

683683
// Check that task construction sets the correct env bindings.
684-
await stdlibTool.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: nil, filterForSwiftOS: false, backDeploySwiftConcurrency: false)
684+
await stdlibTool.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: nil, filterForSwiftOS: false, backDeploySwiftConcurrency: false, backDeploySwiftSpan: false)
685685
#expect(delegate.shellTasks.count == 1)
686686
let task = try #require(delegate.shellTasks[safe: 0])
687687
#expect(task.environment.bindingsDictionary == ["CODESIGN_ALLOCATE": "/path/to/codesign_allocate"])

0 commit comments

Comments
 (0)