Hive
A target dependency with a non-default LinkingStatus drops its transitive frameworks from the App’s build phases
GitHub issue · Open
What happened?
Setting status: to anything other than the default .required on a .target or .project dependency removes every transitive dependency behind that edge from the depending target’s Embed Frameworks and Frameworks build phases.
status is documented as controlling how that one dependency is linked (.optional → weakly linked, .none → skip linking), so I expect it to change only that, and to leave everything reachable behind the dependency embedded and linked exactly as with .required. Instead the whole subtree disappears, and tuist generate reports no warning or error.
In the example below App → MyFramework (non-default status) → DeepFramework.
MyFramework is a .framework:
status |
Frameworks (link) | Embed Frameworks |
|---|---|---|
.required |
MyFramework.framework |
DeepFramework.framework, MyFramework.framework |
.none |
(empty) | MyFramework.framework — DeepFramework missing |
.optional |
MyFramework.framework (weak) |
MyFramework.framework — DeepFramework missing |
MyFramework is a .staticFramework:
status |
Frameworks (link) | Embed Frameworks |
|---|---|---|
.required |
DeepFramework.framework, MyFramework.framework |
DeepFramework.framework |
.none |
(empty) — DeepFramework missing |
(empty) — DeepFramework missing |
In every row I expect DeepFramework to appear exactly as it does for .required; only MyFramework’s own linking should differ. Because a .framework product cannot embed frameworks itself, the app is the only target that can embed DeepFramework, so it is absent from the built app bundle entirely.
Two notes that may help triage:
- This is not specific to
.none—.optionalreproduces it identically, so any non-default status triggers it. - It also reproduces with
.project(target:path:status:)across two projects in a workspace.
Possibly related: #8651, #6751, #6838, #7273, #7879, #10753.
Workaround: re-declare the transitive dependency directly on the consuming target with the same status. Not available for SPM dependencies, since .external(name:) accepts no status parameter.
dependencies: [
.target(name: "MyFramework", status: .none),
.target(name: "DeepFramework", status: .none),
]
How do we reproduce it?
-
Create a project with three targets, where the dependency carrying the non-default status has a dependency of its own:
// Project.swiftimport ProjectDescriptionlet project = Project(name: "NoneStatusTransitive",targets: [.target(name: "App",destinations: .iOS,product: .app,bundleId: "dev.tuist.App",infoPlist: .default,sources: ["App/Sources/**"],dependencies: [.target(name: "MyFramework", status: .none)]),.target(name: "MyFramework",destinations: .iOS,product: .framework,bundleId: "dev.tuist.MyFramework",sources: ["MyFramework/Sources/**"],dependencies: [.target(name: "DeepFramework")]),.target(name: "DeepFramework",destinations: .iOS,product: .framework,bundleId: "dev.tuist.DeepFramework",sources: ["DeepFramework/Sources/**"],dependencies: []),])// Tuist.swiftimport ProjectDescriptionlet tuist = Tuist(project: .tuist())for t in App MyFramework DeepFramework; domkdir -p "$t/Sources"echo "public struct $t {}" > "$t/Sources/$t.swift"done -
Run
tuist generate. -
Open
NoneStatusTransitive.xcodeproj, select theApptarget → Build Phases → Embed Frameworks. OnlyMyFramework.frameworkis listed;DeepFramework.frameworkis missing. -
Change
status: .nonetostatus: .required, runtuist generateagain and look at the same phase. BothDeepFramework.frameworkandMyFramework.frameworkare now listed.
Replacing .none with .optional in step 1 gives the same missing framework. Changing MyFramework to product: .staticFramework and using .none leaves both of the App’s phases empty.
Error log
None — and that is part of the problem. tuist generate succeeds with no error or warning; the missing frameworks are only visible in the generated project.pbxproj (see the tables above).
macOS version
26.6 (Apple Silicon)
Tuist version
4.204.0-canary.10 — also reproduces on 4.203.4 (latest stable), on 4.191.0, and on main @ 270f877
Xcode version
26.5 (17F42)
Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.
Start a Flight and preserve its objective, outcome, and session here.
No GitHub comments yet.