Skip to content

rdar://64344067 (Architecture specialization) #462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions Sources/SWBCore/DependencyResolution.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,25 @@ public protocol TargetDependencyResolverDelegate: AnyObject, TargetDiagnosticPro

/// 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.
struct SuperimposedProperties: Hashable, CustomStringConvertible {
let architectures: [String]
let mergeableLibrary: Bool

var forPropagation: Self {
// Do not propagate `mergeableLibrary` to clients.
return .init(architectures: self.architectures, mergeableLibrary: false)
}

var description: String {
return "mergeableLibrary: \(mergeableLibrary)"
return "mergeableLibrary: \(mergeableLibrary), architectures: \(architectures)"
}

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

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

/// Return an effective set of superimposed properties based on a specific target-dependency pair.
func effectiveProperties(target configuredTarget: ConfiguredTarget, dependency: ConfiguredTarget, dependencyResolver: DependencyResolver) -> SuperimposedProperties {
let targetSettings = dependencyResolver.buildRequestContext.getCachedSettings(configuredTarget.parameters, target: configuredTarget.target)
let architectures = Set(self.architectures + targetSettings.globalScope.evaluate(BuiltinMacros.ARCHS))
let mergeableLibrary = {
// If mergeableLibrary is enabled, we only apply it if the dependency is in the target's Link Binary build phase.
// 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.
Expand All @@ -76,7 +87,7 @@ struct SuperimposedProperties: Hashable, CustomStringConvertible {
}
return false
}()
return type(of: self).init(mergeableLibrary: mergeableLibrary)
return type(of: self).init(architectures: Array(architectures), mergeableLibrary: mergeableLibrary)
}
}

Expand Down Expand Up @@ -383,13 +394,13 @@ extension BuildRequestContext {
}

extension DependencyResolver {
nonisolated func specializationParameters(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext) -> SpecializationParameters {
SpecializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, makeAggregateTargetsTransparentForSpecialization: makeAggregateTargetsTransparentForSpecialization)
nonisolated func specializationParameters(_ configuredTarget: ConfiguredTarget, workspaceContext: WorkspaceContext, buildRequest: BuildRequest, buildRequestContext: BuildRequestContext, superimposedProperties: SuperimposedProperties?) -> SpecializationParameters {
SpecializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, makeAggregateTargetsTransparentForSpecialization: makeAggregateTargetsTransparentForSpecialization, superimposedProperties: superimposedProperties)
}
}

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

