docs: audit the Linux build/toolchain gap ahead of Phase Λ
This commit is contained in:
@@ -0,0 +1,696 @@
|
||||
# Λ-W1 Track 1 — Linux build/toolchain audit (build-system lens)
|
||||
|
||||
Static analysis of the build surface only — the 23 `CMakeLists.txt` files, `cmake/`,
|
||||
`src/resource.rc`/`src/resource.h` + the SWELL resgen pipeline, the three vendored submodules
|
||||
as *build inputs*, the `ctest` harness, toolchain-level flags, and the packaging story in
|
||||
`docs/product/versioning-and-release.md`. 2026-08-02, branch `pl-w1-t1-build-toolchain-audit`
|
||||
(cut from `dev` at 6e937b9).
|
||||
|
||||
**Source portability is explicitly NOT audited here** — no Win32/GDI/LICE API usage, path
|
||||
handling, threading, drag/drop, dialog code, fonts, or instrument runtime code. A parallel
|
||||
track (T2) owns that; what I noticed in passing is listed under "Referred to T2" at the end
|
||||
and nowhere else.
|
||||
|
||||
**Verification posture.** I am on Windows and cannot build or run anything on Linux. Every
|
||||
claim below is grounded in a file I opened in this worktree — the tree's own CMake, or a
|
||||
vendored header/source under `vendor/` — and every line number was re-read immediately before
|
||||
being written down. Where a claim depends on runtime behaviour I cannot observe (does REAPER's
|
||||
Linux build call `SWELL_dllMain`? does GCC accept this TU?), it is marked `[verify — Linux]`
|
||||
with the exact check, and it is **not** stated as fact. To get the vendored citations I ran
|
||||
`git submodule update --init` for all three submodules inside this worktree; nothing in the
|
||||
tracked tree was modified.
|
||||
|
||||
Severity vocabulary, per the dispatch: **Blocker** = the Linux build cannot produce a loadable
|
||||
artifact; **Major** = it builds but is degraded or unshippable; **Minor** = ergonomics,
|
||||
warnings, CI polish. Effort bands are S (< half a day), M (1–3 days), L (a week+), and each
|
||||
names the reason for the band rather than asserting a number.
|
||||
|
||||
---
|
||||
|
||||
## 1. Baseline — what the build does today, per platform
|
||||
|
||||
**Two loadable modules, 84 pure libraries declared under `src/core/`, 91 CTest targets,
|
||||
three vendored submodules.** (84 = the `reasampler_pure_library` + bare `add_library` count
|
||||
across the 19 `src/core/**` CMake files; 46 of them are on the extension's link line.)
|
||||
|
||||
- Root `CMakeLists.txt` is 95 lines and declares **no targets at all**: it owns the version
|
||||
string (`:24`), the channel fork (`:40–54`), the `configure_file` for
|
||||
`version_generated.h` (`:58–61`), the four vendor path variables (`:65–68`), the
|
||||
four-TU `LICE_SRC` list (`:78–83`), `enable_testing()` (`:90`), and three
|
||||
`add_subdirectory` calls (`:92–94`).
|
||||
- Language/toolchain settings are exactly three lines: `CMAKE_CXX_STANDARD 17`,
|
||||
`CMAKE_CXX_STANDARD_REQUIRED ON`, `CMAKE_POSITION_INDEPENDENT_CODE ON`
|
||||
(`CMakeLists.txt:28–30`). There is **no** `CMAKE_BUILD_TYPE`, no `CMAKE_CXX_FLAGS`, no
|
||||
`CMAKE_CXX_EXTENSIONS`, no IPO/LTO setting, and no `target_compile_options` anywhere in
|
||||
the tree — I grepped all 23 CMake files for `compile_options`, `find_package`,
|
||||
`pkg_check`, `link_directories`, `target_link_options`, and `install(` and got zero hits
|
||||
outside the two files named below.
|
||||
- **Platform conditionals exist in exactly two files.** Grepping all 23 `CMakeLists.txt`
|
||||
for `WIN32|APPLE|UNIX|LINUX|MSVC|GNU|Clang|CMAKE_SYSTEM` returns hits only in
|
||||
`src/app/CMakeLists.txt` (`:71` `if(WIN32)`, `:76` `elseif(APPLE)`, `:88` `else()`) and
|
||||
`src/shell/instrument/CMakeLists.txt` (`:9` `if(WIN32 AND EXISTS ...)`). Every other
|
||||
CMake file is platform-neutral by construction: they call the two helpers in
|
||||
`cmake/reasampler_targets.cmake` (`reasampler_pure_library` at `:8–15`,
|
||||
`reasampler_test` at `:19–24`) and nothing else.
|
||||
|
||||
### 1a. The REAPER extension (`reaper_reasampler`)
|
||||
|
||||
Declared as a `MODULE` library over 39 shell TUs plus `${LICE_SRC}`
|
||||
(`src/app/CMakeLists.txt:8–51`), linking 46 pure static libraries (`:52`) with
|
||||
`${SDK_INC} ${WDL_INC}` on the include path (`:56`). Output naming is
|
||||
`PREFIX ""` + channel-derived `OUTPUT_NAME`, pinned to the top of the build tree by
|
||||
`LIBRARY_OUTPUT_DIRECTORY`/`ARCHIVE_OUTPUT_DIRECTORY` (`:65–69`).
|
||||
|
||||
The three-way platform block (`:71–98`) is:
|
||||
|
||||
| Branch | State | What it does |
|
||||
|---|---|---|
|
||||
| `if(WIN32)` `:71–74` | **Live and shipped** | adds `src/resource.rc` for the RC compiler |
|
||||
| `elseif(APPLE)` `:76–86` | **Partly aspirational** | adds `swell-modstub.mm`, defines `SWELL_PROVIDED_BY_APP`, links `-framework AppKit`, forces `.dylib`. The resgen `target_sources` line at `:86` is **commented out**. |
|
||||
| `else()` (Linux) `:88–97` | **Partly aspirational** | adds `swell-modstub-generic.cpp`, defines `SWELL_PROVIDED_BY_APP`, forces `.so`. The resgen `target_sources` line at `:97` is **commented out**. |
|
||||
|
||||
So on Linux the build system today produces `reaper_reasampler.so` with the SWELL modstub
|
||||
compiled in and **no dialog resource of any kind**. `README.md:67–68` states this is
|
||||
"expected, not a bug" — the tree is honest about it.
|
||||
|
||||
Three build-input facts I verified in `vendor/` and that hold in the extension's favour:
|
||||
|
||||
- `vendor/WDL/WDL/swell/swell-modstub-generic.cpp` is entirely inside
|
||||
`#ifdef SWELL_PROVIDED_BY_APP` (`:21`, closed at `:150`). Since
|
||||
`SWELL_LOAD_SWELL_DYLIB` is not defined anywhere in this build, the `#else` branch at
|
||||
`:133` applies: the file's whole contribution is the three
|
||||
`SWELL_curmodule_*resource_head` globals (`:31–33`), the API function-pointer table
|
||||
built from `swell.h` (`:37–49`), and the `SWELL_dllMain` export (`:135`) that resolves
|
||||
every SWELL symbol from a host-supplied `GetFunc`. That is the correct stub for a
|
||||
REAPER extension, and the comment at `src/app/CMakeLists.txt:90` ("With
|
||||
SWELL_PROVIDED_BY_APP you can drop pkg-config / -lX11 entirely") is **correct**: no SWELL
|
||||
implementation TU, no X11, no GDK is compiled or linked into the extension.
|
||||
- The four-TU LICE slice is self-contained on Linux. `lice.cpp` reaches SWELL only through
|
||||
`SWELL_DeleteGfxContext` (`:153`, `:218`), `SWELL_GetCtxFrameBuffer` (`:182`, `:226`) and
|
||||
`SWELL_CreateMemContext` (`:224`); `lice_textnew.cpp` reaches it through
|
||||
`SWELL_PushClipRegion`/`SWELL_SetClipRegion` (`:1027–1028`) and `SWELL_PopClipRegion`
|
||||
(`:1048`). All six are entries in SWELL's API table
|
||||
(`vendor/WDL/WDL/swell/swell-functions.h:857`, `:863`, `:877`, `:885–887`), so the
|
||||
modstub resolves them from the host. No fifth LICE TU is implied: the `_LICE_RGB2HSV`
|
||||
table lives inside `lice.cpp` itself (`:3040`), and the one non-Win32-only helper
|
||||
`utf8makechar` is a file-static defined at `lice_textnew.cpp:26` and used at `:241–242`.
|
||||
- `CMAKE_POSITION_INDEPENDENT_CODE ON` (`CMakeLists.txt:30`) is already set. This is the
|
||||
single most commonly missed setting for a `MODULE` target fed by `STATIC` libraries on
|
||||
Linux, and it is correct today.
|
||||
|
||||
### 1b. The VST3 instrument (`reasampler_vst`)
|
||||
|
||||
Hard-gated to Windows: `if(WIN32 AND EXISTS "${VST3_SDK}/public.sdk/source/main/pluginfactory.cpp")`
|
||||
(`src/shell/instrument/CMakeLists.txt:9`). The gate is a conjunction — even with the
|
||||
`vendor/vst3sdk` slice fully initialised, a Linux configure silently omits the target.
|
||||
`README.md:33–35` states this explicitly. Two module-level `CLAUDE.md` files record the
|
||||
underlying product decision (D5) as *settled*, with "a mac/Linux build" named as a
|
||||
non-goal: `src/core/instrument/CLAUDE.md` (Non-goals/guardrails) and
|
||||
`src/shell/instrument/CLAUDE.md` (same). `src/shell/panel/CLAUDE.md` carries a third
|
||||
instance ("Windows-only (D5) — font/GDI/HFONT choices assume Windows").
|
||||
|
||||
Inside the gate: a 19-source `vst3_sdk` static library (`:15–39`) pinned to SDK tag
|
||||
`v3.7.9_build_61` (confirmed by `git describe --tags` in `vendor/vst3sdk`), and the
|
||||
`reasampler_vst` MODULE (`:44–81`) which additionally compiles `dllmain.cpp` and
|
||||
`moduleinit.cpp` directly (`:78–79`, with the linker-stripping rationale at `:75–77`) plus
|
||||
`${LICE_SRC}` (`:80`). Output is a single **file** named `<name>.vst3` (`:106–111`).
|
||||
|
||||
### 1c. Resources
|
||||
|
||||
`src/resource.rc` declares exactly one dialog — `IDD_BANK_PANEL`, a bare `WS_CHILD`
|
||||
`DIALOG` with an empty `BEGIN`/`END` body and no controls (`:18–22`). `src/resource.h`
|
||||
defines the single id `IDD_BANK_PANEL 1000` (`:8`). It is consumed by
|
||||
`CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_BANK_PANEL), …)` at
|
||||
`src/shell/panel/panel_window.cpp:135`.
|
||||
|
||||
### 1d. Test harness
|
||||
|
||||
91 test declarations across 19 `CMakeLists.txt` files (grep count), matching 91 files in
|
||||
`tests/`. 90 go through `reasampler_test` (`cmake/reasampler_targets.cmake:19–24`, which is
|
||||
`add_executable` + `target_link_libraries` + `add_test`); one is longhand —
|
||||
`app_version_padding_tests` (`src/core/version/CMakeLists.txt:37–42`), which recompiles
|
||||
`app_version.cpp` against a second `configure_file` output so the include-dir substitution
|
||||
works. Neither shape carries anything platform-specific.
|
||||
|
||||
Only two test files mention a platform at all: `tests/test_capture_paths.cpp` (five
|
||||
`#ifdef _WIN32` blocks at `:19`, `:41`, `:87`, `:152`, `:205`, each with a POSIX `#else`
|
||||
branch already written — e.g. `:158–168` asserts `resolveBankFile("/home/d/proj", …)`), and
|
||||
`tests/test_pitch_shift.cpp:3`, where the string is inside a comment explaining why the
|
||||
target links nothing that would drag `<windows.h>` in. No test includes `reaper_plugin.h`,
|
||||
`windows.h`, or any SWELL header. **The test harness is the healthiest part of this
|
||||
surface**; see §3 for the one generator-shape caveat.
|
||||
|
||||
### 1e. Packaging / release, as already committed
|
||||
|
||||
`docs/product/versioning-and-release.md` already commits to Linux as a first-class target:
|
||||
|
||||
- `:361–363` — "**Artifact per platform.** The binary is `reaper_*.dll|.dylib|.so`; the
|
||||
macOS/Linux builds need the SWELL resgen step (CLAUDE.md §SWELL dialog resources) baked
|
||||
into the pipeline. Three platform artifacts per channel per release."
|
||||
- `:307–310` — two named artifacts (stable + beta) per platform, "three platform artifacts
|
||||
each", explicitly handed off to dev-ops.
|
||||
- `:364–366` — "Install is copy-in + restart… there is no server-side rollout."
|
||||
|
||||
What that document does **not** contain: any mention of code signing, macOS notarization, a
|
||||
CI runner, or a Linux install path. I grepped it for `sign|notariz|CI|platform|linux` — the
|
||||
only hits are the lines above. There is no CI configuration anywhere in the repo (`ls -a` on
|
||||
the root shows `.git`, `.gitignore`, `.gitmodules`, `CLAUDE.md`, `CMakeLists.txt`, `LICENSE`,
|
||||
`README.md`, `cmake`, `docs`, `src`, `tests`, `vendor` — no `.github`, no `.gitlab-ci.yml`),
|
||||
and no `install()` rule in any of the 23 CMake files. So "the pipeline" is entirely notional
|
||||
today, on every platform.
|
||||
|
||||
---
|
||||
|
||||
## 2. Findings
|
||||
|
||||
### Λ-01 — the Linux/macOS dialog-resource path is broken in three independent ways, and the fix may not need resgen at all
|
||||
**Severity: Major. Effort: S (mechanical) to M (if the resgen route is taken and automated).**
|
||||
|
||||
**Citations.** `src/app/CMakeLists.txt:94–97` (the Linux comment block) and `:83–86` (the
|
||||
identical macOS one); `vendor/WDL/WDL/swell/swell_resgen.php:313–314`;
|
||||
`vendor/WDL/WDL/swell/swell-dlggen.h:22`; `vendor/WDL/WDL/swell/sample_project/myapp.cpp:192–193`;
|
||||
`vendor/WDL/WDL/swell/swell-dlggen.h:214–218`; `vendor/WDL/WDL/swell/swell-modstub-generic.cpp:31–33`;
|
||||
`vendor/WDL/WDL/swell/swell-functions.h:612–619`; `src/resource.rc:18–22`;
|
||||
`src/shell/panel/panel_window.cpp:135`.
|
||||
|
||||
**What breaks.** Three defects stack, and each alone would sink the step:
|
||||
|
||||
1. **The named script does not exist.** Both comment blocks say
|
||||
`php ${WDL_INC}/swell/mac_resgen.php src/resource.rc` (`:85` and `:96`). Listing
|
||||
`vendor/WDL/WDL/swell/` shows `swell_resgen.php`, `swell_resgen.pl`, and
|
||||
`swell_resgen.sh` — there is no `mac_resgen.php`. `CLAUDE.md` and `README.md:120` both
|
||||
name the correct script; only the CMake comments are wrong, which is exactly the file an
|
||||
implementer will be reading when they do the work.
|
||||
2. **The named output file is not what the generator writes.** Both commented
|
||||
`target_sources` lines (`:86`, `:97`) reference
|
||||
`${REASAMPLER_SRC_DIR}/resource.rc_mac_dlg.h`. `swell_resgen.php:313–314` composes its
|
||||
output names as `$srcfn . "_mac_menu"` and `$srcfn . "_mac_dlg"` — so the actual outputs
|
||||
are `src/resource.rc_mac_dlg` and `src/resource.rc_mac_menu`, with **no** `.h` suffix.
|
||||
3. **Even the correct filename cannot be a `target_sources` entry.** `swell-dlggen.h:22`
|
||||
states the contract outright: "Use swell_resgen.pl or swell_resgen.php on the .rc file,
|
||||
which will produce .rc_mac_dlg, **include that**." The generated body is a run of
|
||||
`SWELL_DEFINE_DIALOG_RESOURCE_BEGIN(...)` macro invocations
|
||||
(`swell_resgen.php:117–129`) that only expand after `swell-dlggen.h` has been included —
|
||||
`swell-dlggen.h:214–218` shows the macro emitting a static `SWELL_DialogRegHelper` that
|
||||
registers into `SWELL_curmodule_dialogresource_head`. The canonical consumption pattern
|
||||
is the SDK's own sample: `sample_project/myapp.cpp:192–193` does
|
||||
`#include "../WDL/swell/swell-dlggen.h"` then `#include "res.rc_mac_dlg"`. A CMake
|
||||
`target_sources` entry for an extensionless file would not even get a language assigned.
|
||||
|
||||
**The runtime consequence, and why this is Major rather than Blocker.** The `.so` still
|
||||
builds and still loads: the extension's whole action family, capture pipeline, and persist
|
||||
layer are untouched. What fails is the dock panel. `swell-functions.h:615` defines
|
||||
`CreateDialog(hinst,resid,par,dlgproc)` as
|
||||
`SWELL_CreateDialog(SWELL_curmodule_dialogresource_head,(resid),par,dlgproc,0)`.
|
||||
`swell-modstub-generic.cpp:32` *defines* that head but nothing ever populates it, because
|
||||
the resgen'd registration TU does not exist — so it stays null and
|
||||
`panel_window.cpp:135`'s `CreateDialogParam` gets a null resource list. The panel is the
|
||||
product's primary surface, so a Linux build without it is not shippable; it is not a
|
||||
Blocker only because the binary loads and the actions work.
|
||||
|
||||
**The route worth considering first.** `swell-functions.h:606–608` documents an escape
|
||||
hatch: *"If you are using CreateDialog() and creating a child window, you can use a resource
|
||||
ID of 0, which creates an opaque child window. Instead of passing a DLGPROC, you should pass
|
||||
a (WNDPROC) routine that returns LRESULT (and cast it to DLGPROC)."* `src/resource.rc:18–22`
|
||||
shows `IDD_BANK_PANEL` is precisely that: a `WS_CHILD` dialog with an empty body and zero
|
||||
controls, whose comment at `:8–10` says "the bank_panel shell owns every pixel and draws the
|
||||
sample grid with LICE in `WM_PAINT`". If the panel is created with resource id 0 on
|
||||
non-Windows, **the entire resgen pipeline — the PHP dependency (`README.md:54`), the
|
||||
generated-file-in-source-tree problem, and the per-platform regeneration story — disappears
|
||||
from the Linux and macOS builds.** The cost is a source change in `panel_window.cpp` and the
|
||||
DLGPROC→WNDPROC return-type contract; that half is T2's call, not mine, and I am naming the
|
||||
option rather than choosing it. This is a shared macOS/Linux fix either way.
|
||||
|
||||
**If the resgen route is taken instead**, the build work is: a `find_program(PHP …)`, an
|
||||
`add_custom_command` generating into `${CMAKE_CURRENT_BINARY_DIR}` (not the source tree —
|
||||
`.gitignore` has no pattern matching `resource.rc_mac_dlg`, so today's manual step would
|
||||
leave an untracked generated file sitting in `src/`), a new one-line shim TU that includes
|
||||
`swell-dlggen.h` then the generated file, and that shim added to `target_sources`. That is
|
||||
the M-effort half of the band.
|
||||
|
||||
---
|
||||
|
||||
### Λ-02 — `--config Release` is a silent no-op on Linux, so the documented ship command produces an unoptimized binary
|
||||
**Severity: Major. Effort: S — one `if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)` block plus a doc correction.**
|
||||
|
||||
**Citations.** `CMakeLists.txt:28–30` (the complete list of language settings — I read the
|
||||
whole 95-line file and there is no `CMAKE_BUILD_TYPE` anywhere); `README.md:106–109`;
|
||||
`CLAUDE.md` §"Build and test" and §"Install / reload".
|
||||
|
||||
**What breaks.** Both docs prescribe `cmake --build build --config Release` as the
|
||||
optimize-and-ship incantation, and `README.md:130–131` tells the user to copy from
|
||||
`build/Release/`. `--config` is a multi-config-generator flag; on Ninja or Unix Makefiles it
|
||||
is accepted and ignored. Because no `CMAKE_BUILD_TYPE` default is set, the resulting build
|
||||
passes **no `-O` flag at all** to GCC/Clang — not `-O0` explicitly, just the compiler's
|
||||
unoptimized default. The failure is silent in both directions: the command succeeds, and the
|
||||
artifact lands at `build/reaper_reasampler.so` rather than the documented `build/Release/`
|
||||
path (see Λ-08).
|
||||
|
||||
This matters more here than the usual "forgot to set the build type", because `CLAUDE.md`'s
|
||||
own performance guardrails section states that the header-inline hot paths (`peaks`
|
||||
envelope compute, `Voice::advanceFrame`, the three envelope evaluators) "presume an
|
||||
**optimizing** build", and there is no LTO configured to recover any of it. A Linux user
|
||||
following the README would ship a binary whose hottest loops did not inline.
|
||||
|
||||
**Sequencing note.** This is not strictly Linux-specific — Ninja on Windows has the same
|
||||
shape — but it becomes load-bearing the moment Linux is a shipped platform, because Makefiles
|
||||
and Ninja are the default there.
|
||||
|
||||
---
|
||||
|
||||
### Λ-03 — no symbol-visibility control, and two modules in one process both export a full copy of LICE
|
||||
**Severity: Major. Effort: S — one `CXX_VISIBILITY_PRESET hidden` + `VISIBILITY_INLINES_HIDDEN` pair, plus a `[verify — Linux]` load test.**
|
||||
|
||||
**Citations.** `CMakeLists.txt:78–83` (`LICE_SRC`); `src/app/CMakeLists.txt:35` and
|
||||
`src/shell/instrument/CMakeLists.txt:80` (the same four LICE TUs compiled into *both*
|
||||
modules — the rationale for the duplication is at `CMakeLists.txt:75–77`);
|
||||
`vendor/reaper-sdk/sdk/reaper_plugin.h:52`; `src/app/main.cpp:251`;
|
||||
`vendor/vst3sdk/pluginterfaces/base/fplatform.h:164`;
|
||||
`vendor/WDL/WDL/swell/Makefile:20`.
|
||||
|
||||
**What breaks.** Nothing in the build sets a visibility preset, so GCC/Clang default to
|
||||
`-fvisibility=default` and every non-static symbol in both modules — all of LICE, all the
|
||||
WDL helpers, every pure library's exported functions, and the whole C++ standard-library
|
||||
instantiation set — lands in the module's dynamic symbol table. On Windows this is a
|
||||
non-issue: each DLL has its own symbol table and per-module resolution. On Linux ELF, symbols
|
||||
from `dlopen`'d objects can participate in global interposition, so `reaper_reasampler.so`'s
|
||||
`LICE_FillRect` and a hypothetical `reasampler_9000.so`'s `LICE_FillRect` are candidates to
|
||||
bind to the same definition. That is a class of bug that presents as "the VST editor draws
|
||||
with the extension's LICE build" and is invisible on Windows.
|
||||
|
||||
**Why hiding is safe here.** The two symbols that genuinely must be exported already say so
|
||||
explicitly: `reaper_plugin.h:52` defines
|
||||
`REAPER_PLUGIN_DLL_EXPORT __attribute__((visibility("default")))` on non-Windows, and
|
||||
`main.cpp:251` uses it (`extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(...)`).
|
||||
On the VST3 side, `fplatform.h:164` defines `SMTG_EXPORT_SYMBOL` the same way. SWELL's own
|
||||
build already uses `-fvisibility=hidden` (`Makefile:20`), which is the precedent. The one
|
||||
thing to confirm on a Linux box is that `swell-modstub-generic.cpp:135`'s `SWELL_dllMain`
|
||||
survives — it carries its own `__attribute__ ((visibility ("default")))` at that line, so it
|
||||
should, but the modstub is compiled by *our* build, not SWELL's.
|
||||
|
||||
---
|
||||
|
||||
### Λ-04 — nothing links a thread library, and `<pthread.h>` is in the extension's include graph
|
||||
**Severity: Major (VST3 target) / Minor (extension). Effort: S — `find_package(Threads)` + `Threads::Threads`.**
|
||||
|
||||
**Citations.** `src/shell/panel/panel_state.h:20–24`;
|
||||
`vendor/vst3sdk/base/thread/source/flock.cpp:71–133`;
|
||||
`src/shell/instrument/CMakeLists.txt:15–39`; `vendor/WDL/WDL/swell/Makefile:90`.
|
||||
|
||||
**What breaks.** No CMake file in the tree calls `find_package(Threads)` or links
|
||||
`Threads::Threads`/`-lpthread`/`-ldl` (grep-verified across all 23). Two separate exposures:
|
||||
|
||||
- **Extension.** `panel_state.h:23` includes `<pthread.h>` on non-Win32. I did not find a
|
||||
pthread *call* in the panel's own code, so this may be an include-only dependency that
|
||||
needs no link flag on a modern glibc (where the pthread symbols are in libc proper). That
|
||||
makes it Minor, and `[verify — Linux]`.
|
||||
- **VST3 target.** `flock.cpp` is unambiguous: on non-Windows it calls
|
||||
`pthread_mutexattr_init`/`_settype`/`_destroy` (`:71–76`), `pthread_mutex_init` (`:74`),
|
||||
`pthread_mutex_destroy` (`:89`), `_lock` (`:105`), `_unlock` (`:121`) and `_trylock`
|
||||
(`:133`). It is unconditionally in the `vst3_sdk` source list
|
||||
(`src/shell/instrument/CMakeLists.txt:27`). Any Linux VST3 target must link a thread
|
||||
library. SWELL's own Makefile uses `-lpthread -ldl` (`:90`) for the same reason.
|
||||
|
||||
Adding `find_package(Threads REQUIRED)` + `target_link_libraries(... Threads::Threads)` is
|
||||
correct on all three platforms and costs nothing on Windows.
|
||||
|
||||
---
|
||||
|
||||
### Λ-05 — `reaper_plugin.h`'s SWELL include resolves only by coincidence of the `-I` line
|
||||
**Severity: Minor. Effort: S — a comment, or an `INTERFACE` target that carries both include dirs as one unit.**
|
||||
|
||||
**Citations.** `vendor/reaper-sdk/sdk/reaper_plugin.h:42–54`; `CMakeLists.txt:65–66`;
|
||||
`src/app/CMakeLists.txt:56`; `src/shell/instrument/CMakeLists.txt:97`.
|
||||
|
||||
**What breaks — and, today, does not.** On non-Windows, `reaper_plugin.h:49` does
|
||||
`#include "../WDL/swell/swell.h"`. Relative to the including file's own directory
|
||||
(`vendor/reaper-sdk/sdk/`) that resolves to `vendor/reaper-sdk/WDL/swell/swell.h`, which does
|
||||
not exist — listing `vendor/reaper-sdk/` shows only `README`, `reaper-plugins`, and `sdk`.
|
||||
The include is nevertheless satisfiable, because the quoted-include fallback searches the
|
||||
`-I` path, and `WDL_INC` is `${CMAKE_CURRENT_SOURCE_DIR}/vendor/WDL/WDL` (`CMakeLists.txt:66`) —
|
||||
so `WDL_INC` + `../WDL/swell/swell.h` normalizes to `vendor/WDL/WDL/swell/swell.h`, which
|
||||
exists. Both module targets pass `${WDL_INC}` (`src/app:56`, `src/shell/instrument:97`), so it
|
||||
holds today.
|
||||
|
||||
The hazard is that this is a load-bearing coincidence with no comment anywhere, and it is
|
||||
**invisible on Windows** — `reaper_plugin.h:43` takes `<windows.h>` instead, so dropping
|
||||
`${WDL_INC}` from any target that includes `reaper_plugin.h` would pass CI on Windows and
|
||||
fail only on Linux/macOS. Worth pinning before more targets are added.
|
||||
|
||||
---
|
||||
|
||||
### Λ-06 — the build has no platform partition of its source list, so every T2 finding lands here as a CMake change
|
||||
**Severity: Minor (structural). Effort: S per TU once T2 says which ones need it.**
|
||||
|
||||
**Citations.** `src/app/CMakeLists.txt:8–51` and `:71–98`;
|
||||
`src/shell/actions/drag_out_win.cpp:8`, `:249`, `:282`.
|
||||
|
||||
**Mechanism.** All 39 shell TUs are listed unconditionally (`:8–51`); the platform block at
|
||||
`:71–98` only *adds* sources, never removes or substitutes any. Every TU therefore enters a
|
||||
Linux compile, and the only thing standing between the build and a compile error is each
|
||||
file's own `#ifdef` discipline. Today that discipline is uneven in a way worth recording:
|
||||
|
||||
- `drag_out_win.cpp` is the model — `#ifdef _WIN32` at `:8` with a real
|
||||
`// ---- macOS / Linux (SWELL)` branch at `:249` and `#endif` at `:282`.
|
||||
- `arrange_drop_win.cpp` and `instrument_drop_win.cpp` contain **zero** occurrences of
|
||||
`_WIN32` (grep count 0 each) and include no `<windows.h>` — they are `_win`-suffixed for
|
||||
the surface they serve, not for a platform dependency, and appear portable by inspection.
|
||||
|
||||
This is not itself a defect; it is the reason the ordering sketch in §5 puts "read T2's
|
||||
output, then partition the source list" as a distinct step rather than folding it into the
|
||||
resgen work.
|
||||
|
||||
---
|
||||
|
||||
### Λ-07 — `std::filesystem` in a pure library, with no stated minimum compiler
|
||||
**Severity: Minor. Effort: S — a `cmake_minimum_required`-adjacent compiler check or a documented floor.**
|
||||
|
||||
**Citations.** `src/core/capture/capture_paths.cpp:5` (`#include <filesystem>`) and `:95`
|
||||
(`std::filesystem::path(rppPath).parent_path().string()`); `README.md:50–54`.
|
||||
|
||||
**What breaks.** `capture_paths` is a *pure* library with its own CTest target
|
||||
(`src/core/capture/CMakeLists.txt:1–2`), so this is on the test-harness path as well as the
|
||||
extension's. GCC 8 requires an explicit `-lstdc++fs` for `std::filesystem`; GCC ≥ 9 and
|
||||
libstdc++ ≥ 9 do not. `README.md:51` states only "A C++17 compiler" as the prerequisite, and
|
||||
`cmake_minimum_required(VERSION 3.19)` (`CMakeLists.txt:1`) says nothing about the compiler.
|
||||
On any current distro toolchain this is a non-issue; it becomes one on an older CI image.
|
||||
Twelve other files in `src/shell/` also use `<filesystem>` (grep), but they are all in the
|
||||
extension target, which shares the same fix.
|
||||
|
||||
---
|
||||
|
||||
### Λ-08 — the artifact path and the install instruction are both wrong for a single-config generator
|
||||
**Severity: Minor. Effort: S — a doc fix, or an `install()` rule if packaging is ever automated.**
|
||||
|
||||
**Citations.** `src/app/CMakeLists.txt:68` (`LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}"`);
|
||||
`README.md:130–133`; `CLAUDE.md` §"Install / reload";
|
||||
`docs/product/versioning-and-release.md:364–366`.
|
||||
|
||||
**What breaks.** On a multi-config generator CMake appends the config directory, giving the
|
||||
documented `build/Release/`. On Ninja/Make the module lands directly at
|
||||
`build/reaper_reasampler.so`, so the README's "copy from `build/Release/`" instruction has no
|
||||
matching directory on Linux. Compounding it, there is no `install()` rule anywhere in the 23
|
||||
CMake files, and no target directory is documented for Linux at all — `CLAUDE.md` and
|
||||
`README.md:132` say "REAPER's `UserPlugins/` folder (Options → Show REAPER resource path)",
|
||||
which is a correct user-facing instruction but not a path a pipeline can use. I could not
|
||||
confirm REAPER's Linux resource-path root from the SDK: grepping
|
||||
`vendor/reaper-sdk/sdk/` for `UserPlugins` and `reaper_*.so` returns nothing — the
|
||||
`reaper_*.dll|dylib|so` filename convention stated in `CLAUDE.md` is not documented in the
|
||||
vendored headers. `[verify — Linux]`.
|
||||
|
||||
---
|
||||
|
||||
### Λ-09 — no warning flags anywhere, so the first GCC/Clang build surfaces a diagnostic set nobody has ever seen
|
||||
**Severity: Minor. Effort: S to add flags; **unknowable** to fix what they reveal — that is the honest band.**
|
||||
|
||||
**Citations.** grep for `target_compile_options` across all 23 CMake files: zero hits (the
|
||||
only `compile_definitions` hits are `SWELL_PROVIDED_BY_APP` at `src/app/CMakeLists.txt:80`,
|
||||
`:92` and the SDK's `DEVELOPMENT`/`RELEASE` selector at
|
||||
`src/shell/instrument/CMakeLists.txt:42`). `CMakeLists.txt:28–30` for the absence of
|
||||
`CMAKE_CXX_EXTENSIONS`.
|
||||
|
||||
**Mechanism.** The codebase has been compiled by exactly one compiler family. MSVC's default
|
||||
is `/W1`; GCC/Clang's default is essentially silent. Adding `-Wall -Wextra` on the first
|
||||
Linux build is the right move, but the size of the resulting diagnostic set over ~39k LOC is
|
||||
not estimable from Windows, which is why the effort band is split. Do **not** add `-Werror`
|
||||
in the same change.
|
||||
|
||||
Secondary, in the same family: `CMAKE_CXX_EXTENSIONS` is unset, so it defaults to `ON` and
|
||||
GCC/Clang compile as `-std=gnu++17` rather than `-std=c++17`. Not a bug; a hygiene knob
|
||||
worth flipping while the flags are being set, since it is the difference between "portable
|
||||
C++17" and "whatever GNU extensions happened to be used".
|
||||
|
||||
---
|
||||
|
||||
### Λ-10 — the submodule story costs a Linux developer a large download for a target that is gated off
|
||||
**Severity: Minor. Effort: S — a doc paragraph.**
|
||||
|
||||
**Citations.** `.gitmodules` (three entries, no `shallow`, no `branch`);
|
||||
`README.md:70–86`; `src/shell/instrument/CMakeLists.txt:9`; `CLAUDE.md` §"One-time submodule setup".
|
||||
|
||||
**Mechanism.** `README.md:72–75` already warns that `git clone --recursive` pulls all of
|
||||
`vendor/vst3sdk`'s nested submodules (`vstgui4`, `tutorials`, `doc`, `cmake`) that this
|
||||
project never links, and prescribes the narrow init instead. What neither the README nor
|
||||
`CLAUDE.md` says is that on Linux the `vendor/vst3sdk` submodule is **pure cost**: the
|
||||
`WIN32 AND EXISTS` gate at `src/shell/instrument/CMakeLists.txt:9` omits the target
|
||||
regardless, so `git submodule update --init vendor/reaper-sdk vendor/WDL` is the complete
|
||||
Linux prerequisite. One sentence in the platform-support table would save every Linux
|
||||
contributor the download.
|
||||
|
||||
---
|
||||
|
||||
## 3. Surfaces checked and found clean
|
||||
|
||||
Recorded so the plan author does not re-spend effort here. Each was read or grepped in this
|
||||
pass.
|
||||
|
||||
- **All 23 `CMakeLists.txt` files were read in full**, not sampled: root; `src/app`;
|
||||
`src/core`; `src/core/{audio,capture,instrument,json,model,reclaim,tracking,ui,util,version,view,wire}`;
|
||||
`src/core/instrument/{bake,engine,map,note,ui}`; `src/core/instrument/engine/{filter,loop}`;
|
||||
`src/shell/instrument`. Plus `cmake/reasampler_targets.cmake`. Of these, 21 are entirely
|
||||
platform-neutral — they declare libraries and tests through the two helpers and nothing
|
||||
else.
|
||||
- **`CMAKE_POSITION_INDEPENDENT_CODE ON`** (`CMakeLists.txt:30`) — the usual Linux
|
||||
`MODULE`-fed-by-`STATIC` trap, already handled.
|
||||
- **`.so` naming.** `PREFIX ""` (`src/app/CMakeLists.txt:66`) + `SUFFIX ".so"` (`:93`) +
|
||||
channel-derived `OUTPUT_NAME` (`:67`) yields `reaper_reasampler.so` /
|
||||
`reaper_reasampler_beta.so`, matching the `reaper_*` convention. `ARCHIVE_OUTPUT_DIRECTORY`
|
||||
(`:69`) is a Windows import-lib concern and harmlessly inert on Linux.
|
||||
- **The channel fork is platform-independent.** `REASAMPLER_CHANNEL` (`CMakeLists.txt:40–54`)
|
||||
threads through `configure_file` (`:58–61`) into names only; nothing in it is
|
||||
Windows-specific, so beta builds fork identically on Linux.
|
||||
- **The version padding canary** (`src/core/version/CMakeLists.txt:21–42`) uses a
|
||||
function-scoped `set()` + second `configure_file` + longhand `add_executable`. Portable;
|
||||
no platform assumption.
|
||||
- **The whole test corpus is platform-neutral.** 91 files in `tests/`, 91 declarations in
|
||||
CMake. Only `test_capture_paths.cpp` branches on platform and it already has POSIX
|
||||
expectations written; only `test_pitch_shift.cpp` mentions `windows.h`, in a comment.
|
||||
- **Generator-shape handling in the docs is already correct.** `README.md:93–98` explains
|
||||
that `-C Debug` is required on multi-config and unnecessary on Ninja/Make. `ctest --test-dir
|
||||
build` works bare on Linux; the `-C Debug` in the README's example is harmless there.
|
||||
- **SWELL API coverage for the LICE slice** — all six SWELL GDI entry points LICE needs are
|
||||
in the modstub-resolved table (`swell-functions.h:857`, `:863`, `:877`, `:885–887`).
|
||||
- **The LICE slice needs no fifth TU on Linux** (`lice.cpp:3040` colorspace table,
|
||||
`lice_textnew.cpp:26` file-static `utf8makechar`).
|
||||
- **`swell-modstub-generic.cpp` compiles against its own includes** — it uses `printf`
|
||||
(`:61`), which arrives via `swell.h` → `swell-types.h:29` (`#include <stdio.h>`). The
|
||||
`dlopen`/`readlink` block (`:69–131`) is behind `SWELL_LOAD_SWELL_DYLIB`, which this build
|
||||
never defines.
|
||||
- **The Linux run-loop IIDs are already in the vendored VST3 slice.**
|
||||
`public.sdk/source/common/commoniids.cpp:50–54` defines `Linux::IEventHandler`,
|
||||
`Linux::ITimerHandler` and `Linux::IRunLoop` under `#if SMTG_OS_LINUX`, and that file is
|
||||
already in the `vst3_sdk` source list (`src/shell/instrument/CMakeLists.txt:36`). A Linux
|
||||
VST3 port needs no additional SDK translation unit for the run loop.
|
||||
- **`SMTG_OS_LINUX` / `SMTG_EXPORT_SYMBOL` are correct in the pinned SDK**
|
||||
(`pluginterfaces/base/fplatform.h:132`, `:164`).
|
||||
- **The MSVC-ABI caveat is Windows-scoped.** `reaper_plugin.h:22` warns that the C++
|
||||
interfaces "require MSVC on win32, or at least the MSVC-compatible C++ ABI" — that is a
|
||||
constraint on Windows toolchains (mingw), not on Linux, where REAPER and the extension both
|
||||
use the Itanium ABI.
|
||||
|
||||
---
|
||||
|
||||
## 4. The VST3-on-Linux question, stated as a fork
|
||||
|
||||
Daniel rules on this. Below is the cost of each side with the evidence, and nothing else.
|
||||
|
||||
### Fork A — extension only on Linux
|
||||
|
||||
Scope: everything in §2 (Λ-01 through Λ-10). The VST3 gate at
|
||||
`src/shell/instrument/CMakeLists.txt:9` stays exactly as it is; a Linux configure omits the
|
||||
target and the `vendor/vst3sdk` submodule becomes optional there (Λ-10).
|
||||
|
||||
**Cost profile:** one Major that must be solved (Λ-01, the panel), two Majors that are
|
||||
one-line-ish but load-bearing (Λ-02 optimization, Λ-03 visibility), one Major/Minor
|
||||
(Λ-04 threads), and six Minors. No new third-party dependency. No new artifact format. No
|
||||
change to any product decision on record.
|
||||
|
||||
**The unknown that dominates the band:** none of the extension's source portability is
|
||||
audited here. The build-system work is small; whether the 39 shell TUs *compile and behave*
|
||||
against SWELL's Win32 subset is T2's answer, and it, not this list, sets the phase's size.
|
||||
|
||||
### Fork B — extension + ReaSampler 9000 on Linux
|
||||
|
||||
Everything in Fork A, plus the following. Each item is a Blocker for the VST3 artifact —
|
||||
without it there is no loadable `.vst3`.
|
||||
|
||||
- **B1 — the entry point is the wrong one, and the right one is already vendored.**
|
||||
`src/shell/instrument/CMakeLists.txt:78` compiles
|
||||
`public.sdk/source/main/dllmain.cpp` into the module. That file does
|
||||
`#include <windows.h>` at `:41` with no `SMTG_OS_*` guard — grepping the file for
|
||||
`SMTG_OS` returns nothing, and its only conditionals are
|
||||
`#if defined(_MSC_VER) && defined(DEVELOPMENT)` (`:43`, `:96`) and `#ifdef __cplusplus`
|
||||
(`:58`, `:87`). Linux needs
|
||||
`public.sdk/source/main/linuxmain.cpp`, which is present in the slice and exports
|
||||
`ModuleEntry`/`ModuleExit` (`linuxmain.cpp:47–52`, `:59–82`). Both are **mandatory** — the
|
||||
SDK's own loader refuses the module without either
|
||||
(`public.sdk/source/vst/hosting/module_linux.cpp:189–204`, error strings "The shared
|
||||
library does not export the required 'ModuleEntry' function" and the same for `ModuleExit`).
|
||||
`GetPluginFactory` is checked at `:205`. **Effort S.**
|
||||
|
||||
- **B2 — the artifact is a directory bundle, not a file.** Today the target sets
|
||||
`SUFFIX ".vst3"` on a MODULE (`src/shell/instrument/CMakeLists.txt:106–111`), producing a
|
||||
single file. `module_linux.cpp:146–170` (`getSOPath`) shows what a Linux host actually
|
||||
opens: the path must be a **directory**, containing `Contents/`, containing
|
||||
`<machine>-linux/` where `<machine>` is `uname().machine` (`:158–163`, helper at `:88–97`),
|
||||
containing `<stem>.so` (`:167–168`). So `reasampler_9000.vst3/Contents/x86_64-linux/reasampler_9000.so`.
|
||||
That is `add_custom_command(POST_BUILD)` work plus an install rule. Install roots are
|
||||
`$HOME/.vst3/`, `/usr/lib/vst3/`, `/usr/local/lib/vst3/` and `$APPFOLDER/vst3/`
|
||||
(`module_linux.cpp:282–302`). `Contents/Resources/moduleinfo.json` is **optional** —
|
||||
`getModuleInfoPath` (`:356–365`) returns empty when the file is absent rather than failing.
|
||||
**Effort M** — mechanical, but it is a new artifact shape with an install story and a
|
||||
per-architecture directory name.
|
||||
|
||||
- **B3 — the drawing stack is the real cost, and it is where Fork B stops being a build task.**
|
||||
The VST3 module compiles the same four LICE TUs (`src/shell/instrument/CMakeLists.txt:80`),
|
||||
and on non-Win32 LICE resolves its GDI through SWELL (§1a). But **a VST3 plugin is not a
|
||||
REAPER extension**: `swell-modstub-generic.cpp`'s default branch exports `SWELL_dllMain`
|
||||
(`:135`), which only REAPER's *extension* loader calls. Nothing hands a VST3 plugin the
|
||||
SWELL function table. That leaves two routes:
|
||||
- **Route B3a — `SWELL_LOAD_SWELL_DYLIB`** (`swell-modstub-generic.cpp:69–131`): the stub
|
||||
reads `/proc/self/exe`, strips to the directory, and `dlopen`s `libSwell.so` beside the
|
||||
host binary (`:81–98`), then calls `SWELL_set_app_main(SWELLAppMain)` (`:112–114`). That
|
||||
reference makes `SWELLAppMain` a **required definition in the plugin** — it is declared at
|
||||
`swell-types.h:137` as "to be implemented by app". Whether REAPER's Linux install ships a
|
||||
`libSwell.so` beside its executable is `[verify — Linux]`, and this route makes the VST3
|
||||
load-bearing on being hosted by REAPER specifically, which is consistent with D5's
|
||||
REAPER-only stance but is a new runtime coupling to state explicitly.
|
||||
- **Route B3b — build SWELL into the module.** `vendor/WDL/WDL/swell/Makefile:100–104`
|
||||
enumerates the 13 SWELL TUs (`swell.o swell-ini.o swell-miscdlg-generic.o
|
||||
swell-wnd-generic.o swell-menu-generic.o swell-kb-generic.o swell-dlg-generic.o
|
||||
swell-gdi-generic.o swell-misc-generic.o swell-gdi-lice.o swell-generic-headless.o
|
||||
swell-generic-gdk.o swell-appstub-generic.o swell-modstub-generic.o`) and `:106–107` the 8
|
||||
LICE TUs it needs alongside. Link requirements at `:90` (`-lpthread -ldl`), `:128–130`
|
||||
(pkg-config `gtk+-3.0` or `gdk-3.0`), `:145–151` (`freetype2` + `-lfontconfig`) and
|
||||
`:154` (`-lGL`).
|
||||
**This is a wholly new third-party dependency surface** — GDK/GTK3, FreeType, Fontconfig,
|
||||
OpenGL — that the extension deliberately avoids via `SWELL_PROVIDED_BY_APP`. Flagging it
|
||||
as a scope expansion, per the dispatch's constraint; it is not something to slip in.
|
||||
**Effort L either way**, and B3b is the larger of the two by a wide margin.
|
||||
|
||||
- **B4 — the editor's platform contract changes.** The view must report
|
||||
`kPlatformTypeX11EmbedWindowID` (`pluginterfaces/gui/iplugview.h:79`) instead of
|
||||
`kPlatformTypeHWND` (`:62`), and timers/file-descriptors must go through
|
||||
`Steinberg::Linux::IRunLoop` (`iplugview.h:267–279`) with `IEventHandler` (`:223–229`) and
|
||||
`ITimerHandler` (`:239–245`) — the header states the reason at `:255–256`: "On Linux the
|
||||
host has to provide this interface to the plug-in as there's no global event run loop
|
||||
defined as on other platforms." The whole `namespace Linux` block is `#if SMTG_OS_LINUX`
|
||||
(`:209`, closed `:284`), so it compiles in automatically. The **build-side** cost is nil
|
||||
(the IIDs are already vendored — see §3); the **source-side** cost is `editor_platform.cpp`
|
||||
and every timer in the editor, which is T2's surface. **Effort: build S, source L.**
|
||||
|
||||
- **B5 — a settled product decision has to be reversed, in writing.** D5 ("Windows-only,
|
||||
VST3-only, REAPER-only") is recorded as a locked decision with "a mac/Linux build" named
|
||||
as a non-goal in `src/core/instrument/CLAUDE.md` (Invariants → the three locked decisions;
|
||||
and again under Non-goals/guardrails) and `src/shell/instrument/CLAUDE.md`
|
||||
(Non-goals/guardrails). `src/shell/panel/CLAUDE.md` carries a third dependent statement.
|
||||
Fork B is not just more work — it invalidates three recorded invariants and needs Daniel's
|
||||
explicit reversal before any code moves.
|
||||
|
||||
**The asymmetry worth naming.** Fork A's build work is small and its unknown is source
|
||||
portability. Fork B's build work is genuinely large (B2, B3) *and* carries an unbounded
|
||||
source-portability tail (B3b's dependency surface, B4's window/run-loop rewrite) *and*
|
||||
requires reversing a settled decision. The two are not "the same phase, one bigger" — they
|
||||
are different-shaped commitments.
|
||||
|
||||
---
|
||||
|
||||
## 5. Open questions
|
||||
|
||||
### `[verify — Linux]` — answerable by trying it on a Linux box
|
||||
|
||||
| # | Question | Exact check |
|
||||
|---|---|---|
|
||||
| V1 | Does the extension compile at all under GCC/Clang? | `cmake -B build -S . -G Ninja && cmake --build build 2>&1 \| tee build.log` — this is the single highest-information action available and gates most of T2's list too. |
|
||||
| V2 | Does `ctest` pass all 91 targets? | `cmake -B build -S . -G Ninja && cmake --build build && ctest --test-dir build --output-on-failure` (no `-C` needed on Ninja). |
|
||||
| V3 | Does REAPER's Linux build call `SWELL_dllMain` in a `reaper_*.so`, populating the API table? | Drop the built `.so` in `~/.config/REAPER/UserPlugins/`, restart, and check the extension's actions appear. A null table shows as immediate crash or dead actions. |
|
||||
| V4 | Where exactly is `UserPlugins/` on Linux, and is `reaper_*.so` the right filename glob? | Options → Show REAPER resource path in a Linux REAPER. The SDK headers do not document either — grep of `vendor/reaper-sdk/sdk/` for `UserPlugins` and `reaper_*.so` returns nothing. |
|
||||
| V5 | Does `panel_state.h:23`'s `<pthread.h>` actually require a link flag on the target glibc? | Build without `Threads::Threads`; if it links, it does not. |
|
||||
| V6 | Does `-fvisibility=hidden` leave `ReaperPluginEntry` and `SWELL_dllMain` exported? | `nm -D --defined-only reaper_reasampler.so \| grep -E 'ReaperPluginEntry\|SWELL_dllMain'` after adding the preset. |
|
||||
| V7 | With Λ-01 fixed by the resource-id-0 route, does `SWELL_CreateDialog` return a usable child HWND for the docker? | Panel toggle action in a Linux REAPER. |
|
||||
| V8 | Does `std::filesystem` need `-lstdc++fs` on the chosen toolchain? | Build `capture_paths_tests` alone; the link error names it. |
|
||||
| V9 | Does REAPER's Linux install ship a `libSwell.so` beside its executable (Fork B, route B3a)? | `ls $(dirname $(readlink -f $(which reaper)))/libSwell.so`. |
|
||||
| V10 | Which `uname -m` values must the VST3 bundle carry (Fork B, B2)? | `uname -m` on each target — REAPER ships x86_64, aarch64 and armv7l Linux builds, and `module_linux.cpp:158–163` keys the bundle directory off exactly this string. |
|
||||
|
||||
### `[Daniel]` — genuine product/scope calls
|
||||
|
||||
| # | Question | Why it is not answerable by reading code |
|
||||
|---|---|---|
|
||||
| D1 | **Fork A or Fork B?** Extension only, or extension + ReaSampler 9000 on Linux? | §4 states both costs. The choice is scope, and D5 makes it a reversal of a recorded decision, not a technical lookup. |
|
||||
| D2 | If Fork B: **route B3a (dlopen the host's `libSwell.so`, requires defining `SWELLAppMain`) or B3b (vendor and build full SWELL, adds GDK/GTK3 + FreeType + Fontconfig + OpenGL)?** | B3b is a dependency-surface expansion; the dispatch requires that be flagged rather than chosen. B3a is cheaper but couples the plugin's UI to being hosted by REAPER specifically. |
|
||||
| D3 | Is **macOS in or out** of the same phase? | Λ-01 is a *shared* macOS/Linux fix (both branches carry the identical broken comment block, `src/app/CMakeLists.txt:83–86` vs `:94–97`), and `swell_resgen.php` output is shared per `README.md:120`. Doing both at once is cheaper than doing them serially — but that is a scope call, and macOS additionally implies signing/notarization, which `versioning-and-release.md` does not mention anywhere. |
|
||||
| D4 | Does **CI get built in this phase**, and on what runner? | `versioning-and-release.md:307–311` and `:361–363` hand "the pipeline" to dev-ops and commit to three platform artifacts per channel, but no CI exists in the repo on any platform. Adding it is infrastructure and needs an explicit ask. |
|
||||
| D5 | Is the Linux artifact **shipped or developer-only** for 1.x? | Determines whether Λ-02 (Release build) and Λ-08 (install path) are must-fix or nice-to-have, and whether a Linux `install()` rule is worth authoring. |
|
||||
|
||||
Deliberately **not** listed as `[Daniel]`, because reading more would settle them: whether
|
||||
`arrange_drop_win.cpp`/`instrument_drop_win.cpp` need platform gating (read the files — T2);
|
||||
whether the SDK's `moduleinfo.json` is required (it is not — `module_linux.cpp:362–364`);
|
||||
whether the Linux run-loop IIDs need a new SDK TU (they do not — `commoniids.cpp:50–54`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Ordering sketch
|
||||
|
||||
Dependency order only — this is sequencing input for a plan author, not the plan.
|
||||
|
||||
**Stage 0 — the one action that reprices everything.** Run V1 and V2 on a Linux box against
|
||||
the tree as-is. Configure will succeed (nothing in the CMake requires Windows); the compile
|
||||
will produce a diagnostic set that is the actual size of this phase. Every band below is
|
||||
provisional until this is done. Λ-10's doc fix rides along free, since the person doing this
|
||||
will hit it first.
|
||||
|
||||
**Stage 1 — toolchain floor, no source dependency.** Λ-02 (`CMAKE_BUILD_TYPE` default),
|
||||
Λ-03 (visibility presets), Λ-04 (`Threads::Threads`), Λ-09 (warning flags + `CXX_EXTENSIONS
|
||||
OFF`), Λ-05 (pin the `WDL_INC` coincidence), Λ-07 (compiler floor). All are root- or
|
||||
target-property edits with no ordering constraint among them and none touching source. Doing
|
||||
them *before* Stage 2 means the resource work is done under warnings rather than retrofitted
|
||||
into them. Ship as one change.
|
||||
|
||||
**Stage 2 — the panel resource decision.** Λ-01. This forks on the resource-id-0 question
|
||||
(§2, Λ-01) and therefore needs T2's read on `panel_window.cpp` first — so it is gated on T2
|
||||
output, not on Stage 1. Whichever route wins, it is a shared macOS/Linux fix (see D3).
|
||||
|
||||
**Stage 3 — source partition.** Λ-06. Once T2 names which TUs need a platform branch or a
|
||||
non-Windows sibling, the source list in `src/app/CMakeLists.txt:8–51` gains its
|
||||
partition. Strictly after T2 and after Stage 2 (the resgen/shim TU, if that route is taken,
|
||||
is one of the entries).
|
||||
|
||||
**Stage 4 — packaging.** Λ-08, plus whatever D5 decides. Depends on Stage 1 (there is no
|
||||
point documenting an install path for an unoptimized artifact) and on V4.
|
||||
|
||||
**Stage 5 — Fork B, only if D1 says so.** Internally ordered: B1 (entry point swap, S) →
|
||||
B3 (SWELL strategy per D2 — this gates everything downstream, because a module that cannot
|
||||
draw cannot be tested) → B2 (bundle layout + install, needs V10) → B4 (X11 embed + run loop,
|
||||
mostly T2's source work). B5 (the D5 reversal) precedes all of them as a documentation act.
|
||||
|
||||
---
|
||||
|
||||
## 7. Referred to T2
|
||||
|
||||
Source-level items I noticed while reading build files. **Recorded, not analyzed** — the
|
||||
parallel track owns each of these and I have deliberately not investigated further.
|
||||
|
||||
1. `src/shell/panel/panel_window.cpp:135` — `CreateDialogParam` + `MAKEINTRESOURCE(IDD_BANK_PANEL)`.
|
||||
Under SWELL this is `SWELL_CreateDialog(SWELL_curmodule_dialogresource_head, …)`. Relevant
|
||||
to Λ-01's resource-id-0 option, which requires the proc to be a `WNDPROC` returning
|
||||
`LRESULT`, cast to `DLGPROC` (`swell-functions.h:606–608`).
|
||||
2. `src/shell/actions/arrange_drop_win.cpp` and `src/shell/actions/instrument_drop_win.cpp` —
|
||||
zero `_WIN32` occurrences, no `<windows.h>`, yet `_win`-suffixed. Confirm they are
|
||||
genuinely portable, or add guards.
|
||||
3. `src/shell/actions/drag_out_win.cpp:249–282` — the `#else` "macOS / Linux (SWELL)" branch
|
||||
exists but has never been compiled. Whether the SWELL drag path is functionally equivalent
|
||||
to the Win32 `IDataObject`/`IDropSource` path is unaudited.
|
||||
4. `src/shell/persist/prune_fs.cpp:36` — `<windows.h>` behind two `_WIN32` guards. Confirm the
|
||||
non-Windows deletion path exists and is equivalent (this file is on the only
|
||||
file-deletion path in the product, per `src/core/reclaim/CLAUDE.md`).
|
||||
5. `src/shell/panel/draw_kit.cpp:11–15`, `draw_kit.h:27–29`, `src/shell/panel/panel_state.h:18–26` —
|
||||
the `windows.h`-vs-`swell/swell.h` include switches. These look correct; whether the
|
||||
SWELL subset actually covers every GDI call the kit makes is the question.
|
||||
6. `src/shell/panel/CLAUDE.md` records "Windows-only (D5) — font/GDI/HFONT choices assume
|
||||
Windows; no cross-platform font-fallback concern" as an invariant. Font selection on
|
||||
Linux SWELL (FreeType/Fontconfig-backed) is the concrete instance.
|
||||
7. `src/core/capture/capture_paths.cpp` — `normalizeSlashes` lowercases on `_WIN32` only, and
|
||||
`tests/test_capture_paths.cpp:19–46` already encodes the case-sensitive POSIX contract.
|
||||
The test is written; the behaviour under a case-sensitive filesystem is unverified.
|
||||
8. `src/shell/instrument/reasampler_editor.h:30` — unguarded `#include <windows.h>`. Only
|
||||
matters under Fork B.
|
||||
Reference in New Issue
Block a user