build(vcv): build Windows here in a resource-bounded container + link ws2_32

- vcv/Makefile: include arch.mk early, link -lws2_32 when ARCH_WIN (the OSC
  server uses Winsock; mingw ignores the MSVC #pragma comment lib).
- vcv/build-win.sh: bounded Docker mingw cross-build (hard CPU/mem caps, no
  extra swap, prebuilt mingw — no toolchain compile). Produces win-x64 .vcvplugin.
- DISTRIBUTION.md: reflect the no-dedicated-box reality — Linux + Windows built
  locally (supervised + bounded), macOS needs the Apple SDK (CI or a Mac).
This commit is contained in:
monkey-w1n5t0n 2026-06-28 05:16:39 +02:00
parent 892384f043
commit c86c7da5df
3 changed files with 97 additions and 13 deletions

View file

@ -15,6 +15,10 @@ official `plugin.mk` `dist` target produces it, named
`<slug>-<version>-<platform>.vcvplugin` (e.g. `MEMLNaut-0.2.0-lin-x64.vcvplugin`). `<slug>-<version>-<platform>.vcvplugin` (e.g. `MEMLNaut-0.2.0-lin-x64.vcvplugin`).
Platforms: `lin-x64`, `win-x64`, `mac-x64`, `mac-arm64`. 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) ### Linux x64 — locally (works today)
The Rack 2 SDK is installed at `$HOME/.local/share/Rack2/Rack-SDK`: The Rack 2 SDK is installed at `$HOME/.local/share/Rack2/Rack-SDK`:
@ -25,21 +29,39 @@ RACK_DIR=$HOME/.local/share/Rack2/Rack-SDK make dist
# -> vcv/dist/MEMLNaut-<version>-lin-x64.vcvplugin # -> vcv/dist/MEMLNaut-<version>-lin-x64.vcvplugin
``` ```
This is the only platform we build on the server: the host toolchain is native Native host toolchain, SDK already present — trivial, no caps needed.
Linux x64 and the SDK is already present. Build products (`build/`, `dist/`,
`plugin.so`, `*.vcvplugin`) are git-ignored — they are publish artefacts, not
source.
### Windows / macOS — cross-built on CI (NOT on the server) ### Windows x64 — locally, resource-bounded (works today)
The other three platforms are cross-built by the official `vcv/build-win.sh` cross-builds Windows in a **hard-capped Docker container** using
[VCVRack/rack-plugin-toolchain](https://github.com/VCVRack/rack-plugin-toolchain), **prebuilt mingw-w64** (no GCC/toolchain compile — it only apt-installs mingw and
the same Docker cross-builder the VCV Library uses. **We do not run that toolchain compiles two `.cpp` files). The container is capped on CPU + memory with no extra
on the production VPS** — its image build compiles mingw + osxcross and pulls the swap, so the cgroup OOM-kills the container rather than the host if it ever exceeded
Apple SDK (multi-GB, multi-hour), which would overload a host that serves live the cap. In practice the host stays at full free RAM throughout.
services. macOS additionally requires the Apple SDK, which we never redistribute.
CI is the right place for all of this; for a local-only macOS build the operator ```bash
can use a Mac with the native SDK. 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) — Apple SDK required
macOS cross-builds need Apple's **licensed macOS SDK**, which we never download or
redistribute on this host. Two routes:
1. **CI** (`.github/workflows/vcv-plugin.yml`) — the
[VCVRack/rack-plugin-toolchain](https://github.com/VCVRack/rack-plugin-toolchain)
builds mac-x64 + mac-arm64 on tagged releases (the toolchain fetches the SDK only
inside its own image build).
2. **A Mac** — build with the native SDK + `RACK_DIR` set to the macOS Rack SDK.
The full rack-plugin-toolchain image build is **not** run on this host: its Dockerfile
`COPY`s the Apple SDK and compiles osxcross, so it both needs the SDK and is multi-GB
/ multi-hour. That belongs on CI. (Linux + Windows above need none of it.)
--- ---

View file

@ -1,5 +1,14 @@
RACK_DIR ?= $(HOME)/.local/share/Rack2/Rack-SDK RACK_DIR ?= $(HOME)/.local/share/Rack2/Rack-SDK
# Detect the target arch early (defines ARCH_WIN/ARCH_LIN/ARCH_MAC) so we can add
# per-platform link libs before plugin.mk wires the link rule. The OSC bridge
# server (src/osc_server.hpp) uses Winsock2 on Windows; mingw ignores the
# MSVC-only `#pragma comment(lib, "ws2_32.lib")`, so link it explicitly.
include $(RACK_DIR)/arch.mk
ifdef ARCH_WIN
LDFLAGS += -lws2_32
endif
FLAGS += -std=c++20 FLAGS += -std=c++20
FLAGS += -I$(RACK_DIR)/include -I$(RACK_DIR)/dep/include FLAGS += -I$(RACK_DIR)/include -I$(RACK_DIR)/dep/include
# The retired nisps-core header tree is gone; the runtime IML/MLP is vendored # The retired nisps-core header tree is gone; the runtime IML/MLP is vendored