let canonicalNameSuffix: String?
if let sdk = configuredTargetSettings.sdk {
Expand Down Expand Up @@ -951,8 +964,20 @@ extension SpecializationParameters {
imposedToolchain = nil
}

// If we did not specialize based on platform, we should not super impose any arches.
let filteredSuperimposedProperties: SuperimposedProperties?
if let superimposedProperties = specialization.superimposedProperties {
if shouldImposePlatform && specializationIsSupported {
filteredSuperimposedProperties = superimposedProperties
} else {
filteredSuperimposedProperties = .init(architectures: [], mergeableLibrary: superimposedProperties.mergeableLibrary)
}
} else {
filteredSuperimposedProperties = nil
}

let fromPackage = workspaceContext.workspace.project(for: forTarget).isPackage
let filteredSpecialization = SpecializationParameters(source: .synthesized, platform: imposedPlatform, sdkVariant: imposedSdkVariant, supportedPlatforms: imposedSupportedPlatforms, toolchain: imposedToolchain, canonicalNameSuffix: imposedCanonicalNameSuffix, superimposedProperties: specialization.superimposedProperties)
let filteredSpecialization = SpecializationParameters(source: .synthesized, platform: imposedPlatform, sdkVariant: imposedSdkVariant, supportedPlatforms: imposedSupportedPlatforms, toolchain: imposedToolchain, canonicalNameSuffix: imposedCanonicalNameSuffix, superimposedProperties: filteredSuperimposedProperties)

// Otherwise, we need to create a new specialization; do so by imposing the specialization on the build parameters.
// 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).
Expand Down
6 changes: 3 additions & 3 deletions Sources/SWBCore/LinkageDependencyResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ actor LinkageDependencyResolver {
await resolver.concurrentPerform(iterations: topLevelTargetsToDiscover.count, maximumParallelism: 100) { [self] i in
if Task.isCancelled { return }
let configuredTarget = topLevelTargetsToDiscover[i]
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
let dependenciesOnPath = LinkageDependencies()
await linkageDependencies(for: configuredTarget, imposedParameters: imposedParameters, dependenciesOnPath: dependenciesOnPath)
}
Expand Down Expand Up @@ -179,10 +179,10 @@ actor LinkageDependencyResolver {
if let imposedParameters = imposedParameters, dependency.target.target.type == .aggregate {
imposedParametersForDependency = imposedParameters
} else {
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
}
} else {
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
}
await self.linkageDependencies(for: dependency.target, imposedParameters: imposedParametersForDependency, dependenciesOnPath: dependenciesOnPath)
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/SWBCore/TargetDependencyResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ fileprivate extension TargetDependencyResolver {
await resolver.concurrentPerform(iterations: topLevelTargetsToDiscover.count, maximumParallelism: 100) { [self] i in
if Task.isCancelled { return }
let configuredTarget = topLevelTargetsToDiscover[i]
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
let imposedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
await discoverInfo(for: configuredTarget, imposedParameters: imposedParameters)
}
}
Expand Down Expand Up @@ -451,7 +451,7 @@ fileprivate extension TargetDependencyResolver {
let targetsUsingSuffixedSDK = configuredTargets.filter { settingsForConfiguredTarget[$0]?.sdk?.canonicalNameSuffix?.nilIfEmpty != nil }
if let suffixedTarget = targetsUsingSuffixedSDK.first, targetsUsingSuffixedSDK.count == 1 {
// Compute specialization parameters without opinion about the suffixed SDK.
let fullParameters = resolver.specializationParameters(suffixedTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
let fullParameters = resolver.specializationParameters(suffixedTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
let parameters = SpecializationParameters(source: .target(name: suffixedTarget.target.name), platform: fullParameters.platform, sdkVariant: fullParameters.sdkVariant, supportedPlatforms: fullParameters.supportedPlatforms, toolchain: nil, canonicalNameSuffix: nil)

// Check if any of the unsuffixed targets are incompatible with parameters other than whether the suffixed SDK is being used.
Expand Down Expand Up @@ -681,10 +681,10 @@ fileprivate extension TargetDependencyResolver {
if let imposedParameters = imposedParameters, dependency.target.target.type == .aggregate {
imposedParametersForDependency = imposedParameters
} else {
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
}
} else {
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
imposedParametersForDependency = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
}
await self.discoverInfo(for: dependency.target, imposedParameters: imposedParametersForDependency)
}
Expand Down Expand Up @@ -754,7 +754,7 @@ fileprivate extension TargetDependencyResolver {
if resolver.makeAggregateTargetsTransparentForSpecialization && dependency.target.target.type == .aggregate {
dependencyImposedParameters = imposedParameters
} else {
dependencyImposedParameters = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
dependencyImposedParameters = resolver.specializationParameters(dependency.target, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
}
await addDependencies(forConfiguredTarget: dependency.target, toDependencyClosure: &dependencyClosure, dependencyPath: &dependencyPath, imposedParameters: dependencyImposedParameters)
}
Expand All @@ -767,7 +767,7 @@ fileprivate extension TargetDependencyResolver {
if resolver.makeAggregateTargetsTransparentForSpecialization {
// 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.
if imposedParameters == nil && configuredDependency.target.target.type == .aggregate && configuredTarget.target.type != .aggregate {
imposedParametersForDependency = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
imposedParametersForDependency = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: imposedParameters?.superimposedProperties?.forPropagation)
} else {
imposedParametersForDependency = imposedParameters
}
Expand All @@ -788,7 +788,7 @@ fileprivate extension TargetDependencyResolver {
// 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
var specializedParameters = imposedParameters?.effectiveParameters(target: configuredTarget, dependency: ConfiguredTarget(parameters: buildParameters, target: dependency), dependencyResolver: resolver)
if specializedParameters == nil {
specializedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext)
specializedParameters = resolver.specializationParameters(configuredTarget, workspaceContext: workspaceContext, buildRequest: buildRequest, buildRequestContext: buildRequestContext, superimposedProperties: nil)
}

// Get the configured dependency. Package product dependencies are always 'explicit'.
Expand Down
Loading