Hive Hive
Sign in

Tuist requires products from a local path package’s own dependencies to be reconfigured in the root PackageSettings

GitHub issue · Open

Metadata
Source
tuist/tuist #11407
Updated
Jun 22, 2026
Domains
Generated projects
Details

Description

When a Tuist project depends on a local Swift package through .package(path:), tuist generate appears to validate external products used by the local package’s test targets, even when the generated project target only depends on a runtime library product from that package.

This means the root Tuist/Package.swift must configure external products that are not part of the dependency closure of the generated app/framework target. More importantly, those external packages are already declared by the local package’s own Package.swift, so the root Tuist package has to repeat dependency information that the local package already owns.

In our real project, adding a local Compute package made tuist generate fail until we added swift-algorithms and Semaphore to the root Tuist/Package.swift, even though those products are only used by Compute test targets.

Minimal Reproduction

I created a standalone fixture under DemoKit/.

Structure:

DemoKit/
Project.swift
Tuist/Package.swift
Sources/main.swift
LocalOnlyPackage/
Package.swift
Sources/DemoRuntime/DemoRuntime.swift
Tests/DemoRuntimeAlgorithmsTests/DemoRuntimeAlgorithmsTests.swift
Tests/DemoRuntimeSemaphoreTests/DemoRuntimeSemaphoreTests.swift

DemoKit/Project.swift has one target:

.target(
name: "DemoApp",
destinations: [.mac],
product: .commandLineTool,
bundleId: "org.openswiftuiproject.openswiftui.DemoKit",
deploymentTargets: .macOS("15.0"),
sources: ["Sources/**"],
dependencies: [
.external(name: "DemoRuntime"),
]
)

DemoKit/Tuist/Package.swift only declares the local package dependency and configures the runtime product:

let package = Package(
name: "DemoKitDependencies",
dependencies: [
.package(path: "../LocalOnlyPackage"),
]
)
#if TUIST
let packageSettings = PackageSettings(
productTypes: [
"DemoRuntime": .staticFramework,
],
baseSettings: .settings(defaultSettings: .none)
)
#endif

LocalOnlyPackage/Package.swift already declares both external package URLs:

dependencies: [
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
.package(url: "https://github.com/groue/Semaphore", from: "0.1.0"),
],

The local package’s runtime library target has no dependency on either product:

targets: [
.target(
name: "DemoRuntime"
),
]

The important part is that LocalOnlyPackage/Package.swift already declares both package URLs. They are local package dependencies, not unknown packages.

Algorithms and Semaphore are used only by local package test targets:

.target(
name: "DemoRuntime"
),
.testTarget(
name: "DemoRuntimeAlgorithmsTests",
dependencies: [
"DemoRuntime",
.product(name: "Algorithms", package: "swift-algorithms"),
]
),
.testTarget(
name: "DemoRuntimeSemaphoreTests",
dependencies: [
"DemoRuntime",
.product(name: "Semaphore", package: "Semaphore"),
]
)

Steps To Reproduce

From DemoKit/:

mise trust
mise install
mise exec -- tuist install
mise exec -- tuist generate --no-open

Actual Behavior

tuist generate fails unless the root Tuist/Package.swift also configures these external products.

The first failure is:

`Algorithms` is not a valid configured external dependency

If Algorithms is added, generation then requires Semaphore as well.

Expected Behavior

tuist generate should not require the root project’s Tuist/Package.swift to re-declare package URLs that are already declared by the local path package’s own Package.swift.

I understand that test-only dependencies can be a tricky area, and SwiftPM itself has not always modeled or pruned them in the way users might intuitively expect. So the test-only aspect may not be the strongest argument by itself.

The more surprising part is the duplication requirement:

  • LocalOnlyPackage/Package.swift already declares swift-algorithms and Semaphore with their package URLs.
  • Tuist already resolves and inspects the local path package.
  • The root DemoKit/Tuist/Package.swift still has to add those same package URLs again.
  • The root PackageSettings.productTypes also has to configure Algorithms and Semaphore, even though DemoApp only references .external(name: "DemoRuntime").
  • This leaks the local package’s internal dependency details into the final Tuist project configuration.

Workaround

Add the local package’s external dependencies again to the root Tuist/Package.swift:

let package = Package(
name: "DemoKitDependencies",
dependencies: [
.package(path: "../LocalOnlyPackage"),
.package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"),
.package(url: "https://github.com/groue/Semaphore", from: "0.1.0"),
]
)
#if TUIST
let packageSettings = PackageSettings(
productTypes: [
"DemoRuntime": .staticFramework,
"Algorithms": .staticFramework,
"Semaphore": .staticFramework,
],
baseSettings: .settings(defaultSettings: .none)
)
#endif

This works, but it duplicates dependency declarations from the local path package and leaks its implementation/test-only details into the root Tuist project.

Environment

  • Tuist: reproduced with 4.193.0 and 4.200.5
  • Swift: Apple Swift 6.2.4
  • Xcode: 26.3 (17C529)
  • macOS: 15.7.7 (24G720)
  • Architecture: arm64

Attachment

DemoKit-tuist-local-path-deps-repro.zip

Comments

No GitHub comments yet.