Hive Hive
Sign in

A target dependency with a non-default LinkingStatus drops its transitive frameworks from the App’s build phases

GitHub issue · Open

Metadata
Source
tuist/tuist #12287
Updated
Aug 12, 2026
Domains
Generated projects
Details

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 AppMyFramework (non-default status) → DeepFramework.

MyFramework is a .framework:

status Frameworks (link) Embed Frameworks
.required MyFramework.framework DeepFramework.framework, MyFramework.framework
.none (empty) MyFramework.frameworkDeepFramework missing
.optional MyFramework.framework (weak) MyFramework.frameworkDeepFramework 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.optional reproduces 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?

  1. Create a project with three targets, where the dependency carrying the non-default status has a dependency of its own:

    // Project.swift
    import ProjectDescription
    let 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.swift
    import ProjectDescription
    let tuist = Tuist(project: .tuist())
    for t in App MyFramework DeepFramework; do
    mkdir -p "$t/Sources"
    echo "public struct $t {}" > "$t/Sources/$t.swift"
    done
  2. Run tuist generate.

  3. Open NoneStatusTransitive.xcodeproj, select the App target → Build PhasesEmbed Frameworks. Only MyFramework.framework is listed; DeepFramework.framework is missing.

  4. Change status: .none to status: .required, run tuist generate again and look at the same phase. Both DeepFramework.framework and MyFramework.framework are 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)

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.