Make packaged macOS liblsl self-contained (afterPack hook)#221
Merged
Conversation
jdpigeon
commented
Jul 6, 2026
| * the dylib resolves its deps from its own directory. Re-signs ad-hoc afterward | ||
| * because install_name_tool invalidates the signature. | ||
| * | ||
| * ponytail: handles the current liblsl→pugixml case; the recursion covers deeper |
Contributor
Author
There was a problem hiding this comment.
remove ponytail oparagraph
jdpigeon
commented
Jul 6, 2026
| * ponytail: handles the current liblsl→pugixml case; the recursion covers deeper | ||
| * chains if a future liblsl build adds them. macOS-only (uses install_name_tool | ||
| * / codesign). Couples packaging to the build machine having the Homebrew deps — | ||
| * same assumption patchDeps already makes for dev. |
Contributor
Author
There was a problem hiding this comment.
Add section: "Remove when this is true"
| - [ ] **Lesson content from Steve Azeka** — owner: Dano. Set a date for the first sample lesson. Fallback: if no lesson by that date, Dano authors one throwaway stub to unblock the lesson surface. | ||
| - [ ] **In-app lesson surface v1** — static markdown render only (no editing, no interactivity). Build against a dummy lesson; tune to real content once a sample lands. Confirm markdown is a rich-enough format with Azeka before building. | ||
| - [ ] **Land LSL** — merge `device-lsl` to `main` and QA it. Muse-first, Mac + Windows. | ||
| - [ ] **Land LSL** — `device-lsl` merged (PR #204). Remaining: packaging verification per platform. |
The Homebrew liblsl that patchDeps symlinks in for Apple Silicon is not self-contained: it dynamically links /opt/homebrew/opt/pugixml/lib/ libpugixml.1.dylib. electron-builder dereferences the symlink so the arm64 liblsl.dylib lands in the app as a real file, but that pugixml path doesn't exist on a user's Mac — so dlopen(liblsl) fails and LSL silently no-ops in distributed builds. (The docs flagged ASAR path breakage as the packaging risk; asarUnpack handles that. This transitive-dep breakage is the actual blocker, surfaced by otool -L on a packaged build.) internals/scripts/afterPack.mjs (wired via build.afterPack) walks liblsl's otool -L deps, copies each external (/opt/homebrew, /usr/local, /opt/local) dylib next to it in app.asar.unpacked/.../prebuild/, rewrites the references to @loader_path, and re-signs ad-hoc (install_name_tool invalidates the signature, and arm64 dlopen requires a valid one). Recursive + arch-agnostic, so an x64 build or a future liblsl with deeper deps is handled too. Verified on a packaged arm64 --dir build: otool -L shows zero /opt/homebrew refs (only @loader_path + /usr/lib), codesign --verify passes on both dylibs, and process.dlopen resolves all deps (fails with "Module did not self-register" rather than "Library not loaded: /opt/homebrew/..."). x64-mac / Windows / Linux packaging verification tracked in TODOS (need those build machines). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SNEh5EFg4dSpRrfJ2ae4vL
1daa3e2 to
de75e05
Compare
Contributor
Author
|
Addressed all three comments and force-pushed (
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
The packaged macOS app can't load liblsl on a clean machine — item 6 of the LSL test plan fails even on arm64, and not for the documented reason (ASAR paths;
asarUnpackalready handles that).patchDeps.mjssymlinks the Homebrew liblsl in for Apple Silicon. electron-builder dereferences the symlink at package time, so the arm64liblsl.dyliblands as a real file — but the Homebrew build dynamically links/opt/homebrew/opt/pugixml/lib/libpugixml.1.dylib, an absolute path that doesn't exist on a user's Mac.dlopen(liblsl)then fails and LSL silently no-ops.Found via
otool -Lon a packaged build (see PR discussion).The fix
internals/scripts/afterPack.mjs(wired viabuild.afterPack) walks liblsl'sotool -Ldeps, copies each external (/opt/homebrew,/usr/local,/opt/local) dylib into the unpacked bundle next to liblsl, rewrites the references to@loader_path, and re-signs ad-hoc (install_name_toolinvalidates the signature, and arm64dlopenrequires a valid one). Recursive + arch-agnostic.Verification (arm64
--dirbuild)otool -Lon packagedliblsl.dylib: only@loader_path/*+/usr/lib/*— zero/opt/homebrewrefslibpugixml.1.dylibbundled alongside, its own id rewritten to@loader_pathcodesign --verifypasses on both dylibsprocess.dlopenresolves all deps (fails with "Module did not self-register", i.e. reached the loader, not "Library not loaded: /opt/homebrew/…")Not covered (tracked in TODOS — need the build machines)
otool -Lconfirmlsl_amd64.dllis self-contained PE; needs a Windows build to confirm koffi loads it.sois x86_64 ELF;lddon Ubuntu + Web-BT smoke🤖 Generated with Claude Code