Skip to content

Commit fe671d3

Browse files
committed
better diagnostics when potentially duplicate packages are found (swiftlang#6317)
motivation: with the introduction of the regsitry, there is a higher likelihood of running into duplicate packages changes: perform a heuristics to try and determin if the packages are duplicate and provide a better diagnostics instead of the standard "duplicate product" one
1 parent b803e3e commit fe671d3

File tree

5 files changed

+580
-189
lines changed

5 files changed

+580
-189
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ private func createResolvedPackages(
505505
throw InternalError("dependency reference for \(product.packageBuilder.package.manifest.packageLocation) not found")
506506
}
507507
let referencedPackageName = referencedPackageDependency.nameForTargetDependencyResolutionOnly
508-
if productRef.name != referencedPackageName {
508+
if productRef.name != referencedPackageName {
509509
let error = PackageGraphError.productDependencyMissingPackage(
510510
productName: productRef.name,
511511
targetName: targetBuilder.target.name,
@@ -522,16 +522,84 @@ private func createResolvedPackages(
522522

523523
// If a target with similar name was encountered before, we emit a diagnostic.
524524
if foundDuplicateTarget {
525+
var duplicateTargets = [String: [Package]]()
525526
for targetName in allTargetNames.sorted() {
526-
// Find the packages this target is present in.
527527
let packages = packageBuilders
528528
.filter({ $0.targets.contains(where: { $0.target.name == targetName }) })
529-
.map{ $0.package.identity.description }
530-
.sorted()
529+
.map{ $0.package }
531530
if packages.count > 1 {
532-
observabilityScope.emit(ModuleError.duplicateModule(targetName, packages))
531+
duplicateTargets[targetName, default: []].append(contentsOf: packages)
533532
}
534533
}
534+
535+
struct Pair: Hashable {
536+
let package1: Package
537+
let package2: Package
538+
539+
static func == (lhs: Pair, rhs: Pair) -> Bool {
540+
return lhs.package1.identity == rhs.package1.identity &&
541+
lhs.package2.identity == rhs.package2.identity
542+
}
543+
544+
public func hash(into hasher: inout Hasher) {
545+
hasher.combine(self.package1.identity)
546+
hasher.combine(self.package2.identity)
547+
}
548+
}
549+
550+
var potentiallyDuplicatePackages = [Pair: [String]]()
551+
for entry in duplicateTargets {
552+
// the duplicate is across exactly two packages
553+
if entry.value.count == 2 {
554+
potentiallyDuplicatePackages[Pair(package1: entry.value[0], package2: entry.value[1]), default: []].append(entry.key)
555+
}
556+
}
557+
558+
var duplicateTargetsAddressed = [String]()
559+
for potentiallyDuplicatePackage in potentiallyDuplicatePackages {
560+
// more than three target matches, or all targets in the package match
561+
if potentiallyDuplicatePackage.value.count > 3 ||
562+
(potentiallyDuplicatePackage.value.sorted() == potentiallyDuplicatePackage.key.package1.targets.map({ $0.name }).sorted()
563+
&&
564+
potentiallyDuplicatePackage.value.sorted() == potentiallyDuplicatePackage.key.package2.targets.map({ $0.name }).sorted())
565+
{
566+
switch (potentiallyDuplicatePackage.key.package1.identity.registry, potentiallyDuplicatePackage.key.package2.identity.registry) {
567+
case (.some(let registryIdentity), .none):
568+
observabilityScope.emit(
569+
ModuleError.duplicateModulesScmAndRegistry(
570+
regsitryPackage: registryIdentity,
571+
scmPackage: potentiallyDuplicatePackage.key.package2.identity,
572+
targets: potentiallyDuplicatePackage.value
573+
)
574+
)
575+
case (.none, .some(let registryIdentity)):
576+
observabilityScope.emit(
577+
ModuleError.duplicateModulesScmAndRegistry(
578+
regsitryPackage: registryIdentity,
579+
scmPackage: potentiallyDuplicatePackage.key.package1.identity,
580+
targets: potentiallyDuplicatePackage.value
581+
)
582+
)
583+
default:
584+
observabilityScope.emit(
585+
ModuleError.duplicateModules(
586+
package: potentiallyDuplicatePackage.key.package1.identity,
587+
otherPackage: potentiallyDuplicatePackage.key.package2.identity,
588+
targets: potentiallyDuplicatePackage.value
589+
)
590+
)
591+
}
592+
duplicateTargetsAddressed += potentiallyDuplicatePackage.value
593+
}
594+
}
595+
596+
for entry in duplicateTargets.filter({ !duplicateTargetsAddressed.contains($0.key) }) {
597+
observabilityScope.emit(
598+
ModuleError.duplicateModule(
599+
targetName: entry.key,
600+
packages: entry.value.map{ $0.identity })
601+
)
602+
}
535603
}
536604

537605
return try packageBuilders.map{ try $0.construct() }

0 commit comments

Comments
 (0)