Hive
Tuist requires products from a local path package’s own dependencies to be reconfigured in the root PackageSettings
GitHub issue · Open
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.swiftalready declaresswift-algorithmsandSemaphorewith their package URLs.- Tuist already resolves and inspects the local path package.
- The root
DemoKit/Tuist/Package.swiftstill has to add those same package URLs again. - The root
PackageSettings.productTypesalso has to configureAlgorithmsandSemaphore, even thoughDemoApponly 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
No GitHub comments yet.