Skip to content

Commit 3997c95

Browse files
committed
Implement the manifest API changes necessary for implementing SE0226
1 parent bf3c7db commit 3997c95

22 files changed

+369
-88
lines changed

Documentation/PackageDescription.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ Represents dependency on other targets in the package or products from other pac
578578
public static func target(name: String) -> Target.Dependency
579579

580580
/// A dependency on a product from a package dependency.
581-
public static func product(name: String, package: String? = nil) -> Target.Dependency
581+
public static func product(name: String, package: String) -> Target.Dependency
582582

583583
// A by-name dependency that resolves to either a target or a product,
584584
// as above, after the package graph has been loaded.

Sources/PackageDescription4/Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,18 @@ public final class Package {
147147
}
148148
}
149149

150+
/// The name of the package, or nil to deduce it from the URL.
151+
public let name: String?
152+
150153
/// The Git url of the package dependency.
151154
public let url: String
152155

153156
/// The dependency requirement of the package dependency.
154157
public let requirement: Requirement
155158

156159
/// Initializes and returns a newly allocated requirement with the specified url and requirements.
157-
init(url: String, requirement: Requirement) {
160+
init(name: String?, url: String, requirement: Requirement) {
161+
self.name = name
158162
self.url = url
159163
self.requirement = requirement
160164
}

Sources/PackageDescription4/PackageDependency.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,30 @@ extension Package.Dependency {
2626
/// .package(url: "https://example.com/example-package.git", from: "1.2.3"),
2727
///
2828
/// - Parameters:
29+
/// - name: The name of the package, or nil to deduce it from the URL.
2930
/// - url: The valid Git URL of the package.
3031
/// - version: The minimum version requirement.
3132
public static func package(
33+
name: String? = nil,
3234
url: String,
3335
from version: Version
3436
) -> Package.Dependency {
35-
return .package(url: url, .upToNextMajor(from: version))
37+
return .package(name: name, url: url, .upToNextMajor(from: version))
3638
}
3739

3840
/// Add a remote package dependency given a version requirement.
3941
///
4042
/// - Parameters:
43+
/// - name: The name of the package, or nil to deduce it from the URL.
4144
/// - url: The valid Git URL of the package.
4245
/// - requirement: A dependency requirement. See static methods on `Package.Dependency.Requirement` for available options.
4346
public static func package(
47+
name: String? = nil,
4448
url: String,
4549
_ requirement: Package.Dependency.Requirement
4650
) -> Package.Dependency {
4751
precondition(!requirement.isLocalPackage, "Use `.package(path:)` API to declare a local package dependency")
48-
return .init(url: url, requirement: requirement)
52+
return .init(name: name, url: url, requirement: requirement)
4953
}
5054

5155
/// Add a package dependency starting with a specific minimum version, up to
@@ -57,16 +61,18 @@ extension Package.Dependency {
5761
/// .package(url: "https://example.com/example-package.git", "1.2.3"..<"1.2.6"),
5862
///
5963
/// - Parameters:
64+
/// - name: The name of the package, or nil to deduce it from the URL.
6065
/// - url: The valid Git URL of the package.
6166
/// - range: The custom version range requirement.
6267
public static func package(
68+
name: String? = nil,
6369
url: String,
6470
_ range: Range<Version>
6571
) -> Package.Dependency {
6672
#if PACKAGE_DESCRIPTION_4
67-
return .init(url: url, requirement: .rangeItem(range))
73+
return .init(name: name, url: url, requirement: .rangeItem(range))
6874
#else
69-
return .init(url: url, requirement: ._rangeItem(range))
75+
return .init(name: name, url: url, requirement: ._rangeItem(range))
7076
#endif
7177
}
7278

@@ -79,9 +85,11 @@ extension Package.Dependency {
7985
/// .package(url: "https://example.com/example-package.git", "1.2.3"..."1.2.6"),
8086
///
8187
/// - Parameters:
88+
/// - name: The name of the package, or nil to deduce it from the URL.
8289
/// - url: The valid Git URL of the package.
8390
/// - range: The closed version range requirement.
8491
public static func package(
92+
name: String? = nil,
8593
url: String,
8694
_ range: ClosedRange<Version>
8795
) -> Package.Dependency {
@@ -91,7 +99,7 @@ extension Package.Dependency {
9199
upper.major, upper.minor, upper.patch + 1,
92100
prereleaseIdentifiers: upper.prereleaseIdentifiers,
93101
buildMetadataIdentifiers: upper.buildMetadataIdentifiers)
94-
return .package(url: url, range.lowerBound..<upperBound)
102+
return .package(name: name, url: url, range.lowerBound..<upperBound)
95103
}
96104

97105
#if !PACKAGE_DESCRIPTION_4
@@ -106,7 +114,7 @@ extension Package.Dependency {
106114
public static func package(
107115
path: String
108116
) -> Package.Dependency {
109-
return .init(url: path, requirement: ._localPackageItem)
117+
return .init(name: nil, url: path, requirement: ._localPackageItem)
110118
}
111119
#endif
112120
}

Sources/PackageDescription4/Target.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
*/
1010

1111
/// A target, the basic building block of a Swift package.
12-
///
12+
///
1313
/// Each target contains a set of source files that are compiled into a module or test suite.
1414
/// You can vend targets to other packages by defining products that include the targets.
15-
///
15+
///
1616
/// A target may depend on other targets within the same package and on products vended by the package's dependencies.
1717
public final class Target {
1818

@@ -93,7 +93,7 @@ public final class Target {
9393

9494
/// The `pkgconfig` name to use for a system library target.
9595
///
96-
/// If present, the Swift Package Manager tries to
96+
/// If present, the Swift Package Manager tries to
9797
/// search for the `<name>.pc` file to get the additional flags needed for the
9898
/// system target.
9999
public let pkgConfig: String?
@@ -400,7 +400,7 @@ public final class Target {
400400

401401
#if !PACKAGE_DESCRIPTION_4
402402
/// Create a system library target.
403-
///
403+
///
404404
/// Use system library targets to adapt a library installed on the system to work with Swift packages.
405405
/// Such libraries are generally installed by system package managers (such as Homebrew and apt-get)
406406
/// and exposed to Swift packages by providing a `modulemap` file along with other metadata such as the library's `pkgConfig` name.
@@ -500,6 +500,7 @@ extension Target.Dependency {
500500
/// - parameters:
501501
/// - name: The name of the product.
502502
/// - package: The name of the package.
503+
@available(_PackageDescription, obsoleted: 5.3, message: "the 'package' argument is now mandatory")
503504
public static func product(name: String, package: String? = nil) -> Target.Dependency {
504505
#if PACKAGE_DESCRIPTION_4
505506
return .productItem(name: name, package: package)
@@ -508,6 +509,20 @@ extension Target.Dependency {
508509
#endif
509510
}
510511

512+
/// Creates a dependency on a product from a package dependency.
513+
///
514+
/// - parameters:
515+
/// - name: The name of the product.
516+
/// - package: The name of the package.
517+
@available(_PackageDescription, introduced: 5.3)
518+
public static func product(name: String, package: String) -> Target.Dependency {
519+
#if PACKAGE_DESCRIPTION_4
520+
return .productItem(name: name, package: package)
521+
#else
522+
return ._productItem(name: name, package: package)
523+
#endif
524+
}
525+
511526
/// Creates a by-name dependency that resolves to either a target or a product but
512527
/// after the package graph has been loaded.
513528
///

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public enum ManifestParseError: Swift.Error {
2323
case runtimeManifestErrors([String])
2424

2525
case duplicateDependencyDecl([[PackageDependencyDescription]])
26+
27+
case targetDependencyUnknownPackage(targetName: String, packageName: String)
2628
}
2729

2830
/// Resources required for manifest loading.
@@ -230,7 +232,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
230232
baseURL: baseURL,
231233
fileSystem: fileSystem ?? localFileSystem
232234
)
233-
try manifestBuilder.build(v4: json)
235+
try manifestBuilder.build(v4: json, toolsVersion: toolsVersion)
234236

235237
// Throw if we encountered any runtime errors.
236238
guard manifestBuilder.errors.isEmpty else {
@@ -254,17 +256,46 @@ public final class ManifestLoader: ManifestLoaderProtocol {
254256
targets: manifestBuilder.targets
255257
)
256258

257-
try validate(manifest)
259+
try validate(manifest, toolsVersion: toolsVersion)
258260

259261
return manifest
260262
}
261263

262264
/// Validate the provided manifest.
263-
private func validate(_ manifest: Manifest) throws {
265+
private func validate(_ manifest: Manifest, toolsVersion: ToolsVersion) throws {
264266
let duplicateDecls = manifest.dependencies.map({ KeyedPair($0, key: PackageReference.computeIdentity(packageURL: $0.url)) }).spm_findDuplicateElements()
265267
if !duplicateDecls.isEmpty {
266268
throw ManifestParseError.duplicateDependencyDecl(duplicateDecls.map({ $0.map({ $0.item }) }))
267269
}
270+
271+
// If the tools version is 5.3 or greater, we want to make sure all target package dependencies are valid.
272+
if toolsVersion >= .v5_3 {
273+
let targetNames = Set(manifest.targets.map({ $0.name }))
274+
for target in manifest.targets {
275+
for targetDependency in target.dependencies {
276+
// If this is a target dependency (or byName that references a target), we don't need to check.
277+
if case .target = targetDependency { continue }
278+
if case .byName(let name) = targetDependency, targetNames.contains(name) { continue }
279+
280+
// If we can't find the package dependency it references, the manifest is invalid.
281+
if manifest.packageDependency(referencedBy: targetDependency) == nil {
282+
let packageName: String
283+
switch targetDependency {
284+
case .product(_, package: let name?),
285+
.byName(let name):
286+
packageName = name
287+
default:
288+
fatalError("Invalid case: this shouldn't be a target, or a product with no name")
289+
}
290+
291+
throw ManifestParseError.targetDependencyUnknownPackage(
292+
targetName: target.name,
293+
packageName: packageName
294+
)
295+
}
296+
}
297+
}
298+
}
268299
}
269300

270301
/// Load the JSON string for the given manifest.

Sources/PackageLoading/PackageDescription4Loader.swift

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
import TSCBasic
1212
import TSCUtility
1313
import PackageModel
14+
import Foundation
1415

1516
extension ManifestBuilder {
16-
mutating func build(v4 json: JSON) throws {
17+
mutating func build(v4 json: JSON, toolsVersion: ToolsVersion) throws {
1718
let package = try json.getJSON("package")
1819
self.name = try package.get(String.self, forKey: "name")
1920
self.pkgConfig = package.get("pkgConfig")
@@ -22,9 +23,14 @@ extension ManifestBuilder {
2223
self.products = try package.getArray("products").map(ProductDescription.init(v4:))
2324
self.providers = try? package.getArray("providers").map(SystemPackageProviderDescription.init(v4:))
2425
self.targets = try package.getArray("targets").map(parseTarget(json:))
25-
self.dependencies = try package
26-
.getArray("dependencies")
27-
.map({ try PackageDependencyDescription(v4: $0, baseURL: self.baseURL, fileSystem: self.fs) })
26+
self.dependencies = try package.getArray("dependencies").map({
27+
try PackageDependencyDescription(
28+
v4: $0,
29+
toolsVersion: toolsVersion,
30+
baseURL: self.baseURL,
31+
fileSystem: self.fs
32+
)
33+
})
2834

2935
self.cxxLanguageStandard = package.get("cxxLanguageStandard")
3036
self.cLanguageStandard = package.get("cLanguageStandard")
@@ -265,7 +271,7 @@ extension PackageDependencyDescription.Requirement {
265271
}
266272

267273
extension PackageDependencyDescription {
268-
fileprivate init(v4 json: JSON, baseURL: String, fileSystem: FileSystem) throws {
274+
fileprivate init(v4 json: JSON, toolsVersion: ToolsVersion, baseURL: String, fileSystem: FileSystem) throws {
269275
let isBaseURLRemote = URL.scheme(baseURL) != nil
270276

271277
func fixURL(_ url: String, requirement: Requirement) throws -> String {
@@ -297,11 +303,14 @@ extension PackageDependencyDescription {
297303
}
298304

299305
let requirement = try Requirement(v4: json.get("requirement"))
306+
let url = try fixURL(json.get("url"), requirement: requirement)
307+
var name: String? = json.get("name")
308+
309+
if name == nil && toolsVersion >= .v5_3 {
310+
name = URL(string: url)?.deletingPathExtension().lastPathComponent
311+
}
300312

301-
try self.init(
302-
url: fixURL(json.get("url"), requirement: requirement),
303-
requirement: requirement
304-
)
313+
self.init(name: name, url: url, requirement: requirement)
305314
}
306315
}
307316

Sources/PackageModel/Manifest.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,25 @@ extension Manifest {
163163
try container.encode(targets, forKey: .targets)
164164
try container.encode(platforms, forKey: .platforms)
165165
}
166+
167+
/// Finds the package dependency referenced by the specified target dependency.
168+
/// - Returns: Returns `nil` if the dependency is a target dependency, if it is a product dependency but has no
169+
/// package name (for tools versions less than 5.3), or if there were no dependencies with the provided name.
170+
public func packageDependency(
171+
referencedBy targetDependency: TargetDescription.Dependency
172+
) -> PackageDependencyDescription? {
173+
let packageName: String
174+
175+
switch targetDependency {
176+
case .product(_, package: let name?),
177+
.byName(name: let name):
178+
packageName = name
179+
default:
180+
return nil
181+
}
182+
183+
return dependencies.first(where: { $0.name == packageName })
184+
}
166185
}
167186

168187
/// The description of an individual target.
@@ -346,14 +365,18 @@ public struct PackageDependencyDescription: Equatable, Codable {
346365
}
347366
}
348367

368+
/// The name of the dependency.
369+
public let name: String?
370+
349371
/// The url of the dependency.
350372
public let url: String
351373

352374
/// The dependency requirement.
353375
public let requirement: Requirement
354376

355377
/// Create a dependency.
356-
public init(url: String, requirement: Requirement) {
378+
public init(name: String?, url: String, requirement: Requirement) {
379+
self.name = name
357380
self.url = url
358381
self.requirement = requirement
359382
}

Sources/PackageModel/ToolsVersion.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public struct ToolsVersion: CustomStringConvertible, Comparable, Hashable, Codab
2121
public static let v4_2 = ToolsVersion(version: "4.2.0")
2222
public static let v5 = ToolsVersion(version: "5.0.0")
2323
public static let v5_2 = ToolsVersion(version: "5.2.0")
24+
public static let v5_3 = ToolsVersion(version: "5.3.0")
2425

2526
/// The current tools version in use.
2627
public static let currentToolsVersion = ToolsVersion(string:

Sources/SPMTestSupport/MockDependencyGraph.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public struct MockManifestGraph {
178178
) -> [PackageDependencyDescription] {
179179
return dependencies.map({ dependency in
180180
return PackageDependencyDescription(
181+
name: dependency.name,
181182
url: repos[dependency.name]?.url ?? "//\(dependency.name)",
182183
requirement: .range(dependency.version.lowerBound ..< dependency.version.upperBound))
183184
})

Sources/SPMTestSupport/TestWorkspace.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,11 @@ public struct TestDependency {
474474
}
475475

476476
public func convert(baseURL: AbsolutePath) -> PackageDependencyDescription {
477-
return PackageDependencyDescription(url: baseURL.appending(RelativePath(name)).pathString, requirement: requirement)
477+
return PackageDependencyDescription(
478+
name: name,
479+
url: baseURL.appending(RelativePath(name)).pathString,
480+
requirement: requirement
481+
)
478482
}
479483
}
480484

Sources/Workspace/Diagnostics.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ public struct ManifestDuplicateDeclDiagnostic: DiagnosticData {
5252
}
5353
}
5454

55+
public struct TargetDependencyUnknownPackageDiagnostic: DiagnosticData {
56+
public let targetName: String
57+
public let packageName: String
58+
59+
public var description: String {
60+
"manifest parse error: target '\(targetName)' depends on an unknown package '\(packageName)'\n"
61+
}
62+
}
63+
5564
extension ManifestParseError: DiagnosticDataConvertible {
5665
public var diagnosticData: DiagnosticData {
5766
switch self {
@@ -61,6 +70,8 @@ extension ManifestParseError: DiagnosticDataConvertible {
6170
return ManifestParseDiagnostic(errors, diagnosticFile: nil)
6271
case .duplicateDependencyDecl(let duplicates):
6372
return ManifestDuplicateDeclDiagnostic(duplicates)
73+
case .targetDependencyUnknownPackage(let targetName, let packageName):
74+
return TargetDependencyUnknownPackageDiagnostic(targetName: targetName, packageName: packageName)
6475
}
6576
}
6677
}

0 commit comments

Comments
 (0)