@@ -3596,27 +3596,53 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
35963596 }
35973597 }
35983598
3599+
35993600 @Test ( . requireSDKs( . macOS) )
36003601 func copySwiftLibs_preSwiftOS_macos( ) async throws {
3601- try await _testCopySwiftLibs ( deploymentTarget: " 10.14.3 " , shouldFilterSwiftLibs: false , shouldBackDeploySwiftConcurrency: false )
3602+ // Swift does not exist in the OS, so shouldFilterSwiftLibs is false. macOS 10.14.3 does not
3603+ // support use of back deployed span and concurrency, but if code using either has
3604+ // availability guards with a version supporting back deployment, the compatibility
3605+ // libraries will be weakly linked and should be copied.
3606+ try await _testCopySwiftLibs ( deploymentTarget: " 10.14.3 " , shouldFilterSwiftLibs: false , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
36023607 }
36033608
36043609 @Test ( . requireSDKs( . macOS) )
36053610 func copySwiftLibs_postSwiftOS_macos( ) async throws {
3606- try await _testCopySwiftLibs ( deploymentTarget: " 10.14.4 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true )
3611+ // macOS 10.14.4 is the first version with Swift in the OS, so shouldFilterSwiftLibs should
3612+ // be true on this and later versions. Both Concurrency and Span back deploy starting
3613+ // at this version.
3614+ try await _testCopySwiftLibs ( deploymentTarget: " 10.14.4 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
36073615 }
36083616
36093617 @Test ( . requireSDKs( . macOS) )
3610- func copySwiftLibs_postSwiftConcurrency_macos( ) async throws {
3611- try await _testCopySwiftLibs ( deploymentTarget: " 12.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false )
3618+ func copySwiftLibs_postSwiftOS_preSwiftConcurrency_macos( ) async throws {
3619+ // macOS 11.5 includes Swift in the OS, but predates the OS copy of Concurrency.
3620+ // Both Concurrency and Span should back deploy.
3621+ try await _testCopySwiftLibs ( deploymentTarget: " 11.5 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true , shouldBackDeploySwiftSpan: true )
36123622 }
36133623
36143624 @Test ( . requireSDKs( . macOS) )
3615- func copySwiftLibs_postSwiftOS_preSwiftConcurrency_macos( ) async throws {
3616- try await _testCopySwiftLibs ( deploymentTarget: " 11.5 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: true )
3625+ func copySwiftLibs_postSwiftConcurrency_macos( ) async throws {
3626+ // macOS 12.0 includes Swift and Concurrency in the OS but not Span.
3627+ // Only Span should back deploy.
3628+ try await _testCopySwiftLibs ( deploymentTarget: " 12.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false , shouldBackDeploySwiftSpan: true )
3629+ }
3630+
3631+ @Test ( . requireSDKs( . macOS) , . requireXcode26( ) )
3632+ func copySwiftLibs_postSwiftSpan_macos( ) async throws {
3633+ // macOS 26.0 includes Swift, Concurrency, and Span in the OS.
3634+ try await _testCopySwiftLibs ( deploymentTarget: " 26.0 " , shouldFilterSwiftLibs: true , shouldBackDeploySwiftConcurrency: false , shouldBackDeploySwiftSpan: false )
36173635 }
36183636
3619- func _testCopySwiftLibs( deploymentTarget: String , shouldFilterSwiftLibs: Bool , shouldBackDeploySwiftConcurrency: Bool , file: StaticString = #filePath, line: Int = #line) async throws {
3637+ func _testCopySwiftLibs( deploymentTarget: String , shouldFilterSwiftLibs: Bool , shouldBackDeploySwiftConcurrency: Bool , shouldBackDeploySwiftSpan: Bool , file: StaticString = #filePath, line: Int = #line) async throws {
3638+ let core = try await getCore ( )
3639+ let defaultDeploymentTarget = core. loadSDK ( . macOS) . defaultDeploymentTarget
3640+ let testsDeploymentTarget : String
3641+ if try Version ( deploymentTarget) < Version ( defaultDeploymentTarget) {
3642+ testsDeploymentTarget = defaultDeploymentTarget
3643+ } else {
3644+ testsDeploymentTarget = deploymentTarget
3645+ }
36203646 // Create a temporary test workspace, consisting of:
36213647 // - an application
36223648 // - a framework embedded directly inside the application
@@ -3625,7 +3651,6 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
36253651 // All use Swift, and the point is to test that the right libswift libs
36263652 // get copied as new import statements are added to the sources that are
36273653 // only indirectly included inside the app.
3628- let core = try await getCore ( )
36293654 try await withTemporaryDirectory { tmpDirPath async throws -> Void in
36303655 let testWorkspace = try await TestWorkspace (
36313656 " Test " ,
@@ -3791,7 +3816,7 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
37913816 " Debug " ,
37923817 buildSettings: [
37933818 // Override the deployment target for tests so we don't get a warning that our deployment target is higher than XCTest's.
3794- " MACOSX_DEPLOYMENT_TARGET " : core . loadSDK ( . macOS ) . defaultDeploymentTarget
3819+ " MACOSX_DEPLOYMENT_TARGET " : testsDeploymentTarget
37953820 ]
37963821 )
37973822 ] ,
@@ -3818,6 +3843,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
38183843 public func foo() -> NSString { return " Foo " }
38193844 @available(macOS 10.15, *)
38203845 public actor A { }
3846+ @available(macOS 10.14.4, *)
3847+ public func bar() { print(Span<Int>.self) }
38213848 """
38223849 }
38233850 try await tester. fs. writeFileContents ( testWorkspace. sourceRoot. join ( " TestProject/SysExMain.swift " ) ) { contents in
@@ -3827,6 +3854,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
38273854 @available(macOS 10.15, *)
38283855 public actor A { }
38293856 @main struct Main { public static func main() { } }
3857+ @available(macOS 10.14.4, *)
3858+ public func bar() { print(Span<Int>.self) }
38303859 """
38313860 }
38323861 try await tester. fs. writeFileContents ( testWorkspace. sourceRoot. join ( " TestProject/SubFrameworkSource.swift " ) ) { contents in
@@ -3840,6 +3869,8 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
38403869 @available(macOS 10.15, *)
38413870 public actor A { }
38423871 }
3872+ @available(macOS 10.14.4, *)
3873+ public func bar() { print(Span<Int>.self) }
38433874 """
38443875 }
38453876
@@ -3917,26 +3948,25 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
39173948 #expect( dependencyInfo. version == expectedDependencyInfo. version)
39183949 XCTAssertSuperset ( Set ( dependencyInfo. inputs) , Set ( expectedDependencyInfo. inputs) )
39193950
3920- // 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 .
3921- if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency {
3951+ // Check the baseline dependency info when nothing is backdeployed .
3952+ if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency && !shouldBackDeploySwiftSpan {
39223953 #expect( dependencyInfo == expectedDependencyInfo)
39233954 }
3955+ #expect( dependencyInfo. outputs. sorted ( ) . contains ( expectedDependencyInfo. outputs. sorted ( ) ) )
39243956
3925- if !shouldFilterSwiftLibs {
3926- #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) )
3927- #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) )
3928- #expect( dependencyInfo. outputs. sorted ( ) . contains ( expectedDependencyInfo. outputs. sorted ( ) ) )
3929- #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftCore.dylib " ) . str) )
3930- #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftFoundation.dylib " ) . str) )
3931- }
3957+ // If we should not filter Swift libs, check they exist in the dependency info.
3958+ #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3959+ #expect( dependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
3960+ #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
3961+ #expect( dependencyInfo. outputs. contains ( buildDir. join ( " App.app/Contents/Frameworks/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
39323962
3933- if shouldBackDeploySwiftConcurrency {
3934- // Note all toolchains have this yet...
3935- if tester . fs . exists ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/ libswift_Concurrency.dylib" ) ) {
3936- #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) )
3937- #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str ) )
3938- }
3939- }
3963+ // If we should back deploy Concurrency, ensure it's in the dependency info.
3964+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) == shouldBackDeploySwiftConcurrency )
3965+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/ libswift_Concurrency.dylib" ) . str ) == shouldBackDeploySwiftConcurrency )
3966+
3967+ // If we should back deploy span, ensure it's in the dependency info.
3968+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-6.2/macosx/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
3969+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " App.app/Contents/Frameworks/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
39403970 }
39413971
39423972 do {
@@ -3962,47 +3992,42 @@ That command depends on command in Target 'agg2' (project \'aProject\'): script
39623992 #expect( unitTestsDependencyInfo. version == unitTestsExpectedDependencyInfo. version)
39633993 XCTAssertSuperset ( Set ( unitTestsDependencyInfo. inputs) , Set ( unitTestsExpectedDependencyInfo. inputs) )
39643994
3965- // 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 .
3966- if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency {
3995+ // Ensure that the baseline dependency info is correct when nothing is backdeployed .
3996+ if shouldFilterSwiftLibs && !shouldBackDeploySwiftConcurrency && !shouldBackDeploySwiftSpan {
39673997 #expect( unitTestsDependencyInfo == unitTestsExpectedDependencyInfo)
39683998 }
39693999
3970- if !shouldFilterSwiftLibs {
3971- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3972- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) )
3973- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) )
3974- }
4000+ // As long as we weren't forced to raise the deployment target for tests, check the dependency info.
4001+ if testsDeploymentTarget == deploymentTarget {
39754002 #expect( unitTestsDependencyInfo. outputs. sorted ( ) . contains ( unitTestsExpectedDependencyInfo. outputs. sorted ( ) ) )
3976- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3977- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftCore.dylib " ) . str) )
3978- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftFoundation.dylib " ) . str) )
3979- }
3980- }
39814003
3982- if shouldBackDeploySwiftConcurrency {
4004+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
4005+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
4006+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftCore.dylib " ) . str) == !shouldFilterSwiftLibs)
4007+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " UnitTests.xctest/Contents/Frameworks/libswiftFoundation.dylib " ) . str) == !shouldFilterSwiftLibs)
4008+
4009+
4010+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftConcurrency)
4011+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftConcurrency)
4012+
39834013 // NOTE: Tests have their deployment target overridden.
3984- if ( try Version ( core. loadSDK ( . macOS) . defaultDeploymentTarget) ) < Version ( 10 , 15 ) {
3985- // Note all toolchains have this yet...
3986- if tester. fs. exists ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) ) {
3987- #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) )
3988- #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) )
3989- }
3990- }
4014+ #expect( unitTestsDependencyInfo. inputs. contains ( core. developerPath. path. join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftSpan)
4015+ #expect( unitTestsDependencyInfo. outputs. contains ( buildDir. join ( " SwiftlessApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str) == shouldBackDeploySwiftSpan)
39914016 }
39924017 }
39934018
39944019 do {
3995- // Checks that a Swift-free app which embeds a system extension using Swift Concurrency, embeds the back-deployment dylib .
4020+ // Checks that a Swift-free app which embeds a system extension using Swift Concurrency and Span , embeds the back-deployment dylibs .
39964021 let swiftDepsPath = tmpDirPath. join ( Path ( " Test/TestProject/build/TestProject.build/Debug/SwiftlessSysExApp.build/SwiftStdLibToolInputDependencies.dep " ) )
39974022 let dependencyInfo = try DependencyInfo ( bytes: tester. fs. read ( swiftDepsPath) . bytes)
39984023
3999- if shouldBackDeploySwiftConcurrency {
4000- // Note all toolchains have this yet...
4001- if tester . fs . exists ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/ libswift_Concurrency.dylib" ) ) {
4002- #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) )
4003- #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/libswift_Concurrency.dylib " ) . str ) )
4004- }
4005- }
4024+ // If we should back deploy Concurrency, ensure it's in the dependency info.
4025+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx/libswift_Concurrency.dylib " ) . str ) == shouldBackDeploySwiftConcurrency )
4026+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/ libswift_Concurrency.dylib" ) . str ) == shouldBackDeploySwiftConcurrency )
4027+
4028+ // If we should back deploy Span, ensure it's in the dependency info.
4029+ #expect ( dependencyInfo . inputs . contains ( core . developerPath . path . join ( " Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-6.2/macosx/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
4030+ #expect ( dependencyInfo . outputs . contains ( buildDir . join ( " SwiftlessSysExApp.app/Contents/Frameworks/libswiftCompatibilitySpan.dylib " ) . str ) == shouldBackDeploySwiftSpan )
40064031 }
40074032 }
40084033
0 commit comments