Skip to content

better diagnostics when potentially duplicate packages are found #6317

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

Merged
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
78 changes: 73 additions & 5 deletions Sources/PackageGraph/PackageGraph+Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ private func createResolvedPackages(
throw InternalError("dependency reference for \(product.packageBuilder.package.manifest.packageLocation) not found")
}
let referencedPackageName = referencedPackageDependency.nameForTargetDependencyResolutionOnly
if productRef.name != referencedPackageName {
if productRef.name != referencedPackageName {
let error = PackageGraphError.productDependencyMissingPackage(
productName: productRef.name,
targetName: targetBuilder.target.name,
Expand All @@ -522,16 +522,84 @@ private func createResolvedPackages(

// If a target with similar name was encountered before, we emit a diagnostic.
if foundDuplicateTarget {
var duplicateTargets = [String: [Package]]()
for targetName in allTargetNames.sorted() {
// Find the packages this target is present in.
let packages = packageBuilders
.filter({ $0.targets.contains(where: { $0.target.name == targetName }) })
.map{ $0.package.identity.description }
.sorted()
.map{ $0.package }
if packages.count > 1 {
observabilityScope.emit(ModuleError.duplicateModule(targetName, packages))
duplicateTargets[targetName, default: []].append(contentsOf: packages)
}
}

struct Pair: Hashable {
let package1: Package
let package2: Package

static func == (lhs: Pair, rhs: Pair) -> Bool {
return lhs.package1.identity == rhs.package1.identity &&
lhs.package2.identity == rhs.package2.identity
}

public func hash(into hasher: inout Hasher) {
hasher.combine(self.package1.identity)
hasher.combine(self.package2.identity)
}
}

var potentiallyDuplicatePackages = [Pair: [String]]()
for entry in duplicateTargets {
// the duplicate is across exactly two packages
if entry.value.count == 2 {
potentiallyDuplicatePackages[Pair(package1: entry.value[0], package2: entry.value[1]), default: []].append(entry.key)
}
}

var duplicateTargetsAddressed = [String]()
for potentiallyDuplicatePackage in potentiallyDuplicatePackages {
// more than three target matches, or all targets in the package match
if potentiallyDuplicatePackage.value.count > 3 ||
(potentiallyDuplicatePackage.value.sorted() == potentiallyDuplicatePackage.key.package1.targets.map({ $0.name }).sorted()
&&
potentiallyDuplicatePackage.value.sorted() == potentiallyDuplicatePackage.key.package2.targets.map({ $0.name }).sorted())
{
switch (potentiallyDuplicatePackage.key.package1.identity.registry, potentiallyDuplicatePackage.key.package2.identity.registry) {
case (.some(let registryIdentity), .none):
observabilityScope.emit(
ModuleError.duplicateModulesScmAndRegistry(
regsitryPackage: registryIdentity,
scmPackage: potentiallyDuplicatePackage.key.package2.identity,
targets: potentiallyDuplicatePackage.value
)
)
case (.none, .some(let registryIdentity)):
observabilityScope.emit(
ModuleError.duplicateModulesScmAndRegistry(
regsitryPackage: registryIdentity,
scmPackage: potentiallyDuplicatePackage.key.package1.identity,
targets: potentiallyDuplicatePackage.value
)
)
default:
observabilityScope.emit(
ModuleError.duplicateModules(
package: potentiallyDuplicatePackage.key.package1.identity,
otherPackage: potentiallyDuplicatePackage.key.package2.identity,
targets: potentiallyDuplicatePackage.value
)
)
}
duplicateTargetsAddressed += potentiallyDuplicatePackage.value
}
}

for entry in duplicateTargets.filter({ !duplicateTargetsAddressed.contains($0.key) }) {
observabilityScope.emit(
ModuleError.duplicateModule(
targetName: entry.key,
packages: entry.value.map{ $0.identity })
)
}
}

return try packageBuilders.map{ try $0.construct() }
Expand Down
Loading