Skip to content

Commit e2799da

Browse files
authored
improve and simplify validation of target based dependencies (#3280)
motivation: SwiftPM 5.2 introduced target based dependencies which added complexity at code level and to users, this PR tries to both simplify the "rules" and the code changes: * allow target dependency in the form of ".product(name:, package:)" where package parameter is the last segment of the dependecy URL, which is the most intuitive choice * change validation code to encourage the above from instead of encouraging adding "name" attibute to the dependency decleration, as we want to get away from adding this attribute in the long run * add several tests that capture the numerous permutations we are coding for
1 parent 53457c3 commit e2799da

File tree

5 files changed

+787
-75
lines changed

5 files changed

+787
-75
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ extension PackageGraph {
141141
)
142142

143143
let rootPackages = resolvedPackages.filter{ rootManifestSet.contains($0.manifest) }
144-
145144
checkAllDependenciesAreUsed(rootPackages, diagnostics)
146145

147146
return try PackageGraph(
@@ -414,37 +413,23 @@ private func createResolvedPackages(
414413
continue
415414
}
416415

417-
// If package name is mentioned, ensure it is valid.
418-
if let packageName = productRef.package {
419-
// Find the declared package and check that it contains
420-
// the product we found above.
421-
guard let dependencyPackage = packageMapByNameForTargetDependencyResolutionOnly[packageName], dependencyPackage.products.contains(product) else {
422-
let error = PackageGraphError.productDependencyIncorrectPackage(
423-
name: productRef.name,
424-
package: packageName
425-
)
426-
diagnostics.emit(error, location: diagnosticLocation())
427-
continue
428-
}
429-
} else if packageBuilder.package.manifest.toolsVersion >= .v5_2 {
430-
// Starting in 5.2, and target-based dependency, we require target product dependencies to
431-
// explicitly reference the package containing the product, or for the product, package and
432-
// dependency to share the same name. We don't check this in manifest loading for root-packages so
433-
// we can provide a more detailed diagnostic here.
416+
// Starting in 5.2, and target-based dependency, we require target product dependencies to
417+
// explicitly reference the package containing the product, or for the product, package and
418+
// dependency to share the same name. We don't check this in manifest loading for root-packages so
419+
// we can provide a more detailed diagnostic here.
420+
if packageBuilder.package.manifest.toolsVersion >= .v5_2 && productRef.package == nil{
434421
let referencedPackageIdentity = identityResolver.resolveIdentity(for: product.packageBuilder.package.manifest.packageLocation)
435-
guard let packageDependency = (packageBuilder.package.manifest.dependencies.first { package in
422+
guard let referencedPackageDependency = (packageBuilder.package.manifest.dependencies.first { package in
436423
return package.identity == referencedPackageIdentity
437424
}) else {
438425
throw InternalError("dependency reference for \(product.packageBuilder.package.manifest.packageLocation) not found")
439426
}
440-
441-
let packageName = product.packageBuilder.package.name
442-
if productRef.name != packageDependency.nameForTargetDependencyResolutionOnly || packageDependency.nameForTargetDependencyResolutionOnly != packageName {
427+
let referencedPackageName = referencedPackageDependency.nameForTargetDependencyResolutionOnly
428+
if productRef.name != referencedPackageName {
443429
let error = PackageGraphError.productDependencyMissingPackage(
444430
productName: productRef.name,
445431
targetName: targetBuilder.target.name,
446-
packageName: packageName,
447-
packageDependency: packageDependency
432+
packageDependency: referencedPackageDependency
448433
)
449434
diagnostics.emit(error, location: diagnosticLocation())
450435
}

Sources/PackageGraph/PackageGraph.swift

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ enum PackageGraphError: Swift.Error {
2121
/// The product dependency not found.
2222
case productDependencyNotFound(dependencyProductName: String, dependencyPackageName: String?, packageName: String, targetName: String)
2323

24-
/// The product dependency was found but the package name did not match.
25-
case productDependencyIncorrectPackage(name: String, package: String)
26-
2724
/// The package dependency name does not match the package name.
2825
case incorrectPackageDependencyName(dependencyPackageName: String, dependencyName: String, dependencyLocation: String, resolvedPackageName: String, resolvedPackageURL: String)
2926

@@ -37,7 +34,6 @@ enum PackageGraphError: Swift.Error {
3734
case productDependencyMissingPackage(
3835
productName: String,
3936
targetName: String,
40-
packageName: String,
4137
packageDependency: PackageDependencyDescription
4238
)
4339

@@ -173,9 +169,6 @@ extension PackageGraphError: CustomStringConvertible {
173169
case .productDependencyNotFound(let dependencyProductName, let dependencyPackageName, let packageName, let targetName):
174170
return "product '\(dependencyProductName)' \(dependencyPackageName.map{ "not found in package '\($0)'" } ?? "not found"). it is required by package '\(packageName)' target '\(targetName)'."
175171

176-
case .productDependencyIncorrectPackage(let name, let package):
177-
return "product dependency '\(name)' in package '\(package)' not found"
178-
179172
case .incorrectPackageDependencyName(let dependencyPackageName, let dependencyName, let dependencyURL, let resolvedPackageName, let resolvedPackageURL):
180173
return """
181174
'\(dependencyPackageName)' dependency on '\(dependencyURL)' has an explicit name '\(dependencyName)' which does not match the \
@@ -191,32 +184,14 @@ extension PackageGraphError: CustomStringConvertible {
191184
case .productDependencyMissingPackage(
192185
let productName,
193186
let targetName,
194-
let packageName,
195187
let packageDependency
196188
):
197189

198-
var solutionSteps: [String] = []
199-
200-
// If the package dependency name is the same as the package name, or if the product name and package name
201-
// don't correspond, we need to rewrite the target dependency to explicit specify the package name.
202-
if packageDependency.nameForTargetDependencyResolutionOnly == packageName || productName != packageName {
203-
solutionSteps.append("""
204-
reference the package in the target dependency with '.product(name: "\(productName)", package: \
205-
"\(packageName)")'
206-
""")
207-
}
208-
209-
// If the name of the product and the package are the same, or if the package dependency implicit name
210-
// deduced from the URL is not correct, we need to rewrite the package dependency declaration to specify the
211-
// package name.
212-
if productName == packageName || packageDependency.nameForTargetDependencyResolutionOnly != packageName {
213-
let dependencySwiftRepresentation = packageDependency.swiftRepresentation(overridingName: packageName)
214-
solutionSteps.append("""
215-
provide the name of the package dependency with '\(dependencySwiftRepresentation)'
216-
""")
217-
}
190+
let solution = """
191+
reference the package in the target dependency with '.product(name: "\(productName)", package: \
192+
"\(packageDependency.nameForTargetDependencyResolutionOnly)")'
193+
"""
218194

219-
let solution = solutionSteps.joined(separator: " and ")
220195
return "dependency '\(productName)' in target '\(targetName)' requires explicit declaration; \(solution)"
221196

222197
case .duplicateProduct(let product, let packages):

Sources/PackageModel/Manifest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public final class Manifest: ObjectIdentifierProtocol {
272272
return nil
273273
}
274274

275-
return dependencies.first(where: { $0.nameForTargetDependencyResolutionOnly == packageName })
275+
return self.dependencies.first(where: { $0.nameForTargetDependencyResolutionOnly == packageName })
276276
}
277277

278278
/// Registers a required product with a particular dependency if possible, or registers it as unknown.

0 commit comments

Comments
 (0)