Hive Hive
Sign in

SwiftPM systemLibrary target generates an invalid macOS framework with a top-level Modules directory

GitHub issue · Open

Metadata
Source
tuist/tuist #12040
Updated
Jul 23, 2026
Domains
Generated projects
Details

What happened?

Tuist materializes GRDB’s SwiftPM GRDBSQLite systemLibrary target as a macOS framework. The generated target’s Copy Module Map build script creates a real directory at:

GRDBSQLite.framework/Modules/module.modulemap

This is not a valid layout for a versioned macOS framework. Its root should contain Versions plus symlinks to versioned content. As a result, strict code signature verification consistently fails:

GRDBSQLite.framework: unsealed contents present in the root directory of an embedded framework

This also affects real projects using Xcode mergeable libraries. With MERGED_BINARY_TYPE=automatic in Debug, Xcode may copy the generated framework into App.app/Contents/ReexportedBinaries. If the top-level Modules directory is retained, app signing fails with the same error.

Expected behavior: Tuist should either preserve the SwiftPM system-library semantics without materializing it as a framework, or generate a valid macOS framework layout, for example with the module map under Versions/A/Modules and a root Modules symlink.

How can we reproduce it?

A minimal reproduction is available here:

https://github.com/RbBtSn0w/tuist-mergeable-system-library-repro

git clone https://github.com/RbBtSn0w/tuist-mergeable-system-library-repro.git
cd tuist-mergeable-system-library-repro
tuist install
tuist generate --no-open
xcodebuild build \
-workspace MergeableSystemLibraryRepro.xcworkspace \
-scheme ReproApp \
-configuration Debug \
-destination 'platform=macOS,arch=arm64' \
-derivedDataPath /tmp/tuist-mergeable-system-library-repro \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY=- \
DEVELOPMENT_TEAM=
codesign --verify --deep --strict --verbose=4 \
/tmp/tuist-mergeable-system-library-repro/Build/Products/Debug/GRDBSQLite.framework

The final command fails consistently.

The generated GRDBSQLite target contains a Copy Module Map script equivalent to:

mkdir -p "$TARGET_BUILD_DIR/$WRAPPER_NAME/Modules"
cp /path/to/GRDB.swift/Sources/GRDBSQLite/module.modulemap \
"$TARGET_BUILD_DIR/$WRAPPER_NAME/Modules/module.modulemap"

Error log

Direct framework verification:

/tmp/tuist-mergeable-system-library-repro/Build/Products/Debug/GRDBSQLite.framework:
unsealed contents present in the root directory of an embedded framework

Observed in the original mergeable-libraries Debug build:

App.app/Contents/ReexportedBinaries/GRDBSQLite.framework:
unsealed contents present in the root directory of an embedded framework
Command CodeSign failed with a nonzero exit code

macOS version

26.5.2 (25F84), arm64

Tuist version

4.202.1

Xcode version

26.6 (17F113)

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
D
dosubot[bot] Jul 23, 2026

Thanks for the detailed report and reproduction project, @RbBtSn0w.

I can confirm the root cause in the codebase. The ModuleMapMapper generates a “Copy Module Map” script phase for framework products that unconditionally creates a top-level Modules directory inside the framework bundle [1]:

mkdir -p "$TARGET_BUILD_DIR/$WRAPPER_NAME/Modules"
cp -f ... "$TARGET_BUILD_DIR/$WRAPPER_NAME/Modules/module.modulemap"

This doesn’t account for macOS versioned framework layouts (Versions/A/Modules with a root symlink), which is what causes the “unsealed contents present in the root directory” codesigning failure.

Additionally, PackageInfoMapper validates that systemLibrary targets have a module.modulemap but doesn’t assign them a special product type [2] — so they can end up materialized as regular frameworks depending on how products are declared, triggering the module map copy logic.

This looks like a legitimate bug. To help debug further, you could try installing this skill following the steps in the agent-skills repo and use a coding agent (Claude, Codex, etc.) to investigate the generated project and explore potential fixes. If that doesn’t yield a solution, the reproduction project you’ve already provided should be very helpful for the maintainers.

To reply, just mention @dosu.


Docs are dead. Just use Dosu.

Leave Feedback Ask Dosu about tuist Share Dosu with your team

R
RbBtSn0w Jul 23, 2026

I’d be happy to work on a fix for this.

My initial approach would be to update ModuleMapMapper so that module maps for versioned macOS frameworks are copied into Versions/A/Modules, with the expected root-level Modules symlink, and add a regression test covering the generated framework layout and strict code-sign verification.

Before I start, would the maintainers prefer this targeted fix, or should SwiftPM systemLibrary targets avoid framework materialization entirely?

R
RbBtSn0w Jul 23, 2026

I’d be happy to work on a fix for this.

My initial approach would be to update ModuleMapMapper so that module maps for versioned macOS frameworks are copied into Versions/A/Modules, with the expected root-level Modules symlink, and add a regression test covering the generated framework layout and strict code-sign verification.

Before I start, would the maintainers prefer this targeted fix, or should SwiftPM systemLibrary targets avoid framework materialization entirely?

@fortmarek

P
pepicrft Jul 23, 2026

@RbBtSn0w could you put together a PR? Seems a valid issue.

L
Loupehope Jul 24, 2026

Hi! This may also help with this case: https://github.com/tuist/tuist/pull/12055 removes the generated Copy Module Map phase by passing the original map through MODULEMAP_PATH instead of MODULEMAP_FILE.

As a result, Tuist no longer copies Modules/module.modulemap into the root of GRDBSQLite.framework, which should avoid the invalid framework layout that causes the mergeable-library re-export signing failure.

L
Loupehope Jul 24, 2026

I forked the repro as Loupehope/tuist-mergeable-system-library-repro and added the tuist binary from https://github.com/tuist/tuist/pull/12055.

Everything now builds and signs correctly.

Note: to build this repository, use ./tuist_bin/tuist instead of the globally installed tuist. The locally tuist may require explicit approval in macOS System Settings > Privacy & Security before it can be run, due to Gatekeeper security checks.