Skip to content

Commit 0401a2a

Browse files
Fix resolve failing when package from registry is referenced by name (#8166)
### Motivation: When running `swift package --replace-scm-with-registry --default-registry-url {our-registry-url} resolve`, the command fails when depending on packages that reference their transitive dependencies by name. I created a [demo](https://github.com/fortmarek/spm-registry-by-name-references) where you can reproduce the issue by running `swift package --replace-scm-with-registry --default-registry-url {our-registry-url} resolve`. You should see the following error: ``` error: 'cpisciotta.xcbeautify': unknown dependency 'Colorizer' in target 'XcbeautifyLib'; valid dependencies are: 'swift-argument-parser' (from 'https://github.com/apple/swift-argument-parser.git'), 'getGuaka.Colorizer', 'MaxDesiatov.XMLCoder' error: 'cpisciotta.xcbeautify': unknown dependency 'XMLCoder' in target 'XcbeautifyLib'; valid dependencies are: 'swift-argument-parser' (from 'https://github.com/apple/swift-argument-parser.git'), 'getGuaka.Colorizer', 'MaxDesiatov.XMLCoder' ``` The issue boils down to: ```swift // Root Package.swift import PackageDescription let package = Package( name: "package-test", dependencies: [ // We're depending on `xcbeautify` from our root `Package.swift` .package(url: "https://github.com/cpisciotta/xcbeautify", exact: "2.13.0") ], targets: [ .target(name: "package-test"), ] ) // Package.swift at https://github.com/cpisciotta/xcbeautify/blob/main/Package.swift import PackageDescription let package = Package( name: "xcbeautify", products: [ .library(name: "XcbeautifyLib", targets: ["XcbeautifyLib"]), ], dependencies: [ .package( url: "https://github.com/getGuaka/Colorizer.git", .upToNextMinor(from: "0.2.1") ), .package( url: "https://github.com/MaxDesiatov/XMLCoder.git", .upToNextMinor(from: "0.17.1") ), ], targets: [ .target( name: "XcbeautifyLib", dependencies: [ // Transitive package products referenced by name "Colorizer", "XMLCoder", ] ), ] ) ``` The issue is that when swizzling the package product names to their identity counterparts, only dependencies referenced via `.product(...)` are swizzled. Indeed, when we change the `XcbeautifyLib`'s dependencies to: ```swift .product(name: "Colorizer", package: "Colorizer"), .product(name: "XMLCoder", package: "XMLCoder"), ``` the resolution succeeds. ### Modifications: To fix the above issue, we swizzle also dependencies referenced by name _iff_ the product name is the same as the package name the root depends on. This aligns with how `byName` is treated in other parts of the codebase, such as [here](https://github.com/swiftlang/swift-package-manager/blob/main/Sources/PackageModel/Manifest/Manifest.swift#L407). ### Result: `swift package --replace-scm-with-registry --default-registry-url {our-registry-url} resolve` succeeds when run in the [repro sample](https://github.com/fortmarek/spm-registry-by-name-references). --------- Co-authored-by: Max Desiatov <[email protected]>
1 parent adf64d4 commit 0401a2a

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

Sources/Workspace/Workspace+Registry.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,35 @@ extension Workspace {
257257
var modifiedDependencies = [TargetDescription.Dependency]()
258258
for dependency in target.dependencies {
259259
var modifiedDependency = dependency
260-
if case .product(let name, let packageName, let moduleAliases, let condition) = dependency,
261-
let packageName
262-
{
263-
// makes sure we use the updated package name for target based dependencies
264-
if let modifiedPackageName = targetDependencyPackageNameTransformations[packageName] {
260+
switch dependency {
261+
case .product(
262+
name: let name,
263+
package: let packageName,
264+
moduleAliases: let moduleAliases,
265+
condition: let condition
266+
):
267+
if let packageName,
268+
// makes sure we use the updated package name for target based dependencies
269+
let modifiedPackageName = targetDependencyPackageNameTransformations[packageName]
270+
{
265271
modifiedDependency = .product(
266272
name: name,
267273
package: modifiedPackageName,
268274
moduleAliases: moduleAliases,
269275
condition: condition
270276
)
271277
}
278+
case .byName(name: let packageName, condition: let condition):
279+
if let modifiedPackageName = targetDependencyPackageNameTransformations[packageName] {
280+
modifiedDependency = .product(
281+
name: packageName,
282+
package: modifiedPackageName,
283+
moduleAliases: [:],
284+
condition: condition
285+
)
286+
}
287+
case .target:
288+
break
272289
}
273290
modifiedDependencies.append(modifiedDependency)
274291
}

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12869,6 +12869,85 @@ final class WorkspaceTests: XCTestCase {
1286912869
["did load manifest for registry package: org.baz (identity: org.baz)"]
1287012870
)
1287112871
}
12872+
12873+
func testTransitiveResolutionFromRegistryWithByNameDependencies() async throws {
12874+
let sandbox = AbsolutePath("/tmp/ws/")
12875+
let fs = InMemoryFileSystem()
12876+
12877+
let workspace = try await MockWorkspace(
12878+
sandbox: sandbox,
12879+
fileSystem: fs,
12880+
roots: [
12881+
MockPackage(
12882+
name: "Root",
12883+
path: "root",
12884+
targets: [
12885+
MockTarget(name: "RootTarget", dependencies: [
12886+
.product(name: "FooProduct", package: "foo"),
12887+
]),
12888+
],
12889+
products: [],
12890+
dependencies: [
12891+
.sourceControl(url: "https://git/org/foo", requirement: .upToNextMajor(from: "1.0.0")),
12892+
],
12893+
toolsVersion: .v5_6
12894+
),
12895+
],
12896+
packages: [
12897+
MockPackage(
12898+
name: "Bar",
12899+
identity: "org.bar",
12900+
alternativeURLs: ["https://git/org/Bar"],
12901+
targets: [
12902+
MockTarget(name: "BarTarget"),
12903+
],
12904+
products: [
12905+
MockProduct(name: "Bar", modules: ["BarTarget"]),
12906+
],
12907+
versions: ["1.0.0", "1.1.0"]
12908+
),
12909+
MockPackage(
12910+
name: "FooPackage",
12911+
identity: "org.foo",
12912+
alternativeURLs: ["https://git/org/foo"],
12913+
targets: [
12914+
MockTarget(name: "FooTarget", dependencies: [
12915+
"Bar",
12916+
]),
12917+
],
12918+
products: [
12919+
MockProduct(name: "FooProduct", modules: ["FooTarget"]),
12920+
],
12921+
dependencies: [
12922+
.sourceControl(url: "https://git/org/Bar", requirement: .upToNextMajor(from: "1.0.0")),
12923+
],
12924+
versions: ["1.0.0", "1.1.0", "1.2.0"]
12925+
),
12926+
]
12927+
)
12928+
12929+
workspace.sourceControlToRegistryDependencyTransformation = .swizzle
12930+
12931+
try await workspace.checkPackageGraph(roots: ["root"]) { graph, diagnostics in
12932+
XCTAssertNoDiagnostics(diagnostics)
12933+
PackageGraphTester(graph) { result in
12934+
result.check(roots: "Root")
12935+
result.check(packages: "org.bar", "org.foo", "Root")
12936+
result.check(modules: "FooTarget", "BarTarget", "RootTarget")
12937+
result.checkTarget("RootTarget") { result in
12938+
result.check(dependencies: "FooProduct")
12939+
}
12940+
result.checkTarget("FooTarget") { result in
12941+
result.check(dependencies: "Bar")
12942+
}
12943+
}
12944+
}
12945+
12946+
workspace.checkManagedDependencies { result in
12947+
result.check(dependency: "org.foo", at: .registryDownload("1.2.0"))
12948+
result.check(dependency: "org.bar", at: .registryDownload("1.1.0"))
12949+
}
12950+
}
1287212951

1287312952
// no dups
1287412953
func testResolutionMixedRegistryAndSourceControl1() async throws {

0 commit comments

Comments
 (0)