Skip to content

Commit f38a04f

Browse files
committed
Implement __SKIP_BUILD setting to prune test runners from builds for apple platforms
1 parent 054f230 commit f38a04f

File tree

3 files changed

+120
-30
lines changed

3 files changed

+120
-30
lines changed

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,8 @@ public final class BuiltinMacros {
13001300
// Info.plist Keys - Sticker Packs
13011301
public static let INFOPLIST_KEY_NSStickerSharingLevel = BuiltinMacros.declareEnumMacro("INFOPLIST_KEY_NSStickerSharingLevel") as EnumMacroDeclaration<StickerSharingLevel>
13021302

1303+
public static let __SKIP_BUILD = BuiltinMacros.declareBooleanMacro("__SKIP_BUILD")
1304+
13031305
// MARK: Built-in Macro Initialization
13041306

13051307
private static var initialized = false
@@ -2391,7 +2393,8 @@ public final class BuiltinMacros {
23912393
ENABLE_XOJIT_PREVIEWS,
23922394
BUILD_ACTIVE_RESOURCES_ONLY,
23932395
ENABLE_ONLY_ACTIVE_RESOURCES,
2394-
ENABLE_PLAYGROUND_RESULTS
2396+
ENABLE_PLAYGROUND_RESULTS,
2397+
__SKIP_BUILD
23952398
]
23962399

23972400
/// Force initialization of entitlements macros.

Sources/SWBCore/TargetDependencyResolver.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -553,18 +553,24 @@ fileprivate extension TargetDependencyResolver {
553553
delegate.emit(.init(behavior: .error, location: .unknown, data: .init("The 'Skip Dependencies' option is deprecated and can no longer be used.")))
554554
}
555555

556-
// Apply the requested dependency scope to the graph.
556+
// Prune targets from the graph as needed.
557+
var keptTargets: OrderedSet<ConfiguredTarget>
558+
var removedTargets: OrderedSet<ConfiguredTarget>
559+
560+
// First, determine targets to be removed based on the dependency scope.
557561
switch buildRequest.dependencyScope {
558562
case .workspace:
559563
// If dependencies are scoped to the workspace, no pruning is required.
564+
keptTargets = allTargets
565+
removedTargets = []
560566
break
561567
case .buildRequest:
562568
if buildRequest.skipDependencies {
563569
delegate.emit(.init(behavior: .error, location: .unknown, data: .init("The 'Skip Dependencies' option is deprecated and cannot be combined with dependency scopes.")))
564570
}
565571
// First, partition the targets into those we're keeping, and those we're removing.
566-
var keptTargets: OrderedSet<ConfiguredTarget> = []
567-
var removedTargets: OrderedSet<ConfiguredTarget> = []
572+
keptTargets = []
573+
removedTargets = []
568574
let requestedTargetGUIDs = Set(buildRequest.buildTargets.map(\.target.guid))
569575
var extraRequestedTargetGUIDs: Set<String> = []
570576
var potentialExtraRequestedPackageTargetGUIDs: Set<String> = []
@@ -594,8 +600,18 @@ fileprivate extension TargetDependencyResolver {
594600
removedTargets.append(configuredTarget)
595601
}
596602
}
603+
}
604+
605+
// Then, remove targets based on the value of the __SKIP_BUILD setting.
606+
let targetsToRemoveBasedOnSettings = keptTargets.filter { configuredTarget in
607+
let settings = buildRequestContext.getCachedSettings(configuredTarget.parameters, target: configuredTarget.target)
608+
return settings.globalScope.evaluate(BuiltinMacros.__SKIP_BUILD)
609+
}
610+
keptTargets.subtract(targetsToRemoveBasedOnSettings)
611+
removedTargets.append(contentsOf: targetsToRemoveBasedOnSettings)
597612

