Skip to content

Bundle and re-sign libusb for the signed macOS CLI#6

Merged
cboulay merged 1 commit into
masterfrom
fix/macos-cli-libusb-linkage
Jul 6, 2026
Merged

Bundle and re-sign libusb for the signed macOS CLI#6
cboulay merged 1 commit into
masterfrom
fix/macos-cli-libusb-linkage

Conversation

@cboulay

@cboulay cboulay commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

The released MCCOutletCLI from the signed macOS tarball is dead on arrival on every machine — even ones with brew install libusb:

dyld[…]: Library not loaded: /opt/homebrew/opt/libusb/lib/libusb-1.0.0.dylib
  Reason: … code signature … not valid for use in process:
  mapping process and mapped file (non-platform) have different Team IDs

The GUI app is fine; only the CLI is broken.

Root cause

Two facts combine:

  1. The CLI links libusb by absolute Homebrew path (otool -L shows /opt/homebrew/opt/libusb/lib/libusb-1.0.0.dylib). lsl.framework gets bundled + @rpath-fixed-up correctly, but libusb had no equivalent bundling step.
  2. The release CLI is Developer-ID signed with the hardened runtime, which enforces library validation: a process may only load dylibs signed by Apple or the same Team ID. Homebrew's libusb bottle is signed by a different team, so dyld refuses it unconditionally. No end-user brew install can fix it — the binary is unrunnable by construction.

Why CI didn't catch it

The MCCOutletCLI --help smoke tests run in the build job, before the sign-macos job adds the hardened runtime, on a runner that has brew libusb. Unsigned/non-hardened binaries can load cross-team dylibs, so the pre-signing test passes, then signing silently breaks the shipped artifact.

Fix

In the sign-macos job, before sign_and_notarize.sh runs on the CLI, treat libusb exactly like lsl.framework:

  1. Copy libusb-1.0.0.dylib into the CLI's Frameworks/ dir — preferring the copy macdeployqt already placed in MCCOutlet.app, falling back to whatever reference the CLI currently links (handles arm64 /opt/homebrew vs x86_64 /usr/local).
  2. Normalize its install name to @rpath/libusb-1.0.0.dylib (resolved via the CLI's existing @executable_path/Frameworks rpath).
  3. Repoint the CLI with install_name_tool -change, resolving the current reference dynamically.
  4. Sign the dylib with the Developer ID before the CLI (dependency before dependent), same as the framework.
  5. Guardrail: after signing, fail the build if the CLI still links any absolute brew dylib.

The dylib ships automatically — Frameworks is already in the release tarball.

Verification

Manually validated the equivalent steps against the v3.2.0 asset on both a dev Mac and a deployed study computer: after the repoint + re-sign the CLI runs and enumerates an attached USB-1608FS-Plus. Once this ships, the downstream brnbci-deploy workaround that repoints + re-signs the binary post-download becomes a no-op.

🤖 Generated with Claude Code

The released MCCOutletCLI linked libusb by absolute Homebrew path. Once
the CLI is Developer-ID signed with the hardened runtime it is subject to
library validation and may only load dylibs signed by Apple or the same
Team ID. Homebrew's libusb bottle is signed by a different team, so dyld
refused it unconditionally and the shipped CLI was unrunnable on every
machine (including ones with `brew install libusb`).

CI missed this because the smoke test runs in the build job, before the
sign-macos job adds the hardened runtime, on a runner that has brew libusb.

Fix, in the sign-macos job before the CLI is signed: treat libusb exactly
like lsl.framework. Copy the dylib into the CLI's Frameworks/ dir
(preferring the macdeployqt'd app copy, falling back to the CLI's current
reference so it works on both /opt/homebrew and /usr/local runners),
normalize its install name to @rpath, repoint the CLI, and sign the dylib
with the Developer ID before the CLI (dependency before dependent). The
dylib ships automatically since Frameworks is already in the tarball.

Also add a guardrail: after signing, fail the build if the CLI still
links any absolute brew dylib.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cboulay cboulay merged commit 66d5f84 into master Jul 6, 2026
5 checks passed
@cboulay cboulay deleted the fix/macos-cli-libusb-linkage branch July 6, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant