@@ -3562,27 +3562,53 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
35623562 }
35633563 }
35643564
3565+
35653566 @Test ( . requireSDKs( . macOS) )
35663567 func copySwiftLibs_preSwiftOS_macos( ) async throws {
3567- try await _testCopySwiftLibs ( deploymentTarget: " 10.14.3 " , shouldFilterSwiftLibs: false , shouldBackDeploySwiftConcurrency: false )
3568+ // Swift does not exist in the OS, so shouldFilterSwiftLibs is false. macOS 10.14.3 does not
3569+ // support use of back deployed span and concurrency, but if code using either has
3570+ // availability guards with a version supporting back deployment, the compatibility
3571+ // libraries will be weakly linked and should be copied.
3572+ try await _testCopySwiftLibs ( deploymentTarget: " 10.14.3 " , shouldFilterSwiftLibs: false , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
35683573 }
35693574
35703575 @Test ( . requireSDKs( . macOS) )
35713576 func copySwiftLibs_postSwiftOS_macos( ) async throws {
3572- try await _testCopySwiftLibs ( deploymentTarget: " 10.14.4 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true )
3577+ // macOS 10.14.4 is the first version with Swift in the OS, so shouldFilterSwiftLibs should
3578+ // be true on this and later versions. Both Concurrency and Span back deploy starting
3579+ // at this version.
3580+ try await _testCopySwiftLibs ( deploymentTarget: " 10.14.4 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
35733581 }
35743582
35753583 @Test ( . requireSDKs( . macOS) )
3576- func copySwiftLibs_postSwiftConcurrency_macos( ) async throws {
3577- try await _testCopySwiftLibs ( deploymentTarget: " 12.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false )
3584+ func copySwiftLibs_postSwiftOS_preSwiftConcurrency_macos( ) async throws {
3585+ // macOS 11.5 includes Swift in the OS, but predates the OS copy of Concurrency.
3586+ // Both Concurrency and Span should back deploy.
3587+ try await _testCopySwiftLibs ( deploymentTarget: " 11.5 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
35783588 }
35793589
35803590 @Test ( . requireSDKs( . macOS) )
3581- func copySwiftLibs_postSwiftOS_preSwiftConcurrency_macos( ) async throws {
3582- try await _testCopySwiftLibs ( deploymentTarget: " 11.5 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true )
3591+ func copySwiftLibs_postSwiftConcurrency_macos( ) async throws {
3592+ // macOS 12.0 includes Swift and Concurrency in the OS but not Span.
3593+ // Only Span should back deploy.
3594+ try await _testCopySwiftLibs ( deploymentTarget: " 12.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false , shouldBackDeploySwiftSpan: true )
3595+ }
3596+
3597+ @Test ( . requireSDKs( . macOS) , . requireXcode26( ) )
3598+ func copySwiftLibs_postSwiftSpan_macos( ) async throws {
3599+ // macOS 26.0 includes Swift, Concurrency, and Span in the OS.
3600+ try await _testCopySwiftLibs ( deploymentTarget: " 26.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false , shouldBackDeploySwiftSpan: false )
35833601 }
35843602
3585- func _testCopySwiftLibs( deploymentTarget: String , shouldFilterSwiftLibs: Bool , shouldBackDeploySwiftConcurrency: Bool , file: StaticString = #filePath, line: Int = #line) async throws {
3603+ func _testCopySwiftLibs( deploymentTarget: String , shouldFilterSwiftLibs: Bool , shouldBackDeploySwiftConcurrency: Bool , shouldBackDeploySwiftSpan: Bool , file: StaticString = #filePath, line: Int = #line) async throws {
3604+ let core = try await getCore ( )
3605+ let defaultDeploymentTarget = core. loadSDK ( . macOS) . defaultDeploymentTarget
3606+ let testsDeploymentTarget : String
3607+ if try Version ( deploymentTarget) < Version ( defaultDeploymentTarget) {
3608+ testsDeploymentTarget = defaultDeploymentTarget
3609+ } else {
3610+ testsDeploymentTarget = deploymentTarget
3611+ }
35863612 // Create a temporary test workspace, consisting of:
35873613 // - an application
35883614 // - a framework embedded directly inside the application
@@ -3591,7 +3617,6 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
35913617 // All use Swift, and the point is to test that the right libswift libs
35923618 // get copied as new import statements are added to the sources that are
35933619 // only indirectly included inside the app.
3594- let core = try await getCore ( )
35953620 try await withTemporaryDirectory { tmpDirPath async throws -> Void in
35963621 let testWorkspace = try await TestWorkspace (
35973622 " Test " ,
@@ -3757,7 +3782,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
37573782 " Debug " ,
37583783 buildSettings: [
37593784 // Override the deployment target for tests so we don't get a warning that our deployment target is higher than XCTest's.
3760- " MACOSX_DEPLOYMENT_TARGET " : core . loadSDK ( . macOS ) . defaultDeploymentTarget
3785+ " MACOSX_DEPLOYMENT_TARGET " : testsDeploymentTarget
37613786 ]
37623787 )
37633788 ] ,
@@ -3784,6 +3809,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
37843809 public func foo() -> NSString { return " Foo " }
37853810 @available(macOS 10.15, *)
37863811 public actor A { }
3812+ @available(macOS 10.14.4, *)
3813+ public func bar() { print(Span<Int>.self) }
37873814 """
37883815 }
37893816 try await tester. fs. writeFileContents ( testWorkspace. sourceRoot. join ( " TestProject/SysExMain.swift " ) ) { contents in
@@ -3793,6 +3820,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
37933820 @available(macOS 10.15, *)
37943821 public actor A { }
37953822 @main struct Main { public static func main() { } }
3823+ @available(macOS 10.14.4, *)
3824+ public func bar() { print(Span<Int>.self) }
37963825 """
37973826 }
37983827 try await tester. fs. writeFileContents ( testWorkspace. sourceRoot. join ( " TestProject/SubFrameworkSource.swift " ) ) { contents in
@@ -3806,6 +3835,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
38063835 @available(macOS 10.15, *)
38073836 public actor A { }
38083837 }
3838+ @available(macOS 10.14.4, *)
3839+ public func bar() { print(Span<Int>.self) }
38093840 """
38103841 }
38113842
@@ -3883,26 +3914,25 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
38833914 #expect( dependencyInfo. version == expectedDependencyInfo. version)
38843915 XCTAssertSuperset ( Set ( dependencyInfo. inputs) , Set ( expectedDependencyInfo. inputs) )
38853916
3886- // Ensure that the dependency info is correct. Due to the changing nature of how Swift overlays are added, there is no need to be explicit about each one, so only the mechanism is validated by checking a handful of stable Swift overlays .
3887- if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency {
3917+ // Check the baseline dependency info when nothing is backdeployed .
3918+ if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency && !shouldBackDeploySwiftSpan {
38883919 #expect( dependencyInfo == expectedDependencyInfo)
38893920 }
3921+ #expect( dependencyInfo. outputs. sorted ( ) . contains ( expectedDependencyInfo. outputs. sorted ( ) ) )
38903922
3891- if !shouldFilterSwiftLibs {
3892- #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) )
3893- #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) )
3894- #expect( dependencyInfo. outputs. sorted ( ) . contains ( expectedDependencyInfo. outputs. sorted ( ) ) )
3895- #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftCore.dylib " ) . str) )
3896- #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftFoundation.dylib " ) . str) )
3897- }
3923+ // If we should not filter Swift libs, check they exist in the dependency info.
3924+ #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3925+ #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
3926+ #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3927+ #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
38983928
3899- if shouldBackDeploySwiftConcurrency {
3900- // Note all toolchains have this yet...
3901- if tester . fs . exists ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/ libswift_Concurrency.dylib" ) ) {
3902- #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) )
3903- #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str ) )
3904- }
3905- }
3929+ // If we should back deploy Concurrency, ensure it's in the dependency info.
3930+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) == shouldBackDeploySwiftConcurrency )
3931+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/ libswift_Concurrency.dylib" ) . str ) == shouldBackDeploySwiftConcurrency )
3932+
3933+ // If we should back deploy span, ensure it's in the dependency info.
3934+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-6.2/macosx/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
3935+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
39063936 }
39073937
39083938 do {
@@ -3928,47 +3958,42 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
39283958 #expect( unitTestsDependencyInfo. version == unitTestsExpectedDependencyInfo. version)
39293959 XCTAssertSuperset ( Set ( unitTestsDependencyInfo. inputs) , Set ( unitTestsExpectedDependencyInfo. inputs) )
39303960
3931- // Ensure that the dependency info is correct. Due to the changing nature of how Swift overlays are added, there is no need to be explicit about each one, so only the mechanism is validated by checking a handful of stable Swift overlays .
3932- if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency {
3961+ // Ensure that the baseline dependency info is correct when nothing is backdeployed .
3962+ if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency && !shouldBackDeploySwiftSpan {
39333963 #expect( unitTestsDependencyInfo == unitTestsExpectedDependencyInfo)
39343964 }
39353965
3936- if !shouldFilterSwiftLibs {
3937- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3938- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) )
3939- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) )
3940- }
3966+ // As long as we weren't forced to raise the deployment target for tests, check the dependency info.
3967+ if testsDeploymentTarget == deploymentTarget {
39413968 #expect( unitTestsDependencyInfo. outputs. sorted ( ) . contains ( unitTestsExpectedDependencyInfo. outputs. sorted ( ) ) )
3942- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3943- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftCore.dylib " ) . str) )
3944- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftFoundation.dylib " ) . str) )
3945- }
3946- }
39473969
3948- if shouldBackDeploySwiftConcurrency {
3970+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3971+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
3972+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3973+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
3974+
3975+
3976+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftConcurrency)
3977+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftConcurrency)
3978+
39493979 // NOTE: Tests have their deployment target overridden.
3950- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3951- // Note all toolchains have this yet...
3952- if tester. fs. exists ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) ) {
3953- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) )
3954- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) )
3955- }
3956- }
3980+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftSpan)
3981+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftSpan)
39573982 }
39583983 }
39593984
39603985 do {
3961- // Checks that a Swift-free app which embeds a system extension using Swift Concurrency, embeds the back-deployment dylib .
3986+ // Checks that a Swift-free app which embeds a system extension using Swift Concurrency and Span , embeds the back-deployment dylibs .
39623987 let swiftDepsPath = tmpDirPath. join ( Path ( " Test/TestProject/build/TestProject.build/Debug/SwiftlessSysExApp.build/SwiftStdLibToolInputDependencies.dep " ) )
39633988 let dependencyInfo = try DependencyInfo ( bytes: tester. fs. read ( swiftDepsPath) . bytes)
39643989
3965- if shouldBackDeploySwiftConcurrency {
3966- // Note all toolchains have this yet...
3967- if tester . fs . exists ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/ libswift_Concurrency.dylib" ) ) {
3968- #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) )
3969- #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str ) )
3970- }
3971- }
3990+ // If we should back deploy Concurrency, ensure it's in the dependency info.
3991+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) == shouldBackDeploySwiftConcurrency )
3992+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/ libswift_Concurrency.dylib" ) . str ) == shouldBackDeploySwiftConcurrency )
3993+
3994+ // If we should back deploy Span, ensure it's in the dependency info.
3995+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-6.2/macosx/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
3996+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
39723997 }
39733998 }
39743999
0 commit comments