memlnaut-nisps/vcv/DISTRIBUTION.md
monkey-w1n5t0n 6584423ccd build(vcv): macOS x64+arm64 cross-build here, resource-bounded (osxcross + system clang)
- vcv/build-mac.sh: bounded Docker osxcross build (system clang, NOT LLVM-from-
  source; cached toolchain image for fast reruns) → ad-hoc-signed mac-x64 +
  mac-arm64 .vcvplugin. MacOSX12.3 SDK from joseluisq/macosx-sdks.
- DISTRIBUTION.md: macOS is now a host bounded cross-build, not CI-only.
All four platforms (lin/win/mac-x64/mac-arm64) now published at /next/vcv.
2026-06-28 06:22:51 +02:00

6.9 KiB

Distributing the MEMLNaut VCV Rack plugin

How the MEMLNaut plugin is built for every platform and published to the public download page at https://meml.lnfinitemonkeys.org/next/vcv/.

Plugin slug: MEMLNaut. Version comes from plugin.json (jq -r .version).


1. Build artefacts (.vcvplugin)

A .vcvplugin is a tar.zst archive of a MEMLNaut/ directory containing the platform binary (plugin.so / .dll / .dylib), plugin.json, and res/. The official plugin.mk dist target produces it, named <slug>-<version>-<platform>.vcvplugin (e.g. MEMLNaut-0.2.0-lin-x64.vcvplugin). Platforms: lin-x64, win-x64, mac-x64, mac-arm64.

We have no dedicated build box, so we build on this host, but resource-bounded so a build can never starve the live services. Build products (build/, dist/, plugin.so, *.vcvplugin) are git-ignored — they are publish artefacts, not source.

Linux x64 — locally (works today)

The Rack 2 SDK is installed at $HOME/.local/share/Rack2/Rack-SDK:

cd vcv
RACK_DIR=$HOME/.local/share/Rack2/Rack-SDK make dist
# -> vcv/dist/MEMLNaut-<version>-lin-x64.vcvplugin

Native host toolchain, SDK already present — trivial, no caps needed.

Windows x64 — locally, resource-bounded (works today)

vcv/build-win.sh cross-builds Windows in a hard-capped Docker container using prebuilt mingw-w64 (no GCC/toolchain compile — it only apt-installs mingw and compiles two .cpp files). The container is capped on CPU + memory with no extra swap, so the cgroup OOM-kills the container rather than the host if it ever exceeded the cap. In practice the host stays at full free RAM throughout.

vcv/build-win.sh                 # defaults: CPUS=6 MEM=8g, Rack SDK 2.6.4
CPUS=4 MEM=6g vcv/build-win.sh   # tighter caps
# -> vcv/dist/MEMLNaut-<version>-win-x64.vcvplugin

Note: the OSC bridge server needs Winsock on Windows; vcv/Makefile links -lws2_32 when ARCH_WIN is set (mingw ignores the MSVC #pragma comment(lib,…)).

macOS (x64 + arm64) — locally, resource-bounded (works today)

vcv/build-mac.sh cross-builds both macOS arches in a hard-capped Docker container (--cpus=8 --memory=20g --memory-swap=20g, no host swap), ad-hoc signs the dylibs with rcodesign, and writes vcv/dist/MEMLNaut-<version>-mac-{x64,arm64}.vcvplugin.

vcv/build-mac.sh                  # first run ~18 min (builds the osxcross toolchain image)
vcv/build-mac.sh                  # reruns ~2.5 min (cached image)
REBUILD_TOOLCHAIN=1 vcv/build-mac.sh   # force-rebuild the toolchain image

Key choices that keep it bounded + legal-ish:

  • osxcross is built with the container's SYSTEM clang — we do NOT compile Clang/LLVM from source (the official toolchain's build_clang.sh is the multi-hour, RAM-hungry trap). osxcross only builds cctools-port + ld64 + wrappers (~16-18 min, peak < 1 GB RAM). The result is cached as a local Docker image nisps-osxcross:darwin21.4-12.3 (~3 GB, contains the SDK) so reruns are fast.
  • Apple macOS SDK: MacOSX12.3.sdk.tar.xz from joseluisq/macosx-sdks. Apple's licence restricts SDK use to Apple hardware — this is a grey area; it never leaves the local toolchain image and is never redistributed.
  • The dylibs are ad-hoc signed, not notarised, so macOS Gatekeeper may quarantine a download. Users clear it once: xattr -dr com.apple.quarantine ~/Documents/Rack2/plugins-mac-*/MEMLNaut.