598-
// For each removed target, identify all kept targets reachable by traversing only edges which originate at a removed target.
613+
// For each removed target, identify all kept targets reachable by traversing only edges which originate at a removed target.
614+
if !removedTargets.isEmpty {
599615
var reachableKeptTargetsByRemovedTarget: [ConfiguredTarget: OrderedSet<ConfiguredTarget>] = [:]
600616
do {
601617
func visit(_ configuredTarget: ConfiguredTarget) {

Tests/SWBCoreTests/DependencyScopingTests.swift

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import SWBTestSupport
1818
import Foundation
1919

2020
@Suite fileprivate struct DependencyScopingTests: CoreBasedTests {
21-
@Test(.requireSDKs(.macOS))
21+
@Test(.requireSDKs(.host))
2222
func buildRequestScopeBasics() async throws {
2323
let core = try await getCore()
2424

@@ -77,8 +77,8 @@ import Foundation
7777

7878
// Configure the targets and create a BuildRequest.
7979
let buildParameters = BuildParameters(configuration: "Debug")
80-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
81-
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T2")!)
80+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
81+
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T2")))
8282
do {
8383
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
8484
let buildRequestContext = BuildRequestContext(workspaceContext: workspaceContext)
@@ -100,7 +100,78 @@ import Foundation
100100
}
101101
}
102102

103-
@Test(.requireSDKs(.macOS))
103+
@Test(.requireSDKs(.host))
104+
func skipBuildSetting() async throws {
105+
let core = try await getCore()
106+
107+
let workspace = try TestWorkspace(
108+
"Workspace",
109+
projects: [
110+
TestProject(
111+
"P1",
112+
groupTree: TestGroup(
113+
"G1",
114+
children: [
115+
TestFile("S1.c"),
116+
]
117+
),
118+
buildConfigurations: [
119+
TestBuildConfiguration("Debug", buildSettings: [:]),
120+
],
121+
targets: [
122+
TestStandardTarget(
123+
"T1",
124+
type: .framework,
125+
buildConfigurations: [
126+
TestBuildConfiguration("Debug", buildSettings: ["PRODUCT_NAME": "$(TARGET_NAME)", "__SKIP_BUILD": "YES"]),
127+
],
128+
buildPhases: [
129+
TestSourcesBuildPhase(["S1.c"])
130+
],
131+
dependencies: ["T2"]
132+
),
133+
TestStandardTarget(
134+
"T2",
135+
type: .framework,
136+
buildConfigurations: [
137+
TestBuildConfiguration("Debug", buildSettings: ["PRODUCT_NAME": "$(TARGET_NAME)"]),
138+
],
139+
buildPhases: [
140+
TestSourcesBuildPhase(["S1.c"])
141+
],
142+
dependencies: ["T3"]
143+
),
144+
TestStandardTarget(
145+
"T3",
146+
type: .framework,
147+
buildConfigurations: [
148+
TestBuildConfiguration("Debug", buildSettings: ["PRODUCT_NAME": "$(TARGET_NAME)"]),
149+
],
150+
buildPhases: [
151+
TestSourcesBuildPhase(["S1.c"])
152+
]
153+
)
154+
]
155+
),
156+
]
157+
).load(core)
158+
let workspaceContext = WorkspaceContext(core: core, workspace: workspace, processExecutionCache: .sharedForTesting)
159+
160+
// Configure the targets and create a BuildRequest.
161+
let buildParameters = BuildParameters(configuration: "Debug")
162+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
163+
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T2")))
164+
do {
165+
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1], dependencyScope: .workspace, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
166+
let buildRequestContext = BuildRequestContext(workspaceContext: workspaceContext)
167+
let delegate = EmptyTargetDependencyResolverDelegate(workspace: workspaceContext.workspace)
168+
let buildGraph = await TargetGraphFactory(workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, delegate: delegate).graph(type: .dependency)
169+
#expect(buildGraph.allTargets.map({ $0.target.name }) == ["T3", "T2"])
170+
delegate.checkNoDiagnostics()
171+
}
172+
}
173+
174+
@Test(.requireSDKs(.host))
104175
func buildRequestScopeRemovingInteriorTarget() async throws {
105176
let core = try await getCore()
106177

@@ -159,8 +230,8 @@ import Foundation
159230

160231
// Configure the targets and create a BuildRequest.
161232
let buildParameters = BuildParameters(configuration: "Debug")
162-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
163-
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T3")!)
233+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
234+
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T3")))
164235

165236
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1, t3], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
166237
let buildRequestContext = BuildRequestContext(workspaceContext: workspaceContext)
@@ -172,7 +243,7 @@ import Foundation
172243
delegate.checkNoDiagnostics()
173244
}
174245

175-
@Test(.requireSDKs(.macOS))
246+
@Test(.requireSDKs(.host))
176247
func buildRequestScopeRemovingGroupOfInteriorTargets() async throws {
177248
let core = try await getCore()
178249

@@ -287,12 +358,12 @@ import Foundation
287358

288359
// Configure the targets and create a BuildRequest.
289360
let buildParameters = BuildParameters(configuration: "Debug")
290-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
291-
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T2")!)
292-
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T3")!)
293-
let t6 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T6")!)
294-
let t7 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T7")!)
295-
let t8 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T8")!)
361+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
362+
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T2")))
363+
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T3")))
364+
let t6 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T6")))
365+
let t7 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T7")))
366+
let t8 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T8")))
296367

297368
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1, t2, t3, t6, t7, t8], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
298369
let buildRequestContext = BuildRequestContext(workspaceContext: workspaceContext)
@@ -308,7 +379,7 @@ import Foundation
308379
delegate.checkNoDiagnostics()
309380
}
310381

311-
@Test(.requireSDKs(.macOS))
382+
@Test(.requireSDKs(.host))
312383
func buildRequestScopeRemovingImplicitAndExplicitDependencies() async throws {
313384
let core = try await getCore()
314385

@@ -390,11 +461,11 @@ import Foundation
390461

391462
// Configure the targets and create a BuildRequest.
392463
let buildParameters = BuildParameters(configuration: "Debug")
393-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
394-
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T2")!)
395-
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T3")!)
396-
let t4 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T4")!)
397-
let t5 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T5")!)
464+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
465+
let t2 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T2")))
466+
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T3")))
467+
let t4 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T4")))
468+
let t5 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T5")))
398469

399470
do {
400471
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1, t2, t3], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
@@ -500,8 +571,8 @@ import Foundation
500571

501572
// Configure the targets and create a BuildRequest.
502573
let buildParameters = BuildParameters(configuration: "Debug", activeRunDestination: .anyiOSDevice)
503-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
504-
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T3")!)
574+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
575+
let t3 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T3")))
505576

506577
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1, t3], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)
507578
let buildRequestContext = BuildRequestContext(workspaceContext: workspaceContext)
@@ -518,7 +589,7 @@ import Foundation
518589
delegate.checkNoDiagnostics()
519590
}
520591

521-
@Test(.requireSDKs(.macOS))
592+
@Test(.requireSDKs(.host))
522593
func buildRequestScopeWithPackages() async throws {
523594
let core = try await getCore()
524595

@@ -620,10 +691,10 @@ import Foundation
620691

621692
// Configure the targets and create a BuildRequest.
622693
let buildParameters = BuildParameters(configuration: "Debug")
623-
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "T1")!)
694+
let t1 = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "T1")))
624695

625-
let somePackageProduct = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "SomePackageProduct")!)
626-
let packageProductWithTransitiveRefs = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: workspace.target(named: "PackageProductWithTransitiveRefs")!)
696+
let somePackageProduct = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "SomePackageProduct")))
697+
let packageProductWithTransitiveRefs = BuildRequest.BuildTargetInfo(parameters: buildParameters, target: try #require(workspace.target(named: "PackageProductWithTransitiveRefs")))
627698

628699
do {
629700
let buildRequest = BuildRequest(parameters: buildParameters, buildTargets: [t1], dependencyScope: .buildRequest, continueBuildingAfterErrors: true, useParallelTargets: false, useImplicitDependencies: true, useDryRun: false)

0 commit comments

Comments
 (0)