Skip to content

Commit 17f6f21

Browse files
authored
Improve diagnostics when a product declared in a package is also used as a dependency (#3594)
* Resolves rdar://71912826 ([SR-13913]: Library product name must match target name??). * Checks if a target dependency includes a product declared in the same package, checks the targets of that product, and throws an error if invalid with improved diagnostics.
1 parent 0b0ac51 commit 17f6f21

File tree

3 files changed

+94
-5
lines changed

3 files changed

+94
-5
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,19 @@ private func createResolvedPackages(
397397
// found errors when there are more important errors to
398398
// resolve (like authentication issues).
399399
if !diagnostics.hasErrors {
400+
// Emit error if a product (not target) declared in the package is also a productRef (dependency)
401+
let declProductsAsDependency = package.products.filter { product in
402+
product.name == productRef.name
403+
}.map {$0.targets}.flatMap{$0}.filter { t in
404+
t.name != productRef.name
405+
}
406+
400407
let error = PackageGraphError.productDependencyNotFound(
401408
package: package.identity.description,
402409
targetName: targetBuilder.target.name,
403410
dependencyProductName: productRef.name,
404-
dependencyPackageName: productRef.package
411+
dependencyPackageName: productRef.package,
412+
dependencyProductInDecl: !declProductsAsDependency.isEmpty
405413
)
406414
diagnostics.emit(error, location: package.diagnosticLocation)
407415
}

Sources/PackageGraph/PackageGraph.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ enum PackageGraphError: Swift.Error {
1919
case cycleDetected((path: [Manifest], cycle: [Manifest]))
2020

2121
/// The product dependency not found.
22-
case productDependencyNotFound(package: String, targetName: String, dependencyProductName: String, dependencyPackageName: String?)
22+
case productDependencyNotFound(package: String, targetName: String, dependencyProductName: String, dependencyPackageName: String?, dependencyProductInDecl: Bool)
2323

2424
/// The package dependency name does not match the package name.
2525
case incorrectPackageDependencyName(package: String, dependencyName: String, dependencyLocation: String, resolvedPackageManifestName: String, resolvedPackageURL: String)
@@ -192,9 +192,12 @@ extension PackageGraphError: CustomStringConvertible {
192192
(cycle.path + cycle.cycle).map({ $0.name }).joined(separator: " -> ") +
193193
" -> " + cycle.cycle[0].name
194194

195-
case .productDependencyNotFound(let package, let targetName, let dependencyProductName, let dependencyPackageName):
196-
return "product '\(dependencyProductName)' required by package '\(package)' target '\(targetName)' \(dependencyPackageName.map{ "not found in package '\($0)'" } ?? "not found")."
197-
195+
case .productDependencyNotFound(let package, let targetName, let dependencyProductName, let dependencyPackageName, let dependencyProductInDecl):
196+
if dependencyProductInDecl {
197+
return "product '\(dependencyProductName)' is declared in the same package '\(package)' and can't be used as a dependency for target '\(targetName)'."
198+
} else {
199+
return "product '\(dependencyProductName)' required by package '\(package)' target '\(targetName)' \(dependencyPackageName.map{ "not found in package '\($0)'" } ?? "not found")."
200+
}
198201
case .incorrectPackageDependencyName(let package, let dependencyName, let dependencyURL, let resolvedPackageManifestName, let resolvedPackageURL):
199202
return """
200203
'\(package)' dependency on '\(dependencyURL)' has an explicit name '\(dependencyName)' which does not match the \

Tests/PackageGraphTests/PackageGraphTests.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,84 @@ class PackageGraphTests: XCTestCase {
573573
}
574574
}
575575

576+
func testProductDependencyDeclaredInSamePackage() throws {
577+
let fs = InMemoryFileSystem(emptyFiles:
578+
"/Foo/Sources/FooTarget/src.swift",
579+
"/Foo/Tests/FooTests/source.swift"
580+
)
581+
582+
let diagnostics = DiagnosticsEngine()
583+
_ = try loadPackageGraph(fs: fs, diagnostics: diagnostics,
584+
manifests: [
585+
Manifest.createV4Manifest(
586+
name: "Foo",
587+
path: "/Foo",
588+
packageKind: .root,
589+
packageLocation: "/Foo",
590+
products: [
591+
ProductDescription(name: "Foo", type: .library(.automatic), targets: ["FooTarget"]),
592+
],
593+
targets: [
594+
TargetDescription(name: "FooTarget", dependencies: []),
595+
TargetDescription(name: "FooTests", dependencies: ["Foo"], type: .test),
596+
]),
597+
]
598+
)
599+
600+
DiagnosticsEngineTester(diagnostics) { result in
601+
result.check(diagnostic: "product 'Foo' is declared in the same package 'foo' and can't be used as a dependency for target 'FooTests'.", behavior: .error, location: "'Foo' /Foo")
602+
}
603+
}
604+
605+
func testExecutableTargetDependency() throws {
606+
let fs = InMemoryFileSystem(emptyFiles:
607+
"/XYZ/Sources/XYZ/main.swift",
608+
"/XYZ/Tests/XYZTests/tests.swift"
609+
)
610+
let diagnostics = DiagnosticsEngine()
611+
_ = try loadPackageGraph(fs: fs,
612+
diagnostics: diagnostics,
613+
manifests: [
614+
Manifest.createV4Manifest(
615+
name: "XYZ",
616+
path: "/XYZ",
617+
packageKind: .root,
618+
packageLocation: "/XYZ",
619+
targets: [
620+
TargetDescription(name: "XYZ", dependencies: [], type: .executable),
621+
TargetDescription(name: "XYZTests", dependencies: ["XYZ"], type: .test),
622+
]),
623+
]
624+
)
625+
DiagnosticsEngineTester(diagnostics) { _ in }
626+
}
627+
628+
func testSameProductAndTargetNames() throws {
629+
let fs = InMemoryFileSystem(emptyFiles:
630+
"/Foo/Sources/Foo/src.swift",
631+
"/Foo/Tests/FooTests/source.swift"
632+
)
633+
634+
let diagnostics = DiagnosticsEngine()
635+
_ = try loadPackageGraph(fs: fs, diagnostics: diagnostics,
636+
manifests: [
637+
Manifest.createV4Manifest(
638+
name: "Foo",
639+
path: "/Foo",
640+
packageKind: .root,
641+
packageLocation: "/Foo",
642+
products: [
643+
ProductDescription(name: "Foo", type: .library(.automatic), targets: ["Foo"]),
644+
],
645+
targets: [
646+
TargetDescription(name: "Foo", dependencies: []),
647+
TargetDescription(name: "FooTests", dependencies: ["Foo"], type: .test),
648+
]),
649+
]
650+
)
651+
DiagnosticsEngineTester(diagnostics) { _ in }
652+
}
653+
576654
func testProductDependencyNotFoundWithName() throws {
577655
let fs = InMemoryFileSystem(emptyFiles:
578656
"/Foo/Sources/FooTarget/foo.swift"

0 commit comments

Comments
 (0)