Skip to content

Conditional target dependencies manifest api #2425

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

Closed
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
5 changes: 4 additions & 1 deletion Sources/Build/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ extension BuildParameters {

/// Returns the scoped view of build settings for a given target.
fileprivate func createScope(for target: ResolvedTarget) -> BuildSettings.Scope {
return BuildSettings.Scope(target.underlyingTarget.buildSettings, boundCondition: (currentPlatform, configuration))
return BuildSettings.Scope(
target.underlyingTarget.buildSettings,
environment: BuildEnvironment(platform: currentPlatform, configuration: configuration)
)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/PackageDescription4/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public struct BuildConfiguration: Encodable {
/// .define("ENABLE_SOMETHING", .when(configuration: .release)),
/// ],
/// linkerSettings: [
/// .linkLibrary("openssl", .when(platforms: [.linux])),
/// .linkedLibrary("openssl", .when(platforms: [.linux])),
/// ]
/// ),
public struct BuildSettingCondition: Encodable {
Expand Down Expand Up @@ -228,7 +228,7 @@ public struct SwiftSetting: Encodable {
/// Use compilation conditions to only compile statements if a certain condition is true.
/// For example, the Swift compiler will only compile the
/// statements inside the `#if` block when `ENABLE_SOMETHING` is defined:
///
///
/// #if ENABLE_SOMETHING
/// ...
/// #endif
Expand Down
10 changes: 7 additions & 3 deletions Sources/PackageDescription4/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ extension Target.Dependency: Encodable {
case type
case name
case package
case condition
}

private enum Kind: String, Codable {
Expand All @@ -477,16 +478,19 @@ extension Target.Dependency: Encodable {
}
#else
switch self {
case ._targetItem(let name):
case ._targetItem(let name, let condition):
try container.encode(Kind.target, forKey: .type)
try container.encode(name, forKey: .name)
case ._productItem(let name, let package):
try container.encode(condition, forKey: .condition)
case ._productItem(let name, let package, let condition):
try container.encode(Kind.product, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(package, forKey: .package)
case ._byNameItem(let name):
try container.encode(condition, forKey: .condition)
case ._byNameItem(let name, let condition):
try container.encode(Kind.byName, forKey: .type)
try container.encode(name, forKey: .name)
try container.encode(condition, forKey: .condition)
}
#endif
}
Expand Down
62 changes: 44 additions & 18 deletions Sources/PackageDescription4/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public final class Target {
case productItem(name: String, package: String?)
case byNameItem(name: String)
#else
case _targetItem(name: String)
case _productItem(name: String, package: String?)
case _byNameItem(name: String)
case _targetItem(name: String, condition: BuildSettingCondition?)
case _productItem(name: String, package: String?, condition: BuildSettingCondition?)
case _byNameItem(name: String, condition: BuildSettingCondition?)
#endif
}

Expand Down Expand Up @@ -490,11 +490,12 @@ extension Target.Dependency {
///
/// - parameters:
/// - name: The name of the target.
@available(_PackageDescription, obsoleted: 5.2)
public static func target(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .targetItem(name: name)
#else
return ._targetItem(name: name)
return ._targetItem(name: name, condition: nil)
#endif
}

Expand All @@ -508,36 +509,61 @@ extension Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .productItem(name: name, package: package)
#else
return ._productItem(name: name, package: package)
return ._productItem(name: name, package: package, condition: nil)
#endif
}

/// Creates a by-name dependency that resolves to either a target or a product but
/// after the package graph has been loaded.
///
/// - parameters:
/// - name: The name of the dependency, either a target or a product.
@available(_PackageDescription, obsoleted: 5.2)
public static func byName(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .byNameItem(name: name)
#else
return ._byNameItem(name: name, condition: nil)
#endif
}

#if !PACKAGE_DESCRIPTION_4
/// Creates a dependency on a target in the same package.
///
/// - parameters:
/// - name: The name of the target.
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 5.2)
public static func target(name: String, condition: BuildSettingCondition? = nil) -> Target.Dependency {
return ._targetItem(name: name, condition: condition)
}

/// Creates a dependency on a product from a package dependency.
///
/// - parameters:
/// - name: The name of the product.
/// - package: The name of the package.
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 5.2)
public static func product(name: String, package: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .productItem(name: name, package: package)
#else
return ._productItem(name: name, package: package)
#endif
public static func product(
name: String,
package: String,
condition: BuildSettingCondition? = nil
) -> Target.Dependency {
return ._productItem(name: name, package: package, condition: condition)
}

/// Creates a by-name dependency that resolves to either a target or a product but
/// after the package graph has been loaded.
///
/// - parameters:
/// - name: The name of the dependency, either a target or a product.
public static func byName(name: String) -> Target.Dependency {
#if PACKAGE_DESCRIPTION_4
return .byNameItem(name: name)
#else
return ._byNameItem(name: name)
#endif
/// - condition: The condition under which the dependency is exercised.
@available(_PackageDescription, introduced: 5.2)
public static func byName(name: String, condition: BuildSettingCondition? = nil) -> Target.Dependency {
return ._byNameItem(name: name, condition: condition)
}
#endif
}

// MARK: ExpressibleByStringLiteral
Expand All @@ -552,7 +578,7 @@ extension Target.Dependency: ExpressibleByStringLiteral {
#if PACKAGE_DESCRIPTION_4
self = .byNameItem(name: value)
#else
self = ._byNameItem(name: value)
self = ._byNameItem(name: value, condition: nil)
#endif
}
}
6 changes: 3 additions & 3 deletions Sources/PackageLoading/ManifestLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,14 @@ public final class ManifestLoader: ManifestLoaderProtocol {
for targetDependency in target.dependencies {
// If this is a target dependency (or byName that references a target), we don't need to check.
if case .target = targetDependency { continue }
if case .byName(let name) = targetDependency, targetNames.contains(name) { continue }
if case .byName(let name, _) = targetDependency, targetNames.contains(name) { continue }

// If we can't find the package dependency it references, the manifest is invalid.
if manifest.packageDependency(referencedBy: targetDependency) == nil {
let packageName: String
switch targetDependency {
case .product(_, package: let name?),
.byName(let name):
case .product(_, package: let name?, _),
.byName(let name, _):
packageName = name
default:
fatalError("Invalid case: this shouldn't be a target, or a product with no name")
Expand Down
19 changes: 9 additions & 10 deletions Sources/PackageLoading/PackageBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -526,14 +526,14 @@ public final class PackageBuilder {
guard let target = targetMap[$0.name] else { return [] }
return target.dependencies.compactMap({
switch $0 {
case .target(let name):
case .target(let name, _):
// Since we already checked above that all referenced targets
// has to present, we always expect this target to be present in
// potentialModules dictionary.
return potentialModuleMap[name]!
case .product:
return nil
case .byName(let name):
case .byName(let name, _):
// By name dependency may or may not be a target dependency.
return potentialModuleMap[name]
}
Expand All @@ -559,12 +559,12 @@ public final class PackageBuilder {
let deps: [Target] = targetMap[potentialModule.name].map({
$0.dependencies.compactMap({
switch $0 {
case .target(let name):
case .target(let name, _):
// We don't create an object for targets which have no sources.
if emptyModules.contains(name) { return nil }
return targets[name]!

case .byName(let name):
case .byName(let name, _):
// We don't create an object for targets which have no sources.
if emptyModules.contains(name) { return nil }
return targets[name]
Expand All @@ -583,10 +583,10 @@ public final class PackageBuilder {
switch $0 {
case .target:
return nil
case .byName(let name):
case .byName(let name, _):
// If this dependency was not found locally, it is a product dependency.
return potentialModuleMap[name] == nil ? (name, nil) : nil
case .product(let name, let package):
case .product(let name, let package, _):
return (name, package)
}
}) ?? []
Expand Down Expand Up @@ -788,13 +788,12 @@ public final class PackageBuilder {
assignment.value = setting.value

if let config = setting.condition?.config.map({ BuildConfiguration(rawValue: $0)! }) {
let condition = BuildSettings.ConfigurationCondition(config)
let condition = ConfigurationCondition(configuration: config)
assignment.conditions.append(condition)
}

if let platforms = setting.condition?.platformNames.map({ platformRegistry.platformByName[$0]! }), !platforms.isEmpty {
var condition = BuildSettings.PlatformsCondition()
condition.platforms = platforms
let condition = PlatformsCondition(platforms: platforms)
assignment.conditions.append(condition)
}

Expand Down Expand Up @@ -1116,7 +1115,7 @@ private extension Manifest {
let names = targets.flatMap({ target in
[target.name] + target.dependencies.compactMap({
switch $0 {
case .target(let name):
case .target(let name, _):
return name
case .byName, .product:
return nil
Expand Down
32 changes: 16 additions & 16 deletions Sources/PackageLoading/PackageDescription4Loader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,7 @@ extension ManifestBuilder {
func parseBuildSetting(_ json: JSON, tool: TargetBuildSettingDescription.Tool) throws -> TargetBuildSettingDescription.Setting {
let json = try json.getJSON("data")
let name = try TargetBuildSettingDescription.SettingName(rawValue: json.get("name"))!

var condition: TargetBuildSettingDescription.Condition?
if let conditionJSON = try? json.getJSON("condition") {
condition = try parseCondition(conditionJSON)
}
let condition = try (try? json.getJSON("condition")).flatMap(ManifestConditionDescription.init(v4:))

let value = try json.get([String].self, forKey: "value")

Expand All @@ -176,14 +172,6 @@ extension ManifestBuilder {

/// Looks for Xcode-style build setting macros "$()".
private static let invalidValueRegex = try! RegEx(pattern: #"(\$\(.*?\))"#)

func parseCondition(_ json: JSON) throws -> TargetBuildSettingDescription.Condition {
let platformNames: [String]? = try? json.getArray("platforms").map({ try $0.get("name") })
return .init(
platformNames: platformNames ?? [],
config: try? json.get("config").get("config")
)
}
}

extension SystemPackageProviderDescription {
Expand Down Expand Up @@ -332,20 +320,32 @@ extension TargetDescription.TargetType {
extension TargetDescription.Dependency {
fileprivate init(v4 json: JSON) throws {
let type = try json.get(String.self, forKey: "type")
let condition = try (try? json.getJSON("condition")).flatMap(ManifestConditionDescription.init(v4:))

switch type {
case "target":
self = try .target(name: json.get("name"))
self = try .target(name: json.get("name"), condition: condition)

case "product":
let name = try json.get(String.self, forKey: "name")
self = .product(name: name, package: json.get("package"))
self = .product(name: name, package: json.get("package"), condition: condition)

case "byname":
self = try .byName(name: json.get("name"))
self = try .byName(name: json.get("name"), condition: condition)

default:
fatalError()
}
}
}

extension ManifestConditionDescription {
fileprivate init?(v4 json: JSON) throws {
if case .null = json { return nil }
let platformNames: [String]? = try? json.getArray("platforms").map({ try $0.get("name") })
self.init(
platformNames: platformNames ?? [],
config: try? json.get("config").get("config")
)
}
}
Loading