Skip to content

Commit 29dfdd5

Browse files
authored
relax strickter validation to warning to allow transition (#3713)
motivation: recent change to SwiftPM introduced strickter validation of dependencies uniquness to address undefined behavior. however, we want to allow users some times to adjust before making a breaking change changes: update the validation to emit a warning instead of error under the specific condition the previous behavior incorrectly ignored!
1 parent 401e679 commit 29dfdd5

File tree

3 files changed

+96
-21
lines changed

3 files changed

+96
-21
lines changed

Sources/PackageGraph/PackageGraph+Loading.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ private func createResolvedPackages(
216216

217217
// Create a map of package builders keyed by the package identity.
218218
// This is guaranteed to be unique so we can use spm_createDictionary
219-
let packageMapByIdentity: [PackageIdentity: ResolvedPackageBuilder] = packageBuilders.spm_createDictionary{
219+
let packagesByIdentity: [PackageIdentity: ResolvedPackageBuilder] = packageBuilders.spm_createDictionary{
220220
return ($0.package.identity, $0)
221221
}
222222

@@ -242,7 +242,7 @@ private func createResolvedPackages(
242242
}
243243

244244
// Otherwise, look it up by its identity.
245-
if let resolvedPackage = packageMapByIdentity[dependency.identity] {
245+
if let resolvedPackage = packagesByIdentity[dependency.identity] {
246246
// check if this resolved package already listed in the dependencies
247247
// this means that the dependencies share the same identity
248248
// FIXME: this works but the way we find out about this is based on a side effect, need to improve it
@@ -255,16 +255,24 @@ private func createResolvedPackages(
255255
return diagnostics.emit(error, location: package.diagnosticLocation)
256256
}
257257

258-
// check if the resolved package url is the same as the dependency one
258+
// check if the resolved package location is the same as the dependency one
259259
// if not, this means that the dependencies share the same identity
260-
// FIXME: this works but the way we find out about this is based on a side effect, need to improve it
260+
// which only allowed when overriding
261261
if resolvedPackage.package.manifest.packageLocation != dependencyLocation && !resolvedPackage.allowedToOverride {
262262
let error = PackageGraphError.dependencyAlreadySatisfiedByIdentifier(
263263
package: package.identity.description,
264264
dependencyLocation: dependencyLocation,
265265
otherDependencyURL: resolvedPackage.package.manifest.packageLocation,
266266
identity: dependency.identity)
267-
return diagnostics.emit(error, location: package.diagnosticLocation)
267+
// 9/2021 this is currently emitting a warning only to support
268+
// backwards compatibility with older versions of SwiftPM that had too weak of a validation
269+
// we will upgrade this to an error in a few versions to tighten up the validation
270+
if dependency.explicitNameForTargetDependencyResolutionOnly == .none ||
271+
resolvedPackage.package.manifestName == dependency.explicitNameForTargetDependencyResolutionOnly {
272+
diagnostics.emit(.warning(error.description + ". this will be upgraded to an error in future versions of SwiftPM."), location: package.diagnosticLocation)
273+
} else {
274+
return diagnostics.emit(error, location: package.diagnosticLocation)
275+
}
268276
}
269277

270278
// checks if two dependencies have the same explicit name which can cause target based dependency package lookup issue

Sources/PackageModel/Manifest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public final class Manifest: ObjectIdentifierProtocol {
168168
/// Returns the package dependencies required for a particular products filter.
169169
public func dependenciesRequired(for productFilter: ProductFilter) -> [PackageDependency] {
170170
#if ENABLE_TARGET_BASED_DEPENDENCY_RESOLUTION
171-
// If we have already calcualted it, returned the cached value.
171+
// If we have already calculated it, returned the cached value.
172172
if let dependencies = self._requiredDependencies[productFilter] {
173173
return self.dependencies
174174
} else {

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5658,7 +5658,7 @@ final class WorkspaceTests: XCTestCase {
56585658
])
56595659
}
56605660

5661-
func testDuplicateDependencyIdentityAtRoot() throws {
5661+
func testDuplicateDependencyIdentityWithNameAtRoot() throws {
56625662
let sandbox = AbsolutePath("/tmp/ws/")
56635663
let fs = InMemoryFileSystem()
56645664

@@ -5720,6 +5720,68 @@ final class WorkspaceTests: XCTestCase {
57205720
}
57215721
}
57225722

5723+
func testDuplicateDependencyIdentityWithoutNameAtRoot() throws {
5724+
let sandbox = AbsolutePath("/tmp/ws/")
5725+
let fs = InMemoryFileSystem()
5726+
5727+
let workspace = try MockWorkspace(
5728+
sandbox: sandbox,
5729+
fs: fs,
5730+
roots: [
5731+
MockPackage(
5732+
name: "Root",
5733+
targets: [
5734+
MockTarget(name: "RootTarget", dependencies: [
5735+
.product(name: "FooProduct", package: "FooUtilityPackage"),
5736+
.product(name: "BarProduct", package: "BarUtilityPackage")
5737+
]),
5738+
],
5739+
products: [],
5740+
dependencies: [
5741+
.scm(path: "foo/utility", requirement: .upToNextMajor(from: "1.0.0")),
5742+
.scm(path: "bar/utility", requirement: .upToNextMajor(from: "1.0.0")),
5743+
],
5744+
toolsVersion: .v5
5745+
),
5746+
],
5747+
packages: [
5748+
MockPackage(
5749+
name: "FooUtilityPackage",
5750+
path: "foo/utility",
5751+
targets: [
5752+
MockTarget(name: "FooTarget"),
5753+
],
5754+
products: [
5755+
MockProduct(name: "FooProduct", targets: ["FooTarget"]),
5756+
],
5757+
versions: ["1.0.0", "2.0.0"]
5758+
),
5759+
// this package never gets loaded since the dependency declaration identity is the same as "FooPackage"
5760+
MockPackage(
5761+
name: "BarUtilityPackage",
5762+
path: "bar/utility",
5763+
targets: [
5764+
MockTarget(name: "BarTarget"),
5765+
],
5766+
products: [
5767+
MockProduct(name: "BarProduct", targets: ["BarTarget"]),
5768+
],
5769+
versions: ["1.0.0", "2.0.0"]
5770+
),
5771+
]
5772+
)
5773+
5774+
workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in
5775+
DiagnosticsEngineTester(diagnostics) { result in
5776+
result.check(
5777+
diagnostic: "'root' dependency on '/tmp/ws/pkgs/bar/utility' conflicts with dependency on '/tmp/ws/pkgs/foo/utility' which has the same identity 'utility'",
5778+
behavior: .error,
5779+
location: "'Root' /tmp/ws/roots/Root"
5780+
)
5781+
}
5782+
}
5783+
}
5784+
57235785
func testDuplicateExplicitDependencyName_AtRoot() throws {
57245786
let sandbox = AbsolutePath("/tmp/ws/")
57255787
let fs = InMemoryFileSystem()
@@ -6257,8 +6319,6 @@ final class WorkspaceTests: XCTestCase {
62576319
]
62586320
)
62596321

6260-
// FIXME: rdar://72940946
6261-
// we need to improve this situation or diagnostics when working on identity
62626322
workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in
62636323
DiagnosticsEngineTester(diagnostics) { result in
62646324
result.check(
@@ -6282,8 +6342,8 @@ final class WorkspaceTests: XCTestCase {
62826342
name: "Root",
62836343
targets: [
62846344
MockTarget(name: "RootTarget", dependencies: [
6285-
.product(name: "FooUtilityProduct", package: "FooUtilityPackage"),
6286-
.product(name: "BarProduct", package: "BarPackage")
6345+
.product(name: "FooUtilityProduct", package: "utility"),
6346+
.product(name: "BarProduct", package: "bar")
62876347
]),
62886348
],
62896349
products: [],
@@ -6311,7 +6371,7 @@ final class WorkspaceTests: XCTestCase {
63116371
path: "bar",
63126372
targets: [
63136373
MockTarget(name: "BarTarget", dependencies: [
6314-
.product(name: "OtherUtilityProduct", package: "OtherUtilityPackage"),
6374+
.product(name: "OtherUtilityProduct", package: "utility"),
63156375
]),
63166376
],
63176377
products: [
@@ -6337,12 +6397,19 @@ final class WorkspaceTests: XCTestCase {
63376397
]
63386398
)
63396399

6340-
// FIXME: rdar://72940946
6341-
// we need to improve this situation or diagnostics when working on identity
6400+
// 9/2021 this is currently emitting a warning only to support backwards compatibility
6401+
// we will upgrade this to an error in a few versions to tighten up the validation
63426402
workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in
63436403
DiagnosticsEngineTester(diagnostics) { result in
63446404
result.check(
6345-
diagnostic: "product 'OtherUtilityProduct' required by package 'bar' target 'BarTarget' not found in package 'OtherUtilityPackage'.",
6405+
diagnostic: "'bar' dependency on '/tmp/ws/pkgs/other-foo/utility' conflicts with dependency on '/tmp/ws/pkgs/foo/utility' which has the same identity 'utility'. this will be upgraded to an error in future versions of SwiftPM.",
6406+
behavior: .warning,
6407+
location: "'BarPackage' /tmp/ws/pkgs/bar"
6408+
)
6409+
// FIXME: rdar://72940946
6410+
// we need to improve this situation or diagnostics when working on identity
6411+
result.check(
6412+
diagnostic: "product 'OtherUtilityProduct' required by package 'bar' target 'BarTarget' not found in package 'utility'.",
63466413
behavior: .error,
63476414
location: "'BarPackage' /tmp/ws/pkgs/bar"
63486415
)
@@ -6420,10 +6487,10 @@ final class WorkspaceTests: XCTestCase {
64206487
]
64216488
)
64226489

6423-
// FIXME: rdar://72940946
6424-
// we need to improve this situation or diagnostics when working on identity
64256490
workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in
64266491
DiagnosticsEngineTester(diagnostics) { result in
6492+
// FIXME: rdar://72940946
6493+
// we need to improve this situation or diagnostics when working on identity
64276494
result.check(
64286495
diagnostic: "cyclic dependency declaration found: Root -> FooUtilityPackage -> BarPackage -> FooUtilityPackage",
64296496
behavior: .error,
@@ -6505,10 +6572,10 @@ final class WorkspaceTests: XCTestCase {
65056572
]
65066573
)
65076574

6508-
// FIXME: rdar://72940946
6509-
// we need to improve this situation or diagnostics when working on identity
65106575
workspace.checkPackageGraph(roots: ["Root"]) { graph, diagnostics in
65116576
DiagnosticsEngineTester(diagnostics) { result in
6577+
// FIXME: rdar://72940946
6578+
// we need to improve this situation or diagnostics when working on identity
65126579
result.check(
65136580
diagnostic: "cyclic dependency declaration found: Root -> FooUtilityPackage -> BarPackage -> FooUtilityPackage",
65146581
behavior: .error,
@@ -6573,10 +6640,10 @@ final class WorkspaceTests: XCTestCase {
65736640
]
65746641
)
65756642

6576-
// FIXME: rdar://72940946
6577-
// we need to improve this situation or diagnostics when working on identity
65786643
workspace.checkPackageGraph(roots: ["foo"]) { graph, diagnostics in
65796644
DiagnosticsEngineTester(diagnostics) { result in
6645+
// FIXME: rdar://72940946
6646+
// we need to improve this situation or diagnostics when working on identity
65806647
result.check(
65816648
diagnostic: "cyclic dependency declaration found: Root -> BarPackage -> Root",
65826649
behavior: .error,

0 commit comments

Comments
 (0)