Skip to content

Commit cc30527

Browse files
authored
Enable use of product ID to distinguish duplicate product names (#5990)
Resolves rdar://102138022
1 parent 1c167ff commit cc30527

File tree

5 files changed

+262
-131
lines changed

5 files changed

+262
-131
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -522,18 +522,20 @@ private class DuplicateProductsChecker {
522522
init(packageBuilders: [ResolvedPackageBuilder]) {
523523
for packageBuilder in packageBuilders {
524524
let pkgID = packageBuilder.package.identity.description.lowercased()
525-
packageIDToBuilder[pkgID] = packageBuilder
525+
self.packageIDToBuilder[pkgID] = packageBuilder
526526
}
527527
}
528528

529529
func run(lookupByProductIDs: Bool = false, observabilityScope: ObservabilityScope) throws {
530530
var productToPkgMap = [String: [String]]()
531-
for (_, pkgBuilder) in packageIDToBuilder {
531+
for (pkgID, pkgBuilder) in packageIDToBuilder {
532532
let useProductIDs = pkgBuilder.package.manifest.disambiguateByProductIDs || lookupByProductIDs
533533
let depProductRefs = pkgBuilder.package.targets.map{$0.dependencies}.flatMap{$0}.compactMap{$0.product}
534534
for depRef in depProductRefs {
535535
if let depPkg = depRef.package?.lowercased() {
536-
checkedPkgIDs.append(depPkg)
536+
if !checkedPkgIDs.contains(depPkg) {
537+
checkedPkgIDs.append(depPkg)
538+
}
537539
let depProductIDs = packageIDToBuilder[depPkg]?.package.products.filter { $0.identity == depRef.identity }.map { useProductIDs && $0.isDefaultLibrary ? $0.identity : $0.name } ?? []
538540
for depID in depProductIDs {
539541
productToPkgMap[depID, default: []].append(depPkg)
@@ -543,18 +545,30 @@ private class DuplicateProductsChecker {
543545
productToPkgMap[depRef.name, default: []].append(contentsOf: depPkgs)
544546
checkedPkgIDs.append(contentsOf: depPkgs)
545547
}
548+
if !checkedPkgIDs.contains(pkgID.lowercased()) {
549+
checkedPkgIDs.append(pkgID.lowercased())
550+
}
546551
}
547552
for (depIDOrName, depPkgs) in productToPkgMap.filter({Set($0.value).count > 1}) {
548553
let name = depIDOrName.components(separatedBy: "_").dropFirst().joined(separator: "_")
549554
throw PackageGraphError.duplicateProduct(product: name.isEmpty ? depIDOrName : name, packages: depPkgs.sorted())
550555
}
551556
}
552557

553-
let uncheckedPkgs = packageIDToBuilder.filter{!checkedPkgIDs.contains($0.key)}
554-
for (pkgID, pkgBuilder) in uncheckedPkgs {
555-
let productIDOrNames = pkgBuilder.products.map { pkgBuilder.package.manifest.disambiguateByProductIDs && $0.product.isDefaultLibrary ? $0.product.identity : $0.product.name }
556-
for productIDOrName in productIDOrNames {
557-
productToPkgMap[productIDOrName, default: []].append(pkgID)
558+
// Check packages that exist but are not in a dependency graph
559+
let untrackedPkgs = packageIDToBuilder.filter{!checkedPkgIDs.contains($0.key.lowercased())}
560+
for (pkgID, pkgBuilder) in untrackedPkgs {
561+
for product in pkgBuilder.products {
562+
// Check if checking product ID only is safe
563+
let useIDOnly = lookupByProductIDs && product.product.isDefaultLibrary
564+
if !useIDOnly {
565+
// This untracked pkg could have a product name conflicting with a
566+
// product name from another package, but since it's not depended on
567+
// by other packages, keep track of both this product's name and ID
568+
// just in case other packages are < .v5_8
569+
productToPkgMap[product.product.name, default: []].append(pkgID)
570+
}
571+
productToPkgMap[product.product.identity, default: []].append(pkgID)
558572
}
559573
}
560574

Sources/PackageModel/PackageModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ extension Package.Error: CustomStringConvertible {
124124

125125
extension Manifest {
126126
public var disambiguateByProductIDs: Bool {
127-
return self.toolsVersion >= .vNext
127+
return self.toolsVersion >= .v5_8
128128
}
129129
}
130130

Sources/PackageModel/Target.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,12 @@ public class Target: PolymorphicCodableProtocol {
5454
public var identity: String {
5555
if let pkg = package {
5656
return pkg.lowercased() + "_" + name
57+
} else {
58+
// this is hit only if this product is referenced `.byName(name)`
59+
// which assumes the name of this product, its package, and its target
60+
// all have the same name
61+
return name.lowercased() + "_" + name
5762
}
58-
// package ID won't be included for products referenced only by name
59-
return name
6063
}
6164

6265
/// Creates a product reference instance.

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ final class BuildPlanTests: XCTestCase {
122122
Manifest.createRootManifest(
123123
name: "thisPkg",
124124
path: .init(path: "/thisPkg"),
125-
toolsVersion: .vNext,
125+
toolsVersion: .v5_8,
126126
dependencies: [
127127
.localSourceControl(path: .init(path: "/fooPkg"), requirement: .upToNextMajor(from: "1.0.0")),
128128
.localSourceControl(path: .init(path: "/barPkg"), requirement: .upToNextMajor(from: "2.0.0")),
@@ -188,7 +188,7 @@ final class BuildPlanTests: XCTestCase {
188188
Manifest.createFileSystemManifest(
189189
name: "fooPkg",
190190
path: .init(path: "/fooPkg"),
191-
toolsVersion: .vNext,
191+
toolsVersion: .v5_8,
192192
dependencies: [
193193
.localSourceControl(path: .init(path: "/barPkg"), requirement: .upToNextMajor(from: "1.0.0")),
194194
.localSourceControl(path: .init(path: "/bazPkg"), requirement: .upToNextMajor(from: "1.0.0")),
@@ -227,7 +227,7 @@ final class BuildPlanTests: XCTestCase {
227227
Manifest.createRootManifest(
228228
name: "thisPkg",
229229
path: .init(path: "/thisPkg"),
230-
toolsVersion: .vNext,
230+
toolsVersion: .v5_8,
231231
dependencies: [
232232
.localSourceControl(path: .init(path: "/xPkg"), requirement: .upToNextMajor(from: "1.0.0")),
233233
.localSourceControl(path: .init(path: "/yPkg"), requirement: .upToNextMajor(from: "1.0.0")),
@@ -298,7 +298,7 @@ final class BuildPlanTests: XCTestCase {
298298
Manifest.createFileSystemManifest(
299299
name: "fooPkg",
300300
path: .init(path: "/fooPkg"),
301-
toolsVersion: .vNext,
301+
toolsVersion: .v5_8,
302302
dependencies: [
303303
.localSourceControl(path: .init(path: "/barPkg"), requirement: .upToNextMajor(from: "1.0.0")),
304304
.localSourceControl(path: .init(path: "/bazPkg"), requirement: .upToNextMajor(from: "1.0.0")),
@@ -360,7 +360,7 @@ final class BuildPlanTests: XCTestCase {
360360
Manifest.createFileSystemManifest(
361361
name: "fooPkg",
362362
path: .init(path: "/fooPkg"),
363-
toolsVersion: .vNext,
363+
toolsVersion: .v5_8,
364364
dependencies: [
365365
.localSourceControl(path: .init(path: "/barPkg"), requirement: .upToNextMajor(from: "1.0.0")),
366366
],
@@ -503,7 +503,7 @@ final class BuildPlanTests: XCTestCase {
503503
Manifest.createRootManifest(
504504
name: "thisPkg",
505505
path: .init(path: "/thisPkg"),
506-
toolsVersion: .vNext,
506+
toolsVersion: .v5_8,
507507
dependencies: [
508508
.localSourceControl(path: .init(path: "/fooPkg"), requirement: .upToNextMajor(from: "1.0.0")),
509509
.localSourceControl(path: .init(path: "/barPkg"), requirement: .upToNextMajor(from: "2.0.0")),

0 commit comments

Comments
 (0)