Skip to content

Commit e9fa373

Browse files
authored
[NFC] Replace PackageConditionProtocol with PackageCondition (#7117)
Unblocks `PackageGraph` value types refactoring in #7160 and macros cross-compilation fix in #7118. Multiple places in our codebase pass around and store `[any PackageConditionProtocol]` arrays of existentials with `FIXME` notes that those should be sets and not arrays. We also can't convert types containing these arrays to value types with auto-generated `Hashable` conformance, since neither elements of these arrays nor arrays themselves are `Hashable`. Adding `Hashable` requirement to `PackageConditionProtocol` doesn't fix the issue, since existentials of this protocol still wouldn't conform to `Hashable`. A simple alternative is to create `enum PackageCondition` with cases for each condition type. We only have two of those types and they're always declared in the same module. There's no use case for externally defined types for this protocol. That allows us to convert uses of `[any PackageConditionProtocol]` to `[PackageCondition]`. Existing protocol is kept around until clients of SwiftPM can migrate to the new `PackageCondition` enum. This also allows us to remove a custom conformance to `Hashable` on `ResolvedTarget`, unblocking a conversion of `ResolvedTarget` to a value type and making it `Sendable` in the future.
1 parent 40dcdbd commit e9fa373

File tree

10 files changed

+103
-40
lines changed

10 files changed

+103
-40
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -869,10 +869,10 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> {
869869
enum Dependency {
870870

871871
/// Dependency to another target, with conditions.
872-
case target(_ target: ResolvedTargetBuilder, conditions: [PackageConditionProtocol])
872+
case target(_ target: ResolvedTargetBuilder, conditions: [PackageCondition])
873873

874874
/// Dependency to a product, with conditions.
875-
case product(_ product: ResolvedProductBuilder, conditions: [PackageConditionProtocol])
875+
case product(_ product: ResolvedProductBuilder, conditions: [PackageCondition])
876876
}
877877

878878
/// The target reference.
@@ -918,7 +918,10 @@ private final class ResolvedTargetBuilder: ResolvedBuilder<ResolvedTarget> {
918918
try self.target.validateDependency(target: targetBuilder.target)
919919
return .target(try targetBuilder.construct(), conditions: conditions)
920920
case .product(let productBuilder, let conditions):
921-
try self.target.validateDependency(product: productBuilder.product, productPackage: productBuilder.packageBuilder.package.identity)
921+
try self.target.validateDependency(
922+
product: productBuilder.product,
923+
productPackage: productBuilder.packageBuilder.package.identity
924+
)
922925
let product = try productBuilder.construct()
923926
if !productBuilder.packageBuilder.isAllowedToVendUnsafeProducts {
924927
try self.diagnoseInvalidUseOfUnsafeFlags(product)

Sources/PackageGraph/Resolution/ResolvedTarget.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -19,10 +19,10 @@ public final class ResolvedTarget {
1919
/// Represents dependency of a resolved target.
2020
public enum Dependency {
2121
/// Direct dependency of the target. This target is in the same package and should be statically linked.
22-
case target(_ target: ResolvedTarget, conditions: [PackageConditionProtocol])
22+
case target(_ target: ResolvedTarget, conditions: [PackageCondition])
2323

2424
/// The target depends on this product.
25-
case product(_ product: ResolvedProduct, conditions: [PackageConditionProtocol])
25+
case product(_ product: ResolvedProduct, conditions: [PackageCondition])
2626

2727
public var target: ResolvedTarget? {
2828
switch self {
@@ -38,7 +38,7 @@ public final class ResolvedTarget {
3838
}
3939
}
4040

41-
public var conditions: [PackageConditionProtocol] {
41+
public var conditions: [PackageCondition] {
4242
switch self {
4343
case .target(_, let conditions): return conditions
4444
case .product(_, let conditions): return conditions
@@ -141,7 +141,7 @@ public final class ResolvedTarget {
141141
/// The list of platforms that are supported by this target.
142142
public let platforms: SupportedPlatforms
143143

144-
/// Create a target instance.
144+
/// Create a resolved target instance.
145145
public init(
146146
target: Target,
147147
dependencies: [Dependency],

Sources/PackageLoading/PackageBuilder.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -697,7 +697,7 @@ public final class PackageBuilder {
697697

698698
// The created targets mapped to their name.
699699
var targets = [String: Target]()
700-
// If a direcotry is empty, we don't create a target object for them.
700+
// If a directory is empty, we don't create a target object for them.
701701
var emptyModules = Set<String>()
702702

703703
// Start iterating the potential targets.
@@ -710,7 +710,7 @@ public final class PackageBuilder {
710710

711711
// Get the dependencies of this target.
712712
let dependencies: [Target.Dependency] = try manifestTarget.map {
713-
try $0.dependencies.compactMap { dependency in
713+
try $0.dependencies.compactMap { dependency -> Target.Dependency? in
714714
switch dependency {
715715
case .target(let name, let condition):
716716
// We don't create an object for targets which have no sources.
@@ -1145,12 +1145,12 @@ public final class PackageBuilder {
11451145
return table
11461146
}
11471147

1148-
func buildConditions(from condition: PackageConditionDescription?) -> [PackageConditionProtocol] {
1149-
var conditions: [PackageConditionProtocol] = []
1148+
func buildConditions(from condition: PackageConditionDescription?) -> [PackageCondition] {
1149+
var conditions = [PackageCondition]()
11501150

11511151
if let config = condition?.config.flatMap({ BuildConfiguration(rawValue: $0) }) {
11521152
let condition = ConfigurationCondition(configuration: config)
1153-
conditions.append(condition)
1153+
conditions.append(.configuration(condition))
11541154
}
11551155

11561156
if let platforms = condition?.platformNames.map({
@@ -1163,7 +1163,7 @@ public final class PackageBuilder {
11631163
!platforms.isEmpty
11641164
{
11651165
let condition = PlatformsCondition(platforms: platforms)
1166-
conditions.append(condition)
1166+
conditions.append(.platforms(condition))
11671167
}
11681168

11691169
return conditions

Sources/PackageModel/BuildSettings.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2018 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -43,14 +43,14 @@ public enum BuildSettings {
4343
/// The assignment value.
4444
public var values: [String]
4545

46-
// FIXME: This should be a set but we need Equatable existential (or AnyEquatable) for that.
46+
// FIXME: This should use `Set` but we need to investigate potential build failures on Linux caused by using it.
4747
/// The condition associated with this assignment.
48-
public var conditions: [PackageConditionProtocol] {
48+
public var conditions: [PackageCondition] {
4949
get {
50-
return _conditions.map{ $0.condition }
50+
return _conditions.map { $0.underlying }
5151
}
5252
set {
53-
_conditions = newValue.map{ PackageConditionWrapper($0) }
53+
_conditions = newValue.map { PackageConditionWrapper($0) }
5454
}
5555
}
5656

Sources/PackageModel/Manifest/PackageConditionDescription.swift

+63-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -27,11 +27,12 @@ public protocol PackageConditionProtocol: Codable {
2727
func satisfies(_ environment: BuildEnvironment) -> Bool
2828
}
2929

30-
/// Wrapper for package condition so it can be conformed to Codable.
30+
/// Wrapper for package condition so it can conform to Codable.
3131
struct PackageConditionWrapper: Codable, Equatable, Hashable {
3232
var platform: PlatformsCondition?
3333
var config: ConfigurationCondition?
3434

35+
@available(*, deprecated, renamed: "underlying")
3536
var condition: PackageConditionProtocol {
3637
if let platform {
3738
return platform
@@ -42,6 +43,17 @@ struct PackageConditionWrapper: Codable, Equatable, Hashable {
4243
}
4344
}
4445

46+
var underlying: PackageCondition {
47+
if let platform {
48+
return .platforms(platform)
49+
} else if let config {
50+
return .configuration(config)
51+
} else {
52+
fatalError("unreachable")
53+
}
54+
}
55+
56+
@available(*, deprecated, message: "Use .init(_ condition: PackageCondition) instead")
4557
init(_ condition: PackageConditionProtocol) {
4658
switch condition {
4759
case let platform as PlatformsCondition:
@@ -52,6 +64,55 @@ struct PackageConditionWrapper: Codable, Equatable, Hashable {
5264
fatalError("unknown condition \(condition)")
5365
}
5466
}
67+
68+
init(_ condition: PackageCondition) {
69+
switch condition {
70+
case let .platforms(platforms):
71+
self.platform = platforms
72+
case let .configuration(configuration):
73+
self.config = configuration
74+
}
75+
}
76+
}
77+
78+
/// One of possible conditions used in package manifests to restrict targets from being built for certain platforms or
79+
/// build configurations.
80+
public enum PackageCondition: Hashable {
81+
case platforms(PlatformsCondition)
82+
case configuration(ConfigurationCondition)
83+
84+
public func satisfies(_ environment: BuildEnvironment) -> Bool {
85+
switch self {
86+
case .configuration(let configuration):
87+
return configuration.satisfies(environment)
88+
case .platforms(let platforms):
89+
return platforms.satisfies(environment)
90+
}
91+
}
92+
93+
public var platformsCondition: PlatformsCondition? {
94+
guard case let .platforms(platformsCondition) = self else {
95+
return nil
96+
}
97+
98+
return platformsCondition
99+
}
100+
101+
public var configurationCondition: ConfigurationCondition? {
102+
guard case let .configuration(configurationCondition) = self else {
103+
return nil
104+
}
105+
106+
return configurationCondition
107+
}
108+
109+
public init(platforms: [Platform]) {
110+
self = .platforms(.init(platforms: platforms))
111+
}
112+
113+
public init(configuration: BuildConfiguration) {
114+
self = .configuration(.init(configuration: configuration))
115+
}
55116
}
56117

57118
/// Platforms condition implies that an assignment is valid on these platforms.

Sources/PackageModel/Target/Target.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ public class Target: PolymorphicCodableProtocol {
7979
/// A target dependency to a target or product.
8080
public enum Dependency {
8181
/// A dependency referencing another target, with conditions.
82-
case target(_ target: Target, conditions: [PackageConditionProtocol])
82+
case target(_ target: Target, conditions: [PackageCondition])
8383

8484
/// A dependency referencing a product, with conditions.
85-
case product(_ product: ProductReference, conditions: [PackageConditionProtocol])
85+
case product(_ product: ProductReference, conditions: [PackageCondition])
8686

8787
/// The target if the dependency is a target dependency.
8888
public var target: Target? {
@@ -103,7 +103,7 @@ public class Target: PolymorphicCodableProtocol {
103103
}
104104

105105
/// The dependency conditions.
106-
public var conditions: [PackageConditionProtocol] {
106+
public var conditions: [PackageCondition] {
107107
switch self {
108108
case .target(_, let conditions):
109109
return conditions

Sources/SPMBuildCore/Plugins/PluginInvocation.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -584,7 +584,7 @@ public extension PluginTarget {
584584
}
585585

586586
fileprivate extension Target.Dependency {
587-
var conditions: [PackageConditionProtocol] {
587+
var conditions: [PackageCondition] {
588588
switch self {
589589
case .target(_, let conditions): return conditions
590590
case .product(_, let conditions): return conditions

Sources/XCBuildSupport/PIF.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -1002,7 +1002,7 @@ public enum PIF {
10021002
}
10031003

10041004
public var conditions: [String] {
1005-
let filters = [PlatformsCondition(platforms: [packageModelPlatform])].toPlatformFilters().map { (filter: PIF.PlatformFilter) -> String in
1005+
let filters = [PackageCondition(platforms: [packageModelPlatform])].toPlatformFilters().map { filter in
10061006
if filter.environment.isEmpty {
10071007
return filter.platform
10081008
} else {

Sources/XCBuildSupport/PIFBuilder.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -781,7 +781,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
781781
private func addDependency(
782782
to target: ResolvedTarget,
783783
in pifTarget: PIFTargetBuilder,
784-
conditions: [PackageConditionProtocol],
784+
conditions: [PackageCondition],
785785
linkProduct: Bool
786786
) {
787787
// Only add the binary target as a library when we want to link against the product.
@@ -803,7 +803,7 @@ final class PackagePIFProjectBuilder: PIFProjectBuilder {
803803
private func addDependency(
804804
to product: ResolvedProduct,
805805
in pifTarget: PIFTargetBuilder,
806-
conditions: [PackageConditionProtocol],
806+
conditions: [PackageCondition],
807807
linkProduct: Bool
808808
) {
809809
pifTarget.addDependency(
@@ -1498,15 +1498,15 @@ private extension BuildSettings.AssignmentTable {
14981498

14991499
private extension BuildSettings.Assignment {
15001500
var configurations: [BuildConfiguration] {
1501-
if let configurationCondition = conditions.lazy.compactMap({ $0 as? ConfigurationCondition }).first {
1501+
if let configurationCondition = conditions.lazy.compactMap(\.configurationCondition).first {
15021502
return [configurationCondition.configuration]
15031503
} else {
15041504
return BuildConfiguration.allCases
15051505
}
15061506
}
15071507

15081508
var pifPlatforms: [PIF.BuildSettings.Platform]? {
1509-
if let platformsCondition = conditions.lazy.compactMap({ $0 as? PlatformsCondition }).first {
1509+
if let platformsCondition = conditions.lazy.compactMap(\.platformsCondition).first {
15101510
return platformsCondition.platforms.compactMap { PIF.BuildSettings.Platform(rawValue: $0.name) }
15111511
} else {
15121512
return nil
@@ -1537,10 +1537,10 @@ public struct DelayedImmutable<Value> {
15371537
}
15381538
}
15391539

1540-
extension Array where Element == PackageConditionProtocol {
1540+
extension [PackageCondition] {
15411541
func toPlatformFilters() -> [PIF.PlatformFilter] {
15421542
var result: [PIF.PlatformFilter] = []
1543-
let platformConditions = self.compactMap{ $0 as? PlatformsCondition }.flatMap{ $0.platforms }
1543+
let platformConditions = self.compactMap(\.platformsCondition).flatMap { $0.platforms }
15441544

15451545
for condition in platformConditions {
15461546
switch condition {

Tests/PackageLoadingTests/PackageBuilderTests.swift

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2014-2021 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -19,8 +19,7 @@ import XCTest
1919
import class TSCBasic.InMemoryFileSystem
2020

2121
/// Tests for the handling of source layout conventions.
22-
class PackageBuilderTests: XCTestCase {
23-
22+
final class PackageBuilderTests: XCTestCase {
2423
func testDotFilesAreIgnored() throws {
2524
let fs = InMemoryFileSystem(emptyFiles:
2625
"/Sources/foo/.Bar.swift",
@@ -2990,7 +2989,7 @@ class PackageBuilderTests: XCTestCase {
29902989

29912990
var assignment = BuildSettings.Assignment()
29922991
assignment.values = ["YOLO"]
2993-
assignment.conditions = [PlatformsCondition(platforms: [PackageModel.Platform.custom(name: "bestOS", oldestSupportedVersion: .unknown)])]
2992+
assignment.conditions = [PackageCondition(platforms: [.custom(name: "bestOS", oldestSupportedVersion: .unknown)])]
29942993

29952994
var settings = BuildSettings.AssignmentTable()
29962995
settings.add(assignment, for: .SWIFT_ACTIVE_COMPILATION_CONDITIONS)

0 commit comments

Comments
 (0)