Skip to content

Add support for the libswiftCompatibilitySpan.dylib backward deployment library #359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions Sources/SWBCore/PlatformRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ public final class Platform: Sendable {
/// Minimum OS version for Swift-in-the-OS support. If this is `nil`, the platform does not support Swift-in-the-OS at all.
fileprivate(set) var minimumOSForSwiftInTheOS: Version? = nil

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

/// 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the platform does not support Swift concurrency at all."

Think this needs to be updated to reflect Span, rather than Swift Concurrency

fileprivate(set) var minimumOSForSwiftSpan: Version? = nil

/// The canonical name of the public SDK for this platform.
/// - remark: This does not mean that this SDK exists, just that this is its canonical name if it does exist.
@_spi(Testing) public let sdkCanonicalName: String?
Expand Down Expand Up @@ -244,6 +247,11 @@ extension Platform {
return self.minimumOSForSwiftConcurrency ?? self.correspondingDevicePlatform?.minimumOSForSwiftConcurrency ?? nil
}

/// Determines the deployment version to use for Swift Span support.
fileprivate func swiftOSSpanVersion(_ deploymentTarget: StringMacroDeclaration) -> Version? {
return self.minimumOSForSwiftSpan ?? self.correspondingDevicePlatform?.minimumOSForSwiftSpan ?? nil
}