53
vcv/build-win.sh Executable file
View file

@ -0,0 +1,53 @@
#!/usr/bin/env bash
#
# build-win.sh — cross-build the MEMLNaut VCV plugin for Windows x64, here, in a
# RESOURCE-BOUNDED Docker container (prebuilt mingw-w64; no GCC/toolchain compile).
#
# Why Docker + caps: this host also runs live services. The container is hard-
# capped (CPU + memory + no extra swap) so the build physically cannot starve or
# crash the host — the cgroup OOM-kills the container, not the system, if it ever
# exceeded the cap. In practice this build peaks well under the cap (it only
# apt-installs prebuilt mingw + compiles two .cpp files), and the host stays at
# full free RAM throughout. Tune CPUS / MEM below for your machine.
#
# Output: vcv/dist/MEMLNaut-<version>-win-x64.vcvplugin
# Requires: docker. Run from anywhere; paths are resolved from this script.
set -euo pipefail
CPUS="${CPUS:-6}" # cores the container may use
MEM="${MEM:-8g}" # hard memory cap (== memory-swap, so no host swap)
RACK_SDK_VERSION="${RACK_SDK_VERSION:-2.6.4}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # vcv/
WORK="$(mktemp -d /tmp/vcv-win-build.XXXXXX)"
trap 'rm -rf "$WORK"' EXIT
echo "==> staging a clean copy of the plugin source"
cp -r "$SCRIPT_DIR/." "$WORK/"
rm -rf "$WORK/build" "$WORK/dist" "$WORK/plugin.so" 2>/dev/null || true
cat > "$WORK/_in-container.sh" <<EOF
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq g++-mingw-w64-x86-64-posix make unzip wget zstd tar jq >/dev/null
cd /tmp
wget -q "https://vcvrack.com/downloads/Rack-SDK-${RACK_SDK_VERSION}-win-x64.zip"
unzip -q "Rack-SDK-${RACK_SDK_VERSION}-win-x64.zip"
cd /src
export RACK_DIR=/tmp/Rack-SDK
export CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix
export STRIP=x86_64-w64-mingw32-strip OBJCOPY=x86_64-w64-mingw32-objcopy
make clean >/dev/null 2>&1 || true
make -j${CPUS} dist
ls -la dist/
EOF
echo "==> building Windows plugin in a bounded container (cpus=$CPUS, mem=$MEM, no extra swap)"
docker run --rm --cpus="$CPUS" --memory="$MEM" --memory-swap="$MEM" \
-v "$WORK:/src" ubuntu:24.04 bash /src/_in-container.sh
mkdir -p "$SCRIPT_DIR/dist"
cp "$WORK"/dist/*-win-x64.vcvplugin "$SCRIPT_DIR/dist/"
echo "==> done: $(ls "$SCRIPT_DIR"/dist/*-win-x64.vcvplugin)"