@@ -1222,4 +1222,181 @@ fileprivate struct PreviewsTaskConstructionTests: CoreBasedTests {
12221222 }
12231223 }
12241224
1225+ /// rdar://171933514 — The stub executor link task must be ordered after the debug.dylib link task.
1226+ /// Without this, llbuild can schedule them concurrently, causing "no such file or directory" for
1227+ /// the debug.dylib when the stub executor link starts before the debug.dylib link completes.
1228+ @Test ( . requireSDKs( . iOS) )
1229+ func previewsDylibStubExecutorDependsOnDebugDylib( ) async throws {
1230+ let core = try await getCore ( )
1231+
1232+ let testProject = try await TestProject (
1233+ " ProjectName " ,
1234+ groupTree: TestGroup (
1235+ " Sources " , path: " Sources " ,
1236+ children: [
1237+ TestFile ( " File.swift " ) ,
1238+ TestFile ( " Info.plist " ) ,
1239+ ] ) ,
1240+ targets: [
1241+ TestStandardTarget (
1242+ " AppTarget " ,
1243+ type: . application,
1244+ buildConfigurations: [
1245+ TestBuildConfiguration ( " Debug " , buildSettings: [
1246+ " SDKROOT " : " iphoneos " ,
1247+ " PRODUCT_NAME " : " $(TARGET_NAME) " ,
1248+ " SWIFT_OPTIMIZATION_LEVEL " : " -Onone " ,
1249+ " SWIFT_EXEC " : swiftCompilerPath. str,
1250+ " SWIFT_VERSION " : swiftVersion,
1251+ " ENABLE_PREVIEWS " : " NO " ,
1252+ " ENABLE_XOJIT_PREVIEWS " : " YES " ,
1253+ " ENABLE_DEBUG_DYLIB " : " YES " ,
1254+ " INFOPLIST_FILE " : " Sources/Info.plist " ,
1255+ " CODE_SIGN_IDENTITY " : " - " ,
1256+ ] ) ,
1257+ ] ,
1258+ buildPhases: [
1259+ TestSourcesBuildPhase ( [
1260+ TestBuildFile ( " File.swift " ) ,
1261+ ] ) ,
1262+ ] )
1263+ ] )
1264+ let tester = try TaskConstructionTester ( core, testProject)
1265+
1266+ let fs = PseudoFS ( )
1267+ try fs. writeSimulatedPreviewsJITStubExecutorLibraries ( sdk: core. loadSDK ( . iOSSimulator) )
1268+
1269+ await tester. checkBuild ( runDestination: . iOSSimulator, fs: fs) { results in
1270+ results. checkNoDiagnostics ( )
1271+
1272+ let ldDebugDylib = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " .debug.dylib " ) ) ) { $0 }
1273+ let ldPreviewDylib = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " __preview.dylib " ) ) ) { $0 }
1274+ let ldStubExecutor = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " /AppTarget " ) ) ) { $0 }
1275+
1276+ let signDebugDylib = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " .debug.dylib " ) ) ) { $0 }
1277+ let signPreviewDylib = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " __preview.dylib " ) ) ) { $0 }
1278+ let signApp = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " .app " ) ) ) { $0 }
1279+
1280+ // Stub executor must wait for debug.dylib to be linked
1281+ if let ldStubExecutor, let ldDebugDylib {
1282+ results. checkTaskFollows ( ldStubExecutor, antecedent: ldDebugDylib)
1283+ }
1284+
1285+ // CodeSign must wait for all three linking tasks
1286+ if let ldDebugDylib, let signDebugDylib {
1287+ results. checkTaskFollows ( signDebugDylib, antecedent: ldDebugDylib)
1288+ }
1289+ if let ldPreviewDylib, let signPreviewDylib {
1290+ results. checkTaskFollows ( signPreviewDylib, antecedent: ldPreviewDylib)
1291+ }
1292+ if let ldStubExecutor, let signApp {
1293+ results. checkTaskFollows ( signApp, antecedent: ldStubExecutor)
1294+ }
1295+ if let ldDebugDylib, let signApp {
1296+ results. checkTaskFollows ( signApp, antecedent: ldDebugDylib)
1297+ }
1298+ if let ldPreviewDylib, let signApp {
1299+ results. checkTaskFollows ( signApp, antecedent: ldPreviewDylib)
1300+ }
1301+ }
1302+ }
1303+
1304+ /// rdar://171933514 — Same ordering invariant as above, but for multi-arch builds where
1305+ /// per-arch link tasks go through lipo. The per-arch stub executor link must be ordered
1306+ /// after the lipo'd debug.dylib.
1307+ @Test ( . requireSDKs( . macOS) )
1308+ func previewsDylibStubExecutorDependsOnDebugDylibMultiArch( ) async throws {
1309+ let core = try await getCore ( )
1310+ let archs = [ " arm64 " , " x86_64 " ]
1311+ let archsJoined = archs. joined ( separator: " " )
1312+
1313+ let testProject = try await TestProject (
1314+ " ProjectName " ,
1315+ groupTree: TestGroup (
1316+ " Sources " , path: " Sources " ,
1317+ children: [
1318+ TestFile ( " File.swift " ) ,
1319+ TestFile ( " Info.plist " ) ,
1320+ TestFile ( " Entitlements.plist " ) ,
1321+ ] ) ,
1322+ targets: [
1323+ TestStandardTarget (
1324+ " AppTarget " ,
1325+ type: . application,
1326+ buildConfigurations: [
1327+ TestBuildConfiguration ( " Debug " , buildSettings: [
1328+ " PRODUCT_NAME " : " $(TARGET_NAME) " ,
1329+ " SWIFT_OPTIMIZATION_LEVEL " : " -Onone " ,
1330+ " SWIFT_EXEC " : swiftCompilerPath. str,
1331+ " SWIFT_VERSION " : swiftVersion,
1332+ " ARCHS " : archsJoined,
1333+ " ENABLE_PREVIEWS " : " NO " ,
1334+ " ENABLE_XOJIT_PREVIEWS " : " YES " ,
1335+ " ENABLE_DEBUG_DYLIB " : " YES " ,
1336+ " ENABLE_DEBUG_DYLIB_OVERRIDE " : " YES " ,
1337+ " DEFINES_MODULE " : " NO " ,
1338+ " INFOPLIST_FILE " : " Sources/Info.plist " ,
1339+ " CODE_SIGN_ENTITLEMENTS " : " Sources/Entitlements.plist " ,
1340+ " CODE_SIGN_IDENTITY " : " - " ,
1341+ ] ) ,
1342+ ] ,
1343+ buildPhases: [
1344+ TestSourcesBuildPhase ( [
1345+ TestBuildFile ( " File.swift " ) ,
1346+ ] ) ,
1347+ ] )
1348+ ] )
1349+ let tester = try TaskConstructionTester ( core, testProject)
1350+
1351+ await tester. checkBuild ( runDestination: . anyMac, fs: localFS) { results in
1352+ results. checkNoDiagnostics ( )
1353+
1354+ var ldDebugDylibs : [ any PlannedTask ] = [ ]
1355+ var ldPreviewDylibs : [ any PlannedTask ] = [ ]
1356+ var ldStubExecutors : [ any PlannedTask ] = [ ]
1357+
1358+ for arch in archs {
1359+ let ldDebugDylib = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " .debug.dylib " ) ) , . matchRuleItem( arch) ) { $0 }
1360+ let ldPreviewDylib = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " __preview.dylib " ) ) , . matchRuleItem( arch) ) { $0 }
1361+ let ldStubExecutor = results. checkTask ( . matchRuleType( " Ld " ) , . matchRuleItemPattern( . suffix( " /AppTarget " ) ) , . matchRuleItem( arch) ) { $0 }
1362+
1363+ // Stub executor must wait for lipo'd debug.dylib
1364+ if let ldStubExecutor, let ldDebugDylib {
1365+ results. checkTaskFollows ( ldStubExecutor, antecedent: ldDebugDylib)
1366+ }
1367+
1368+ if let ldDebugDylib { ldDebugDylibs. append ( ldDebugDylib) }
1369+ if let ldPreviewDylib { ldPreviewDylibs. append ( ldPreviewDylib) }
1370+ if let ldStubExecutor { ldStubExecutors. append ( ldStubExecutor) }
1371+ }
1372+
1373+ let signDebugDylib = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " .debug.dylib " ) ) ) { $0 }
1374+ let signPreviewDylib = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " __preview.dylib " ) ) ) { $0 }
1375+ let signApp = results. checkTask ( . matchRuleType( " CodeSign " ) , . matchRuleItemPattern( . suffix( " .app " ) ) ) { $0 }
1376+
1377+ // CodeSign must wait for all linking tasks
1378+ for ldDebugDylib in ldDebugDylibs {
1379+ if let signDebugDylib {
1380+ results. checkTaskFollows ( signDebugDylib, antecedent: ldDebugDylib)
1381+ }
1382+ if let signApp {
1383+ results. checkTaskFollows ( signApp, antecedent: ldDebugDylib)
1384+ }
1385+ }
1386+ for ldPreviewDylib in ldPreviewDylibs {
1387+ if let signPreviewDylib {
1388+ results. checkTaskFollows ( signPreviewDylib, antecedent: ldPreviewDylib)
1389+ }
1390+ if let signApp {
1391+ results. checkTaskFollows ( signApp, antecedent: ldPreviewDylib)
1392+ }
1393+ }
1394+ for ldStubExecutor in ldStubExecutors {
1395+ if let signApp {
1396+ results. checkTaskFollows ( signApp, antecedent: ldStubExecutor)
1397+ }
1398+ }
1399+ }
1400+ }
1401+
12251402}
0 commit comments