/// Determines if the platform supports Swift in the OS.
public func supportsSwiftInTheOS(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool {
guard let deploymentTarget = self.deploymentTargetMacro else { return false }
Expand All @@ -265,7 +273,7 @@ extension Platform {
return version >= minimumSwiftInTheOSVersion
}

/// 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.
/// 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.
public func supportsSwiftConcurrencyNatively(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool? {
guard let deploymentTarget = self.deploymentTargetMacro else { return false }

Expand All @@ -287,6 +295,29 @@ extension Platform {

return version >= minimumSwiftConcurrencyVersion
}

/// 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.
public func supportsSwiftSpanNatively(_ scope: MacroEvaluationScope, forceNextMajorVersion: Bool = false, considerTargetDeviceOSVersion: Bool = true) -> Bool? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add default values for the arguments? This is only ever called in one place.

guard let deploymentTarget = self.deploymentTargetMacro else { return false }

// If we have target device info and its platform matches the build platform, compare the device OS version
let targetDeviceVersion: Version?
if considerTargetDeviceOSVersion && scope.evaluate(BuiltinMacros.TARGET_DEVICE_PLATFORM_NAME) == self.name {
targetDeviceVersion = try? Version(scope.evaluate(BuiltinMacros.TARGET_DEVICE_OS_VERSION))
} else {
targetDeviceVersion = nil
}

// Otherwise fall back to comparing the minimum deployment target
let deploymentTargetVersion = try? Version(scope.evaluate(deploymentTarget))

guard let version = targetDeviceVersion ?? deploymentTargetVersion else { return false }

// Return `nil` here as there is no metadata for the platform to allow downstream clients to be aware of this.
guard let minimumSwiftSpanVersion = swiftOSSpanVersion(deploymentTarget) else { return nil }

return version >= minimumSwiftSpanVersion
}
}

extension Platform: CustomStringConvertible {
Expand Down Expand Up @@ -676,6 +707,7 @@ public final class PlatformRegistry {
if let variant = platform.defaultSDKVariant {
platform.minimumOSForSwiftInTheOS = variant.minimumOSForSwiftInTheOS
platform.minimumOSForSwiftConcurrency = variant.minimumOSForSwiftConcurrency
platform.minimumOSForSwiftSpan = variant.minimumOSForSwiftSpan
}
}

Expand Down
14 changes: 9 additions & 5 deletions Sources/SWBCore/SDKRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,12 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {
/// Minimum OS version for Swift-in-the-OS support. If this is `nil`, the platform does not support Swift-in-the-OS at all.
public let minimumOSForSwiftInTheOS: Version?

/// Minimum OS version for built-in Swift concurrency support. If this is `nil`, the platform does not support Swift concurrency at all.
/// 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.
public let minimumOSForSwiftConcurrency: Version?

/// 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.
public let minimumOSForSwiftSpan: Version?

/// The path prefix under which all built content produced by this SDK variant should be installed, relative to the system root.
///
/// Empty string if content should be installed directly into the system root (default).
Expand Down Expand Up @@ -392,9 +395,10 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {

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

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

self.systemPrefix = supportedTargetDict["SystemPrefix"]?.stringValue ?? {
switch name {
Expand Down Expand Up @@ -445,12 +449,12 @@ public final class SDKVariant: PlatformInfoProvider, Sendable {
}
}

private static func fallbackSwiftVersions(variantName name: String) -> (String?, String?) {
private static func fallbackSwiftVersions(variantName name: String) -> (os: String?, concurrency: String?, span: String?) {
switch name {
case "macos", "macosx":
return ("10.14.4", "12.0")
return ("10.14.4", "12.0", nil)
default:
return (nil, nil)
return (nil, nil, nil)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public final class SwiftStdLibToolSpec : GenericCommandLineToolSpec, SpecIdentif
}

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

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

if backDeploySwiftSpan {
commandLine.append("--back-deploy-swift-span")
}

let outputs = [delegate.createVirtualNode("CopySwiftStdlib \(wrapperPathString.str)")]

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,13 @@ final class SwiftStandardLibrariesTaskProducer: PhasedTaskProducer, TaskProducer
let supportsConcurrencyNatively = context.platform?.supportsSwiftConcurrencyNatively(scope)
let backDeploySwiftConcurrency = supportsConcurrencyNatively != nil && supportsConcurrencyNatively != true

let supportsSpanNatively = context.platform?.supportsSwiftSpanNatively(scope)
let backDeploySwiftSpan = supportsSpanNatively != nil && supportsSpanNatively != true

let cbc = CommandBuildContext(producer: context, scope: scope, inputs: [ input ])
let foldersToScanExpr: MacroStringListExpression? = foldersToScan.count > 0 ? scope.namespace.parseLiteralStringList(foldersToScan): nil
await appendGeneratedTasks(&tasks) { delegate in
await context.swiftStdlibToolSpec.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: foldersToScanExpr, filterForSwiftOS: filterForSwiftOS, backDeploySwiftConcurrency: backDeploySwiftConcurrency)
await context.swiftStdlibToolSpec.constructSwiftStdLibraryToolTask(cbc, delegate, foldersToScan: foldersToScanExpr, filterForSwiftOS: filterForSwiftOS, backDeploySwiftConcurrency: backDeploySwiftConcurrency, backDeploySwiftSpan: backDeploySwiftSpan)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,18 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
// If true, then the Swift concurrency dylibs should be copied into the app/framework's bundles.
var backDeploySwiftConcurrency = false

// If true, then the Swift Span dylibs should be copied into the app/framework's bundles.
var backDeploySwiftSpan = false

// The allowed list of libraries that should *not* be filtered when `filterForSwiftOS=true`.
let allowedLibsForSwiftOS = ["libswiftXCTest" ]

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

// The allowed list of libraries that should *not* be filtered when `backDeploySwiftSpan=true`.
let allowedLibsForSwiftSpan = ["libswiftCompatibilitySpan"]

func absolutePath(_ path: Path) -> Path {
return path.isAbsolute ? path : task.workingDirectory.join(path)
}
Expand Down Expand Up @@ -368,6 +374,9 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
case "--back-deploy-swift-concurrency":
self.backDeploySwiftConcurrency = true

case "--back-deploy-swift-span":
self.backDeploySwiftSpan = true

default:
throw StubError.error("unrecognized argument: \(arg)")
}
Expand Down Expand Up @@ -787,6 +796,9 @@ public final class EmbedSwiftStdLibTaskAction: TaskAction {
if backDeploySwiftConcurrency && allowedLibsForSwiftConcurrency.contains(item) {
shouldInclude = true
}
if backDeploySwiftSpan && allowedLibsForSwiftSpan.contains(item) {
shouldInclude = true
}

return shouldInclude
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/SWBCoreTests/CommandLineSpecTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ import SWBMacro
let cbc = CommandBuildContext(producer: producer, scope: mockScope, inputs: [FileToBuild(absolutePath: Path.root.join("tmp/input"), fileType: mockFileType)], output: nil)

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