CI (below) remains an alternative for macOS — and the only option if you don't want the SDK on this host — but the local bounded build is the default now (no dedicated box).


2. CI: .github/workflows/vcv-plugin.yml

Builds all four platforms on ubuntu-latest runners via the pinned toolchain (TOOLCHAIN_REF, currently Rack SDK 2.6.6 / toolchain image v19). Triggers on:

  • pushes touching vcv/** (smoke-build the matrix),
  • pull requests touching vcv/**,
  • tag pushes v* (build and attach to a GitHub Release),
  • manual workflow_dispatch.

Per matrix platform the job: checks out the plugin + pinned toolchain, fetches the per-platform Rack SDKs (make rack-sdk-all), builds/loads the cached toolchain Docker image, then runs make docker-plugin-build-<platform> PLUGIN_DIR=$GITHUB_WORKSPACE/vcv. The resulting .vcvplugin is uploaded as a workflow artifact. On a v* tag the release job downloads all four and attaches them to the release via softprops/action-gh-release.

The heavy toolchain image is cached by TOOLCHAIN_REF, so only the first run on a new pin pays the build cost.

Cutting a release

# bump version in vcv/plugin.json, commit, then:
git tag v0.2.0
git push origin v0.2.0

CI builds lin-x64 / win-x64 / mac-x64 / mac-arm64 and attaches the four .vcvplugin files to the GitHub release for v0.2.0.

Bumping the toolchain

Edit TOOLCHAIN_REF (and TOOLCHAIN_IMAGE_VERSION if the image version changes) in .github/workflows/vcv-plugin.yml. Pin to a commit — never float on master.


3. Publishing to /next/vcv

The download page lives in the live a-immersive docroot at /home/w1n5t0n/deployments/meml-aimmersive/next/vcv/. It is purely additive — a new subdir alongside /next/animations, disturbing nothing else.

Files:

  • index.html — the download page (Manifold design tokens: dark, JetBrains Mono, --accent #ff6a00). Lists each platform with a download link, the version, VCV install instructions, and which platforms are available now vs built by CI.
  • a-immersive.html — a byte-identical copy of index.html. nginx for meml.lnfinitemonkeys.org sets index a-immersive.html, so /next/vcv/ resolves via this alias (same trick /next/animations uses). No nginx change is needed for a new subdir.
  • MEMLNaut-<version>-<platform>.vcvplugin — the artefact(s).

Adding CI release artifacts to /next/vcv

After a v* release builds on CI, copy the new .vcvplugin files into the publish dir and refresh the cards/links in both HTML copies:

# from a checkout, with the release tag's artifacts downloaded e.g. via:
#   gh release download v0.2.0 -p '*.vcvplugin' -D /tmp/vcvrel
PUB=/home/w1n5t0n/deployments/meml-aimmersive/next/vcv
cp /tmp/vcvrel/*.vcvplugin "$PUB"/
# then edit $PUB/index.html: flip the win/mac cards from "built by CI"
# (.dl.disabled) to live download links, and copy index.html -> a-immersive.html:
cp "$PUB/index.html" "$PUB/a-immersive.html"

Bumping the version means updating the v0.2.0 strings and the filenames in the two HTML copies. Keep index.html and a-immersive.html identical.

Verify

curl -sk -o /dev/null -w '%{http_code}\n' https://meml.lnfinitemonkeys.org/next/vcv/
curl -sk -o /dev/null -w '%{http_code}\n' \
  https://meml.lnfinitemonkeys.org/next/vcv/MEMLNaut-0.2.0-lin-x64.vcvplugin

Both should return 200. Don't disturb /next/, /next/animations/, or the live a-immersive at /.