Skip to content

Commit 4ab9701

Browse files
committed
rdar://64344067 (Architecture specialization)
This implements a "lite" version of architecture specialization, piggy-backing on superimposed parameters. The lite part is that we do not actually build an accurate set of architectures per client but instead will build the superset of all architectures required by any client, plus the default set of architectures inherent to the target. This means we might be overbuilding in certain cases, but we should no longer have issues with arches that are required by client targets missing from specialized targets.
1 parent d42d61d commit 4ab9701

File tree

3 files changed

+42
-17
lines changed

3 files changed

+42
-17
lines changed

Sources/SWBCore/DependencyResolution.swift

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,25 @@ public protocol TargetDependencyResolverDelegate: AnyObject, TargetDiagnosticPro
3333

3434
/// A structure of properties which should be superimposed, that is, imposed on all compatible versions of a target in the build graph. Applying them will coalesce targets in the dependency graph which are otherwise identical except for the presence of these properties.
3535
struct SuperimposedProperties: Hashable, CustomStringConvertible {
36+
let architectures: [String]
3637
let mergeableLibrary: Bool
3738

39+
var forPropagation: Self {
40+
// Do not propagate `mergeableLibrary` to clients.
41+
return .init(architectures: self.architectures, mergeableLibrary: false)
42+
}
43+
3844
var description: String {
39-
return "mergeableLibrary: \(mergeableLibrary)"
45+
return "mergeableLibrary: \(mergeableLibrary), architectures: \(architectures)"
4046
}
4147

4248
/// Returns the overriding build settings dictionary for these properties as appropriate for the given `ConfiguredTarget`.
4349
func overrides(_ configuredTarget: ConfiguredTarget, _ settings: Settings) -> [String: String] {
4450
var overrides = [String: String]()
4551

52+
if !architectures.isEmpty {
53+
overrides[BuiltinMacros.ARCHS.name] = Set(settings.globalScope.evaluate(BuiltinMacros.ARCHS) + architectures).joined(separator: " ")
54+
}
4655
if mergeableLibrary, settings.productType?.autoConfigureAsMergeableLibrary(settings.globalScope) ?? false {
4756
overrides[BuiltinMacros.MERGEABLE_LIBRARY.name] = "YES"
4857
}
@@ -52,6 +61,8 @@ struct SuperimposedProperties: Hashable, CustomStringConvertible {
5261

5362
/// Return an effective set of superimposed properties based on a specific target-dependency pair.
5463
func effectiveProperties(target configuredTarget: ConfiguredTarget, dependency: ConfiguredTarget, dependencyResolver: DependencyResolver) -> SuperimposedProperties {
64+
let targetSettings = dependencyResolver.buildRequestContext.getCachedSettings(configuredTarget.parameters, target: configuredTarget.target)
65+
let architectures = Set(self.architectures + targetSettings.globalScope.evaluate(BuiltinMacros.ARCHS))
5566
let mergeableLibrary = {
5667
// If mergeableLibrary is enabled, we only apply it if the dependency is in the target's Link Binary build phase.
5768
// Note that this doesn't check (for example) linkages defined in OTHER_LDFLAGS, because those are already specifying concrete linkages, and this is used by the automatic merged binary workflow which isn't going to edit those linkages. Projects which want to specify linkages via OTHER_LDFLAGS for merged binaries will need to use manual configuration.
@@ -76,7 +87,7 @@ struct SuperimposedProperties: Hashable, CustomStringConvertible {
7687
}
7788
return false
7889
}()
79-
return type(of: self).init(mergeableLibrary: mergeableLibrary)
90+
return type(of: self).init(architectures: Array(architectures), mergeableLibrary: mergeableLibrary)
8091
}
8192
}
8293

@@ -383,13 +394,13 @@ extension BuildRequestContext {
383394
}
384395

385396
extension DependencyResolver {
386-
nonisolated func specializationParameters(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext) -> SpecializationParameters {
387-
SpecializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, makeAggregateTargetsTransparentForSpecialization: makeAggregateTargetsTransparentForSpecialization)
397+
nonisolated func specializationParameters(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext, superimposedProperties: SuperimposedProperties?) -> SpecializationParameters {
398+
SpecializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, makeAggregateTargetsTransparentForSpecialization: makeAggregateTargetsTransparentForSpecialization, superimposedProperties: superimposedProperties)
388399
}
389400
}
390401

391402
extension SpecializationParameters {
392-
init(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext, makeAggregateTargetsTransparentForSpecialization: Bool) {
403+
init(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext, makeAggregateTargetsTransparentForSpecialization: Bool, superimposedProperties: SuperimposedProperties?) {
393404
if configuredTarget.target.type == .aggregate && makeAggregateTargetsTransparentForSpecialization {
394405
self.init(workspaceContext: workspaceContext, buildRequestContext: buildRequestContext, parameters: buildRequest.parameters)
395406
} else {
@@ -422,7 +433,9 @@ extension SpecializationParameters {
422433
let scope = configuredTargetSettings.globalScope
423434
// If this target has AUTOMATICALLY_MERGE_DEPENDENCIES set, then its direct dependencies are configured as mergeable libraries.
424435
let mergeableLibrary = scope.evaluate(BuiltinMacros.AUTOMATICALLY_MERGE_DEPENDENCIES)
425-
let superimposedProperties = SuperimposedProperties(mergeableLibrary: mergeableLibrary)
436+
// We will propagate any architectures from superimposed properties here since we want to compute the union of all required arches in the graph.
437+
let architectures = Set(scope.evaluate(BuiltinMacros.ARCHS) + (superimposedProperties?.architectures ?? []))
438+
let superimposedProperties = SuperimposedProperties(architectures: Array(architectures), mergeableLibrary: mergeableLibrary)
426439

427440
let canonicalNameSuffix: String?
428441
if let sdk = configuredTargetSettings.sdk {
@@ -951,8 +964,20 @@ extension SpecializationParameters {
951964
imposedToolchain = nil
952965
}
953966

967+
// If we did not specialize based on platform, we should not super impose any arches.
968+
let filteredSuperimposedProperties: SuperimposedProperties?
969+
if let superimposedProperties = specialization.superimposedProperties {
970+
if shouldImposePlatform && specializationIsSupported {
971+
filteredSuperimposedProperties = superimposedProperties
972+
} else {
973+
filteredSuperimposedProperties = .init(architectures: [], mergeableLibrary: superimposedProperties.mergeableLibrary)
974+
}
975+
} else {
976+
filteredSuperimposedProperties = nil
977+
}
978+
954979
let fromPackage = workspaceContext.workspace.project(for: forTarget).isPackage
955-
let filteredSpecialization = SpecializationParameters(source: .synthesized, platform: imposedPlatform, sdkVariant: imposedSdkVariant, supportedPlatforms: imposedSupportedPlatforms, toolchain: imposedToolchain, canonicalNameSuffix: imposedCanonicalNameSuffix, superimposedProperties: specialization.superimposedProperties)
980+
let filteredSpecialization = SpecializationParameters(source: .synthesized, platform: imposedPlatform, sdkVariant: imposedSdkVariant, supportedPlatforms: imposedSupportedPlatforms, toolchain: imposedToolchain, canonicalNameSuffix: imposedCanonicalNameSuffix, superimposedProperties: filteredSuperimposedProperties)
956981

957982
// Otherwise, we need to create a new specialization; do so by imposing the specialization on the build parameters.
958983
// NOTE: If the target doesn't support specialization, then unless the target comes from a package, then it's important to **not** impart those settings unless they are coming from overrides. Doing so has the side-effect of causing dependencies of downstream targets to be specialized incorrectly (e.g. a specialized target shouldn't cause its own dependencies to be specialized).

Sources/SWBCore/LinkageDependencyResolver.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ actor LinkageDependencyResolver {
110110
await resolver.concurrentPerform(iterations: topLevelTargetsToDiscover.count, maximumParallelism: 100) { [self] i in
111111
if Task.isCancelled { return }
112112
let configuredTarget = topLevelTargetsToDiscover[i]
113-
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
113+
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
114114
let dependenciesOnPath = LinkageDependencies()
115115
await linkageDependencies(for: configuredTarget, imposedParameters: imposedParameters, dependenciesOnPath: dependenciesOnPath)
116116
}
@@ -179,10 +179,10 @@ actor LinkageDependencyResolver {
179179
if let imposedParameters = imposedParameters, dependency.target.target.type == .aggregate {
180180
imposedParametersForDependency = imposedParameters
181181
} else {
182-
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
182+
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
183183
}
184184
} else {
185-
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
185+
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
186186
}
187187
await self.linkageDependencies(for: dependency.target, imposedParameters: imposedParametersForDependency, dependenciesOnPath: dependenciesOnPath)
188188
}

Sources/SWBCore/TargetDependencyResolver.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ fileprivate extension TargetDependencyResolver {
334334
await resolver.concurrentPerform(iterations: topLevelTargetsToDiscover.count, maximumParallelism: 100) { [self] i in
335335
if Task.isCancelled { return }
336336
let configuredTarget = topLevelTargetsToDiscover[i]
337-
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
337+
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
338338
await discoverInfo(for: configuredTarget, imposedParameters: imposedParameters)
339339
}
340340
}
@@ -451,7 +451,7 @@ fileprivate extension TargetDependencyResolver {
451451
let targetsUsingSuffixedSDK = configuredTargets.filter { settingsForConfiguredTarget[$0]?.sdk?.canonicalNameSuffix?.nilIfEmpty != nil }
452452
if let suffixedTarget = targetsUsingSuffixedSDK.first, targetsUsingSuffixedSDK.count == 1 {
453453
// Compute specialization parameters without opinion about the suffixed SDK.
454-
let fullParameters = resolver.specializationParameters(suffixedTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
454+
let fullParameters = resolver.specializationParameters(suffixedTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
455455
let parameters = SpecializationParameters(source: .target(name: suffixedTarget.target.name), platform: fullParameters.platform, sdkVariant: fullParameters.sdkVariant, supportedPlatforms: fullParameters.supportedPlatforms, toolchain: nil, canonicalNameSuffix: nil)
456456

457457
// Check if any of the unsuffixed targets are incompatible with parameters other than whether the suffixed SDK is being used.
@@ -681,10 +681,10 @@ fileprivate extension TargetDependencyResolver {
681681
if let imposedParameters = imposedParameters, dependency.target.target.type == .aggregate {
682682
imposedParametersForDependency = imposedParameters
683683
} else {
684-
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
684+
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
685685
}
686686
} else {
687-
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
687+
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
688688
}
689689
await self.discoverInfo(for: dependency.target, imposedParameters: imposedParametersForDependency)
690690
}
@@ -754,7 +754,7 @@ fileprivate extension TargetDependencyResolver {
754754
if resolver.makeAggregateTargetsTransparentForSpecialization && dependency.target.target.type == .aggregate {
755755
dependencyImposedParameters = imposedParameters
756756
} else {
757-
dependencyImposedParameters = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
757+
dependencyImposedParameters = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
758758
}
759759
await addDependencies(forConfiguredTarget: dependency.target, toDependencyClosure: &dependencyClosure, dependencyPath: &dependencyPath, imposedParameters: dependencyImposedParameters)
760760
}
@@ -767,7 +767,7 @@ fileprivate extension TargetDependencyResolver {
767767
if resolver.makeAggregateTargetsTransparentForSpecialization {
768768
// Aggregate targets should be transparent for specialization, so unless we already have imposed parameters, we will compute them based on the parent of the aggregate unless that is an aggregate itself.
769769
if imposedParameters == nil && configuredDependency.target.target.type == .aggregate && configuredTarget.target.type != .aggregate {
770-
imposedParametersForDependency = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
770+
imposedParametersForDependency = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
771771
} else {
772772
imposedParametersForDependency = imposedParameters
773773
}
@@ -788,7 +788,7 @@ fileprivate extension TargetDependencyResolver {
788788
// FIXME: We eventually will also need to reconcile conflicting requirements, one example: <rdar://problem/31587072> In Swift Build, creating ConfiguredTargets from Targets should take into account minimum deployment target
789789
var specializedParameters = imposedParameters?.effectiveParameters(target: configuredTarget, dependency: ConfiguredTarget(parameters: buildParameters, target: dependency), dependencyResolver: resolver)
790790
if specializedParameters == nil {
791-
specializedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
791+
specializedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
792792
}
793793

794794
// Get the configured dependency. Package product dependencies are always 'explicit'.

0 commit comments

Comments
 (0)