Skip to content

Fix macOS release plugins failing to load (v0.4.1) - #18

Merged
keithlostracco merged 2 commits into
mainfrom
fix/macos-release-plugin-loading
Sep 8, 2026
Merged

Fix macOS release plugins failing to load (v0.4.1)#18
keithlostracco merged 2 commits into
mainfrom
fix/macos-release-plugin-loading

Conversation

@keithlostracco

Copy link
Copy Markdown
Contributor

Summary

The v0.4.0 macOS archive fails in TouchDesigner with "plugin is corrupted". Three build-configuration defects and one distribution limitation, all reproduced with the shipped archive on macOS 26.6 / TouchDesigner 2025.33230:

  1. Wrong Python linked. The release runner has no TouchDesigner, so CMake fell back to Homebrew Python 3.14 and linked the plugins against /opt/homebrew/opt/python@3.14/Frameworks/Python.framework/Versions/3.14/Python. That path exists on no user's Mac, and 3.14 is the wrong ABI for the 3.11 TouchDesigner embeds. (The CI log shows the fallback warning.)
  2. Minimum macOS 26, arm64 only. Nothing set a deployment target or architectures, so the binaries inherited the macos-26-arm64 runner's.
  3. Incomplete signature. Only the linker's implicit ad-hoc signature on the Mach-O; codesign --verify rejects the bundle.
  4. Quarantine. Without notarization, macOS refuses any quarantined copy regardless of the above.

Earlier releases never shipped a Mac binary at all (the old workflow uploaded a .dylib the bundle build never produced), which is why this is the first time it shows.

Changes

  • CMakeLists.txt: on macOS, stop linking libpython. Link with -undefined dynamic_lookup so Python symbols resolve from TouchDesigner's own interpreter at load time, as every CPython extension does. Only the 3.11 headers are needed: TouchDesigner's when installed (ANIMATIONCHOP_TD_APP overrides the location), else find_package(Python3 3.11 EXACT ...) so a newer interpreter can never be picked. Deployment target 13.3 (the bindings use std::format on floats, which libc++ only provides from 13.3; TouchDesigner's own minimum is 13.0), universal arm64;x86_64, and an ad-hoc codesign of the whole bundle post-build. Version bumped to 0.4.1.
  • tests/python/CMakeLists.txt: the test extension uses the same headers and undefined-symbol linking on macOS.
  • ci.yml / release.yml: a "Verify macOS plugins" step fails the build if a plugin links any Python library, is not universal, has a minimum OS other than 13.3, or fails codesign --verify --strict. The release gate can no longer publish this class of binary.
  • README.md: install step to clear the quarantine flag (xattr -dr com.apple.quarantine Plugins), updated requirements and build notes.
  • CHANGELOG.md: 0.4.1 entry. run_td_tests.sh is now executable.

Verification

  • Shipped v0.4.0 plugins in TouchDesigner: "Failed to load the plugin", both as downloaded and with quarantine stripped (dyld: missing Python 3.14 dylib).
  • Fixed build with TouchDesigner headers: universal, minos 13.3, no Python link, codesign --verify --deep --strict passes, ctest 18/18, TouchDesigner integration suite 418 passed, 0 failed.
  • Fixed build simulating CI (-DANIMATIONCHOP_TD_APP=/nonexistent, uv's CPython 3.11 on PATH): identical result, 418/418 in TouchDesigner.
  • The same fixed plugin with a quarantine flag applied: refused by macOS. With the flag cleared: 418/418. Hence the README step.

Reviewer notes

  • Getting rid of the quarantine step entirely needs a Developer ID certificate and notarization in the release workflow. That is a separate piece of work; this PR makes the binaries themselves correct.
  • The runner's fallback Python search relies on actions/setup-python putting 3.11 on PATH, which it already does. The first CI run on this branch is the real test of that path; the new verify step will fail loudly if anything is off.

🤖 Generated with Claude Code

keithlostracco and others added 2 commits September 8, 2026 10:52
The v0.4.0 macOS plugins were reported by TouchDesigner as corrupted. The
release runner has no TouchDesigner, so CMake fell back to the runner's
Homebrew Python 3.14 and linked the plugins against
/opt/homebrew/opt/python@3.14/..., a path that exists on no user's Mac and
is the wrong ABI for the 3.11 TouchDesigner embeds. The binaries also
inherited the runner's macOS 26 as their minimum OS and arm64 as their only
architecture, and carried only the linker's partial ad-hoc signature.

The plugins now link no Python library at all: symbols resolve from the host
process at load time (-undefined dynamic_lookup), as for any CPython
extension. Only the 3.11 headers are needed, taken from TouchDesigner when it
is installed and from any CPython 3.11 (EXACT) otherwise. Deployment target
is 13.3 (std::format on floats needs libc++ from 13.3), architectures are
arm64 and x86_64, and the bundle is ad-hoc signed as a whole post-build so
codesign accepts it. CI and the release gate now verify all four properties.

Gatekeeper still refuses a quarantined copy without notarization, so the
README's install steps now include clearing the quarantine flag. Verified
locally: both the TouchDesigner-headers build and the no-TouchDesigner
fallback produce identical universal plugins that pass the 418-check
TouchDesigner integration suite once the flag is cleared.

Also makes run_td_tests.sh executable and bumps the version to 0.4.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A workflow_dispatch run from a branch whose name contains a slash (such as
fix/macos-release-plugin-loading) made the stage name a nested path, so the
zip landed in a subdirectory the upload glob never matched and both
platforms failed at the upload step. Tags are unaffected; branch runs now
use dev-<short sha>.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@keithlostracco
keithlostracco merged commit 920d531 into main Sep 8, 2026
6 checks passed
@keithlostracco
keithlostracco deleted the fix/macos-release-plugin-loading branch September 8, 2026 18:32
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