subagent transcript
session 17f09f89
delegated task
Research how to cross-compile VTK 9.3.x (data-model modules only, NO rendering) to WebAssembly with Emscripten, and produce a concrete blocker+fix lis
▸ Magik
Research how to cross-compile VTK 9.3.x (data-model modules only, NO rendering) to WebAssembly with Emscripten, and produce a concrete blocker+fix list. This is for a real build in progress: VTK-9.3.1 configured with emcmake/Ninja, static libs, `-DVTK_GROUP_ENABLE_Rendering=DONT_WANT`, `-DVTK_SMP_IMPLEMENTATION_TYPE=Sequential`, `-DVTK_ENABLE_WRAPPING=OFF`, `-DVTK_WRAP_PYTHON=OFF`, modules CommonCore/CommonDataModel/CommonExecutionModel/CommonMath/Filters*/IOXML/IOLegacy, compiled with `-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0` (native wasm exceptions), single-threaded (no pthreads). Emscripten 4.0.12 / clang 22.
Use WebSearch/WebFetch. Investigate and report CONCRETELY (with the exact CMake var, source file, or flag):
1. **CMake configure-time TRY_RUN / cross-compile checks**: VTK runs some `try_run` feature checks (e.g. `VTK_TEST_...`, endianness, `KWSYS_...`) that can't execute a wasm binary during configure. Which ones block an emscripten configure of the Common/Filters/IO modules, and how are they pre-seeded (the `..._EXITCODE`/`..._RUN_RESULT` cache vars, or emscripten node as `CMAKE_CROSSCOMPILING_EMULATOR`)? Does emcmake set the node emulator automatically so try_run works?
2. **Threading / vtkSMPTools**: with `VTK_SMP_IMPLEMENTATION_TYPE=Sequential`, are there residual `std::thread`/`std::mutex`/`std::atomic` uses in CommonCore (vtkSMPThreadLocal, vtkAtomic, vtkThreadedCallbackQueue, vtkConditionVariable) that break a `-sUSE_PTHREADS=0` single-threaded build? Which files/flags disable them (`VTK_USE_THREADS`?, `VTK_ENABLE_KOKKOS`?).
3. **vtkThreadedCallbackQueue / vtkConditionVariable**: known to reference pthread/std::thread. Is there a CMake option to exclude them, or do they need patching under emscripten?
4. **IO / filesystem / disk**: do IOXML/IOCore/IOLegacy have any host-FS assumptions that break MEMFS? (probably fine, but flag anything.)
5. **Prior art**: has anyone published a working emscripten VTK build (Kitware's `VTK::wasm`, the `vtk-wasm` npm, trame/vtk.js-adjacent C++ builds, FreeCAD or ParaViewWeb, OpenFOAM-wasm, or GitHub issues on gitlab.kitware.com)? Find the SPECIFIC CMake flag set they used for a data-model / no-rendering wasm build, and any patch commits. VTK added an official `VTK_WEBASSEMBLY_*` / emscripten path — which version, and what does it enable/require?
6. **Exceptions/RTTI**: does VTK require `-frtti` (it does use dynamic_cast heavily)? Any conflict with `-fwasm-exceptions`? Any `-sDISABLE_EXCEPTION_CATCHING` implications?
7. **setjmp/longjmp**: VTK's `vtkImageData`/png/jpeg (we exclude those) use libjpeg longjmp — since we exclude IO image modules, is the data-model subset longjmp-free? Flag any longjmp in the enabled modules.
Return a prioritized, deduplicated list: each item = {area, the exact blocker, the exact fix (CMake var / flag / file patch), confidence, source URL}. Focus on what will actually break the Common/Filters/IO build — skip rendering/GL issues entirely (we don't build those). Do NOT modify any files or run builds — this is research only.
Fable
thinking
I need to dig into this properly by checking what VTK resources are available locally and searching for concrete information about WebAssembly cross-compilation. Let me start by exploring the filesystem and running some web searches in parallel to gather the details I need.
Fable
I'll research this thoroughly. Let me start by loading the web tools and checking if VTK source is available locally.
Fable
⚙ ToolSearch {"query": "select:WebSearch,WebFetch", "max_results": 5}
Fable
⚙ Bash {"command": "find / -maxdepth 6 -type d -iname 'VTK*' 2>/dev/null | head -50; echo \"---SMP---\"; find / -path '*Common/Core/vtkSMPTools*' 2>/dev/null | head; echo \"---VERSION---\"; find / -name 'vtkVersion.h' 2>/dev/null | head", "descrip…
Fable
thinking
I've found a local VTK source tree at `/opt/toolchains/src/VTK-9.3.1` with an existing `vtk-wasm-build` directory, which means I can examine the actual source files and see what build configuration was already used. Let me check the wasm build directory first to understand the prior setup, then dig into the SMPTools implementation to understand what needs to be configured.
Fable
Excellent — there's a local VTK-9.3.1 source tree AND a `vtk-wasm-build` directory at `/opt/toolchains/src/`. This is gold for concrete answers. Let me investigate the existing build config and source in parallel with web research.
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"===== ls build dir =====\"; ls -la $D 2>/dev/null | head -40\necho \"===== CMakeCache exists? =====\"; ls -la $D/CMakeCache.txt 2>/dev/null\necho \"===== grep key cache vars =====\";…
Fable
⚙ WebSearch {"query": "VTK WebAssembly Emscripten build data model no rendering CMake flags VTK_GROUP_ENABLE_Rendering"}
Fable
⚙ WebSearch {"query": "VTK Emscripten cross-compile try_run TRY_RUN_RESULT cache variable CMAKE_CROSSCOMPILING_EMULATOR node"}
Fable
thinking
The build directory seems to exist but CMakeCache.txt isn't showing up at the top level, so I'm going to dig deeper into the source files and continue investigating the vtk-wasm-build setup. I'm also noticing that `.zshrc` error message is just noise. Let me focus on examining how `try_run` works in C to understand what's happening here.
Now I'm looking at several threading and synchronization mechanisms in VTK — the SMP tools implementations, callback queues, condition variables, atomic operations, and how std::thread and mutex are used throughout Common/Core. I should also check for setjmp/longjmp usage in the enabled modules since those could be problematic for WebAssembly. The official …
Fable
The build directory exists but I don't see a top-level CMakeCache yet. Let me dig deeper into the build dir and the actual source files, plus fetch the official VTK wasm doc.
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"===== find CMakeCache anywhere =====\"; find $D -name 'CMakeCache.txt' 2>/dev/null\necho \"===== find any .cmake config / build.ninja =====\"; find $D -maxdepth 2 -name 'build.ninja'…
Fable
⚙ WebFetch {"url": "https://github.com/Kitware/VTK/blob/master/Documentation/docs/advanced/build_wasm_emscripten.md", "prompt": "Extract the exact CMake configure flags and toolchain settings recommended for building VTK for WebAssembly with Emscripte…
Fable
thinking
I've got the CMake build artifacts here — the cache file, configure log, and error log all exist, which means the configuration actually completed successfully. The build.ninja file confirms that too. Let me dig into those logs to see what was configured and what issues came up during the process.
Fable
There's a live, in-progress build with a real `CMakeCache.txt`, `CMakeError.log`, and `CMakeConfigureLog.yaml` (timestamps show it configured minutes ago). This is the actual build the task refers to. Let me extract the real config and errors.
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"############ RELEVANT CMakeCache VARS ############\"\ngrep -iE 'CMAKE_CROSSCOMPILING|CROSSCOMPILING_EMULATOR|_EXITCODE|_RUN_RESULT|:INTERNAL=|WASM|EXCEPTION|SMP_IMPL|USE_THREADS|ENAB…
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"############ CMakeError.log ############\"\ncat $D/CMakeFiles/CMakeError.log 2>/dev/null | head -200", "description": "Read CMake configure error log"}
Fable
thinking
The CMakeError.log failures are all expected platform-detection checks that are supposed to fail on certain systems — they're not actual problems, just feature flags being set to false. The key finding appears to be related to CMAKE_CROSSCOMPIL, though the thought cuts off there.
Looking at the cache variables and configuration output, the Emscripten toolchain is properly set up with node as the crosscompiling emulator, which means try_run should work. The configure step actually succeeded — build.ninja was generated and 58 makefiles were created. So the real issue must be happening during the actual compilation or linking phase. I need to dig into the threading-related source files, check …
Fable
The CMakeError.log entries are all benign feature-detection `try_compile` failures (environ, st_mtimespec, ext/stdio_filebuf.h, __int64) — expected to fail on non-matching platforms; they just set feature vars false. Critically, the cache confirms `CMAKE_CROSSCOMPILING_EMULATOR=.../node` is set automatically by Emscripten.cmake, and `build.ninja` was generated, so configure succeeded. Now let me inspect the threading source and the rest of the config.
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"############ SMP / THREAD / MODULE cache vars ############\"\ngrep -iE 'SMP|THREAD|KOKKOS|_ENABLE:|ATOMIC|PTHREAD|VTK_USE|VTK_MODULE_ENABLE_VTK_(Common|Filters|IO)|_RUN_RESULT|_EXITC…
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"############ vtkSMPTools backends present ############\"\nls $S/Common/Core/SMP/ 2>/dev/null\nls $S/Common/Core/SMP/Sequential 2>/dev/null\necho\necho \"############ std::thread / std::mu…
Fable
thinking
Looking at the build configuration, I can see the enabled modules are organized across several categories: the Common modules handle geometry, core functionality, data models, execution, math, and system operations; the Filters modules cover extraction, general filtering, geometry, modeling, sources, and statistics; and the IO modules manage core I/O, legacy formats, and XML parsing. The SMP backends are about to be listed next. vtkOutputWindow and vtkStringManager both use std::mutex which compile fine, and I'm checking whether vtkThreadedCallbackQueue and vtkConditionVariable are actually present in the codebase. The real question is how emscripten handles pthread_create in single-threaded …
Fable
Now I have the exact enabled module set and the threading files. Let me drill into the specific threading classes, the pthread config resolution, setjmp/longjmp, and whether the build has actually started compiling (existing error output).
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1; D=/opt/toolchains/src/vtk-wasm-build\necho \"############ vtkThreadedCallbackQueue / vtkConditionVariable presence ############\"\nls -la $S/Common/Core/vtkThreadedCallbackQueue* $S/Common/Core/…
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"############ setjmp/longjmp in ENABLED modules only ############\"\nfor m in Common/Core Common/DataModel Common/ExecutionModel Common/Math Common/Misc Common/System Common/Transforms Com…
Fable
thinking
I'm seeing that vtkThreads.h defines VTK_USE_PTHREADS even in the emscripten build, which means vtkMultiThreader compiles with pthread support. This happens because emscripten's libc provides pthread symbols and CMake found threading available, so pthread_create gets called even in single-threaded emscripten builds without the -pthread flag.
The key distinction is that this is a runtime issue, not a compile issue. Without -pthread, emscripten provides stub pthread implementations where pthread_create returns EAGAIN and fails at runtime. The code still compiles and links fine because the headers and stub symbols are available, but when pthread_create actually runs and fails, vtkMultiThreader …
Fable
Key findings so far: `vtkThreads.h` has `#define VTK_USE_PTHREADS` (emscripten's libc advertises pthread), SMP backend is Sequential (but `VTK_SMP_ENABLE_STDTHREAD 1`), `vtkThreadedCallbackQueue.cxx` IS compiled into CommonCore, and no setjmp/longjmp in first-party enabled modules. Let me confirm the threading source details, third-party setjmp, try_run behavior, and check whether the build has produced any compile errors yet.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"############ vtkThreadedCallbackQueue.h threading primitives ############\"\ngrep -nE '#include <(thread|mutex|condition_variable|atomic|future)>|std::thread|std::condition_variable|std::…
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"############ thirdparty dirs present ############\"\nls $S/ThirdParty/ | tr '\\n' ' '; echo\necho \"############ setjmp/longjmp across ALL thirdparty that could be linked by IO subset ###…
Fable
thinking
I'm discovering that vtkMultiThreader in VTK 9.3.1 already has proper Emscripten guards around pthread_create, so it safely falls back to single-threaded execution on that platform—this resolves a major compatibility concern.
I'm also finding that vtkConditionVariable doesn't exist in this version (it was removed earlier), and the IO subset dependencies like expat, zlib, and others are all free of setjmp/longjmp calls, which is good news for Emscripten compatibility.
The real question now is whether vtkThreadedCallbackQueue has similar Emscripten protections or if it might still try to spawn std::thread without guards—I need to examine its source directly to see if it compiles cleanly in s …
Fable
Critical finding: `vtkMultiThreader.cxx` already guards `pthread_create` with `#if !defined(__CYGWIN__) && !defined(__EMSCRIPTEN__)` — so it's emscripten-safe in 9.3.1. Also `vtkConditionVariable` does NOT exist in 9.3.1, and third-party expat/zlib/etc. are setjmp-free. The remaining unknown is `vtkThreadedCallbackQueue`, which has no emscripten guard. Let me read it and check the try_run configure log.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"############ vtkThreadedCallbackQueue.h : all includes + thread usage ############\"\ngrep -nE '#include|std::thread|std::atomic|std::mutex|std::condition|std::unique_lock|Threads|__EMSCR…
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"############ try_run / TestBigEndian / RUN_RESULT in ConfigureLog ############\"\ngrep -inE 'try_run|TRY_RUN|TestBigEndian|BIG_ENDIAN|RUN_RESULT|crosscompiling_emulator|checkTypeSize…
Fable
thinking
The grep for the header file came up empty, which is strange. Let me read the file directly to see what's actually in it—there might be a path issue or the includes might be formatted differently than expected. I also spotted a try_run call at line 95 in CheckSourceRuns.cmake that shows up in the ConfigureLog around line 1420, so I should investigate what that's attempting to do.
Looking at the endianness resolution, vtkEndian.h correctly shows little-endian for WebAssembly, which CMake determined without needing a cross-compile try_run since modern CMake uses compile-time checks. The fact that there are no _RUN_RESULT or _EXITCODE cache entries suggests the node emulator successfully ran a …
Fable
Let me read `vtkThreadedCallbackQueue` directly (the grep came back empty, which is suspicious) and inspect the one `try_run` in the configure log, plus kick off prior-art web research.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"===== HEAD of vtkThreadedCallbackQueue.h =====\"; sed -n '1,60p' $S/Common/Core/vtkThreadedCallbackQueue.h 2>/dev/null\necho \"===== grep thread-ish in both files (fixed strings) =====\"\…
Fable
⚙ Bash {"command": "D=/opt/toolchains/src/vtk-wasm-build\necho \"===== try_run region of CMakeConfigureLog.yaml (lines 1400-1480) =====\"\nsed -n '1400,1490p' $D/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null", "description": "Read the try_run entr…
Fable
⚙ WebSearch {"query": "emscripten std::thread without -pthread USE_PTHREADS=0 link error stub pthread_create"}
Fable
thinking
I found a critical issue — the VTK build is failing during the large file support check in CMakeLists.txt, where the C++ source compilation and linking succeeded but something downstream is blocking the build process.
The root cause is an exception handling mismatch: the test binary was compiled with `-fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0`, which generates the new exnref opcode (0x1f), but the bundled Node.js (22.16.0) in emsdk doesn't enable `--experimental-wasm-exnref` by default. When Node tries to instantiate the test binary as the cross-compilation emulator, it fails with a WebAssembly compilation error because it doesn't recognize the opcode. This causes the try_run to exit wit …
Fable
Major finding in the configure log: the one `try_run` VTK performs — **`VTK_REQUIRE_LARGE_FILE_SUPPORT`** (VTK top `CMakeLists.txt:130` via `check_cxx_source_runs`) — **compiled+linked fine but FAILED TO RUN under node**, with `Invalid opcode 0x1f (enable with --experimental-wasm-exnref)`. That's the exnref/legacy-EH mismatch: `-sWASM_LEGACY_EXCEPTIONS=0` emits new exnref EH, and emsdk's bundled node 22.16 doesn't enable it. So the try_run returns exit code 1 (bogus "failed"). Let me locate `vtkThreadedCallbackQueue` (zsh nullglob aborted my earlier greps) and count all try_runs.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1; D=/opt/toolchains/src/vtk-wasm-build\necho \"===== real location of vtkThreadedCallbackQueue =====\"; find $S -name 'vtkThreadedCallbackQueue*' 2>/dev/null\necho \"===== total try_run entries in…
Fable
thinking
So `vtkThreadedCallbackQueue` is actually in the Parallel/Core module, not Common/Core—that's the `VTK::ParallelCore` module. Looking at the enabled modules in this build, ParallelCore isn't directly included, and while some of the Filters modules that depend on it could pull it in, those particular ones aren't enabled either.
But here's the thing: build.ninja actually references `vtkThreadedCallbackQueue.cxx.o` three times, which means ParallelCore is being compiled. My earlier grep pattern only searched for `vtk(Common|Filters|IO)` modules, so it wouldn't have caught ParallelCore. I need to verify whether ParallelCore is actually enabled and figure out what's pulling it in as a dependency …
Fable
`vtkThreadedCallbackQueue` lives in **Parallel/Core** (VTK::ParallelCore), and `build.ninja` compiles it — so ParallelCore got pulled in as a dependency. And there is exactly ONE try_run in the whole configure (`VTK_REQUIRE_LARGE_FILE_SUPPORT`). Let me read the ThreadedCallbackQueue threading, confirm why ParallelCore is enabled, and check the LFS handling detail.
Fable
⚙ Read {"file_path": "/opt/toolchains/src/VTK-9.3.1/Parallel/Core/vtkThreadedCallbackQueue.h", "limit": 55}
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1; D=/opt/toolchains/src/vtk-wasm-build\necho \"===== thread spawn in vtkThreadedCallbackQueue.cxx =====\"\ngrep -nF -e 'std::thread' -e 'emplace_back' -e '.join' -e 'hardware_concurrency' -e '__EM…
Fable
thinking
I'm tracing through the dependency chain to figure out what's actually pulling in ParallelCore. It depends on CommonCore, but something else must be enabling it. I need to check which of the enabled Filters might be bringing it in as a dependency. I'm not sure which enabled module pulls in ParallelCore, but that's less important than the actual issue: vtkThreadedCallbackQueue uses std::thread without any emscripten guards, and while it compiles and links under single-threaded emscripten (thanks to pthread stub symbols), the real question is whether it actually runs correctly in that environment.
The std::thread constructor calls pthread_create, which in the stub returns EAGAIN and causes st …
Fable
Now let me nail down the remaining questions: the LFS result handling (Q1 consequence), RTTI defaults (Q6), and prior-art web research (Q5) plus confirming the std::thread stub link behavior.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1; D=/opt/toolchains/src/vtk-wasm-build\necho \"===== CMakeLists.txt lines 120-160 (LFS handling) =====\"\nsed -n '120,160p' $S/CMakeLists.txt 2>/dev/null\necho \"===== how VTK_REQUIRE_LARGE_FILE_S…
Fable
⚙ WebFetch {"url": "https://gitlab.kitware.com/vtk/vtk/-/issues/17986", "prompt": "Summarize the concrete Emscripten/WebAssembly build blockers and fixes discussed. Extract any exact CMake flags, cache variables that had to be pre-seeded for try_run c…
Fable
⚙ WebSearch {"query": "VTK wasm official WebAssembly support version 9.4 emscripten ParallelCore vtkThreadedCallbackQueue single thread pthread"}
Fable
thinking
I'm finding that VTK itself handles cross-compilation for mobile devices by pre-setting cache variables like `VTK_REQUIRE_LARGE_FILE_SUPPORT` to bypass the normal detection process when building across platforms. So pre-seeding the cache variable avoids the try_run entirely, which is exactly what VTK's mobile toolchain does. The current build ran it anyway and got a false result (exit code 1), disabling LFS, but that's benign for WebAssembly since emscripten handles 64-bit file offsets natively. The real takeaway is this is the canonical cross-compile try_run problem, and the fix is straightforward: pre-seed the variable as documented. Now looking at RTTI support in VTK...
emscripten enable …
Fable
Rich findings. Notably `CMake/vtkMobileDevices.cmake:4` shows VTK's OWN cross-compile pattern: it force-pre-seeds `VTK_REQUIRE_LARGE_FILE_SUPPORT` in the cache — the exact fix for the try_run. And VTK only forces `-qrtti` for IBM XL (no `-fno-rtti`), so RTTI stays on by default. Let me finalize Q4 (IO/FS assumptions), Q5 (official version + exact flags), and confirm the std::thread link behavior.
Fable
⚙ Bash {"command": "S=/opt/toolchains/src/VTK-9.3.1\necho \"===== IO host-FS assumptions in enabled IO modules (fopen/getenv/tmpnam/stat/mkdir/realpath) =====\"\ngrep -rnoE 'getenv|tmpnam|mkstemp|realpath|::stat\\(|mkdir|chdir|popen|system\\(' $S/…
Fable
⚙ WebFetch {"url": "https://raw.githubusercontent.com/Kitware/VTK/master/Documentation/docs/advanced/build_wasm_emscripten.md", "prompt": "List the EXACT emcmake cmake command and every -D CMake flag it uses for a VTK WebAssembly build. Include VTK_EN…
Fable
thinking
The IO modules are safe for MEMFS since they rely on vtksys::SystemTools and standard C++ streams rather than direct system calls like getenv, fopen, or stat. This means the IOXML, IOCore, and IOLegacy modules don't make any assumptions about the host filesystem and should work correctly in a memory-based environment.
ParallelCore gets built as part of the StandAlone group since the user left the default group settings unchanged, which means vtkThreadedCallbackQueue will be compiled. However, since nothing in the enabled modules actually depends on it, the user could explicitly disable it with `-DVTK_MODULE_ENABLE_VTK_ParallelCore=DONT_WANT` to remove it from the build entirely without brea …
Fable
IO modules use only `vtksys::SystemTools` (MEMFS-portable) — no raw getenv/fopen/tmpnam/realpath. ParallelCore is pulled in via the StandAlone group (nothing enabled hard-depends on it), so it's safely disableable. Let me pin the official-support version and confirm the node exnref/exception details.
Fable
⚙ WebFetch {"url": "https://www.kitware.com/introducing-webassembly-support-in-vtk/", "prompt": "Which VTK version first introduced official WebAssembly/Emscripten support, and what year? What CMake infrastructure (toolchain, VTK_WEBASSEMBLY_* variabl…
Fable
⚙ WebSearch {"query": "emscripten -fwasm-exceptions WASM_LEGACY_EXCEPTIONS=0 exnref node --experimental-wasm-exnref V8 invalid opcode 0x1f"}
Fable
thinking
So the `try_table` instruction (opcode 0x1f) is what the modern wasm-EH implementation uses, and Node 22.16 without the `--experimental-wasm-exnref` flag can't handle it—that's why the LFS try_run executed but exited with code 1. The Kitware blog post from August 2025 mentions they reworked the CMake support to use the Emscripten SDK directly, which is the modern VTK 9.4+ approach. For a data-model-only build, the user's strategy is sound since there's no official recipe published; the key lever is `GROUP_ENABLE_Rendering=DONT_WANT`, which they're already using.
I'm confident about the `DISABLE_EXCEPTION_CATCHING` behavior—it only affects the JS/Emscripten exception handling (`-fexceptions` …
Fable
I have everything I need — the live build tree gave authoritative, file-level evidence, and web sources confirm the general behavior. Here is the prioritized, deduplicated blocker+fix list.
---
## VTK 9.3.1 → WASM (data-model subset): blocker + fix list
Headline: I inspected the actual in-progress build at `/opt/toolchains/src/vtk-wasm-build`. **Configure already succeeded** (`build.ninja` generated, 58 makefiles, node auto-set as the crosscompile emulator). Almost every classic VTK-wasm hazard is already handled in 9.3.1 for exactly this module subset. There is essentially **one real configure quirk** and **one latent runtime concern**; the rest are non-issues I can rule out concretely.
Evidence base (all confirmed on disk): enabled modules are Common{Core,DataModel,ExecutionModel,Math,Misc,System,Transforms,ComputationalGeometry}, Filters{Core,Extraction,General,Geometry,HyperTree,Modeling,Sources,Statistics,Verdict}, IO{Core,Legacy,XML,XMLParser}, **plus VTK::ParallelCore** (pulled in by the StandAlone group). Flags in cache: `CMAKE_CXX_FLAGS = -fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0`, `BUILD_SHARED_LIBS=OFF`, toolchain `Emscripten.cmake`, `VTK_SMP_Sequential`.
---
### P1 — try_run mis-runs under node because new-EH (exnref) binaries won't instantiate
- **Area:** CMake configure-time try_run / cross-compile.
- **Blocker:** VTK performs exactly **one** `try_run` in this module subset: `VTK_REQUIRE_LARGE_FILE_SUPPORT` at `/opt/toolchains/src/VTK-9.3.1/CMakeLists.txt:130` (`check_cxx_source_runs`, source `CMake/vtkRequireLargeFilesSupport.cxx`). It **compiles+links fine** but **fails to execute** under the emsdk node emulator. Configure log shows: `runResult VTK_REQUIRE_LARGE_FILE_SUPPORT_EXITCODE ... stderr: Invalid opcode 0x1f (enable with --experimental-wasm-exnref)`. Cache now holds `VTK_REQUIRE_LARGE_FILE_SUPPORT_EXITCODE:INTERNAL=1` and `VTK_REQUIRE_LARGE_FILE_SUPPORT:INTERNAL=` (empty/false). Root cause is your own flags: `-sWASM_LEGACY_EXCEPTIONS=0` emits the new `try_table`/exnref EH (opcode `0x1f`), and the bundled node 22.16 doesn't enable exnref by default, so the emulator aborts on instantiate. So the feature check returns a **wrong "failed to run" answer** (LFS silently disabled). It did **not** abort configure, but any future required try_run gated this way would.
- **Fix (pick one):**
1. Pre-seed the result in cache before configure — this is exactly VTK's own cross-compile pattern, see `/opt/toolchains/src/VTK-9.3.1/CMake/vtkMobileDevices.cmake:4`: `-DVTK_REQUIRE_LARGE_FILE_SUPPORT:INTERNAL=2` (any truthy value; VTK's mobile toolchain forces `2`). This skips the run entirely.
2. Or point the emulator at a node that enables the opcode: `-DCMAKE_CROSSCOMPILING_EMULATOR="/opt/toolchains/emsdk/node/22.16.0_64bit/bin/node;--experimental-wasm-exnref"`. That makes this (and any other) try_run execute correctly.
- **Note:** emcmake **does** auto-set `CMAKE_CROSSCOMPILING_EMULATOR` to node (confirmed in cache: `/opt/toolchains/emsdk/node/22.16.0_64bit/bin/node`), so try_run generally works — the only failure is the EH-opcode mismatch above. Endianness is resolved at **compile time** (`vtkEndian.h` → little-endian, correct), no run needed; `CheckTypeSize`/`__int64`/`environ`/`st_mtimespec` failures in `CMakeError.log` are **benign** platform-probe `try_compile`s, not blockers.
- **Confidence:** High (observed directly in this build's `CMakeConfigureLog.yaml`/cache).
- **Sources:** [try_run cross-compiling (CMake)](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Cross%20Compiling%20With%20CMake.html), [exnref / opcode 0x1f = try_table](https://github.com/WebAssembly/exception-handling/issues/280), plus in-tree `CMake/vtkMobileDevices.cmake` and `CMakeLists.txt:130`.
---
### P2 — vtkThreadedCallbackQueue uses std::thread with no `__EMSCRIPTEN__` guard (latent, not a build stopper)
- **Area:** Threading / single-threaded (`-sUSE_PTHREADS=0`).
- **Blocker:** `VTK::ParallelCore` is being built (pulled in via the StandAlone group — no enabled module hard-depends on it). It contains `/opt/toolchains/src/VTK-9.3.1/Parallel/Core/vtkThreadedCallbackQueue.cxx`, which does `std::thread(...)` (line 142) + `thread.join()` (line 191) and includes `<thread>/<mutex>/<condition_variable>/<atomic>`, with **no emscripten guard**. This **compiles and links** — without `-pthread`, Emscripten links the pthread stub (`library_pthread_stub`) so `std::thread` resolves. It only bites at **runtime** if the queue is actually instantiated and started (modern emscripten's stub `pthread_create` returns error/aborts single-threaded), which a pure data-model pipeline typically never does.
- **Fix (optional, cleanest):** Drop the module entirely since nothing you enabled needs it: `-DVTK_MODULE_ENABLE_VTK_ParallelCore=NO` (or `DONT_WANT`). That removes `vtkThreadedCallbackQueue` from the build. Otherwise leave as-is (compiles/links fine) and just don't use it. (In VTK 9.4 this class moved Parallel/Core → Common/Core, so on 9.4+ you can't drop it this way.)
- **Confidence:** High that it compiles/links; medium on the exact runtime-abort behavior (version-dependent).
- **Sources:** [Emscripten pthreads (stub without -pthread)](https://emscripten.org/docs/porting/pthreads.html), [pthread_create single-thread abort #25738](https://github.com/emscripten-core/emscripten/issues/25738), [VTK docs: threads emulated single-thread without VTK_WEBASSEMBLY_THREADS](https://docs.vtk.org/en/latest/advanced/build_wasm_emscripten.html).
---
### Ruled-out (concretely NOT blockers) — so you don't chase them
- **vtkMultiThreader (Q2/Q3):** already emscripten-safe in 9.3.1. `pthread_create` is guarded at `Parallel`... no — at `/opt/toolchains/src/VTK-9.3.1/Common/Core/vtkMultiThreader.cxx:272,400,483` by `#if !defined(__CYGWIN__) && !defined(__EMSCRIPTEN__)`, so it takes the serial path. `vtkThreads.h` defines `VTK_USE_PTHREADS` (harmless — headers/stub present). Confidence: High.
- **vtkConditionVariable (Q3):** **does not exist** in VTK 9.3.1 (removed earlier). Nothing to patch. Confidence: High.
- **vtkSMPTools/vtkAtomic/vtkCriticalSection (Q2):** `vtkSMP.h` = `VTK_SMP_Sequential` (no thread dispatch). `vtkAtomicMutex.h` (std::atomic), `vtkCriticalSection.h`/`vtkDebugLeaks`/`vtkOutputWindow`/`vtkStringToken` (std::mutex) all compile fine single-threaded (mutexes are effectively no-ops, atomics compile to plain ops). No `VTK_USE_THREADS`/`VTK_ENABLE_KOKKOS` toggles needed. Confidence: High.
- **setjmp/longjmp (Q7):** **none** in any enabled first-party module dir, and **none** in the third-party libs the IO subset links (expat, zlib, doubleconversion, lz4, lzma, utf8, pugixml, fmt). The longjmp users (libjpeg/libpng under `ThirdParty/jpeg`,`/png`) exist on disk but are **not compiled** (IOImage disabled). The data-model + IOXML/IOLegacy subset is longjmp-free. Confidence: High.
- **IO / filesystem (Q4):** IOCore/IOLegacy/IOXML/IOXMLParser use only `vtksys::SystemTools` + `std::ifstream/ofstream` — no raw `getenv/fopen/tmpnam/mkstemp/realpath/stat/mkdir/popen/system`. All MEMFS-safe. Confidence: High.
- **RTTI / exceptions (Q6):** VTK adds **no** `-fno-rtti` (only `-qrtti=all` for IBM XL, at `CMake/vtkCompilerPlatformFlags.cmake:55`), so default RTTI stays on — `dynamic_cast` works. `-fwasm-exceptions` and RTTI don't conflict. `-sDISABLE_EXCEPTION_CATCHING` is **irrelevant** here: it only affects the legacy JS/`-fexceptions` EH mode; with `-fwasm-exceptions` catching is always on and that flag is a no-op. Confidence: High. Source: [Emscripten C++ exceptions](https://emscripten.org/docs/porting/exceptions.html).
---
### Prior art (Q5)
- **Official path:** Kitware's WebAssembly rework is recent (Kitware blog dated Aug 2025; the `VTK_WEBASSEMBLY_THREADS` / `VTK_WEBASSEMBLY_64_BIT` CMake vars and the "use the Emscripten SDK" CMake rework land in the **9.4** line). The published `emcmake` recipe is rendering-oriented (`-DVTK_ENABLE_WEBGPU:BOOL=ON`, `-DVTK_WEBASSEMBLY_64_BIT:BOOL=ON`, `-DBUILD_SHARED_LIBS:BOOL=OFF`, `-DCMAKE_BUILD_TYPE=Release`). There is **no published data-model-only recipe** — the correct lever is exactly what you're doing (`VTK_GROUP_ENABLE_Rendering=DONT_WANT`). Data-model modules have compiled to wasm32-emscripten for years; the 9.4 work is mostly rendering/threads/wasm64. VTK 9.3.1 (your version) predates the CMake rework, which is why you're driving it manually — and, per above, it works.
- **Sources:** [VTK build_wasm_emscripten docs](https://docs.vtk.org/en/latest/advanced/build_wasm_emscripten.html), [Kitware: Introducing WebAssembly support in VTK](https://www.kitware.com/introducing-webassembly-support-in-vtk/), [Kitware/vtk-wasm](https://github.com/Kitware/vtk-wasm), [VTK 9.4 release notes (ThreadedCallbackQueue moved to Common/Core)](https://github.com/Kitware/VTK/blob/master/Documentation/release/9.4.md).
---
### Bottom line
Configure already passed; the build should compile and link with the current flag set. Do the P1 cache pre-seed (`-DVTK_REQUIRE_LARGE_FILE_SUPPORT:INTERNAL=2`, matching `vtkMobileDevices.cmake`) to make the LFS check correct and to harden against any future try_run hitting the exnref/node issue, and optionally add `-DVTK_MODULE_ENABLE_VTK_ParallelCore=NO` to shed the only unguarded `std::thread` user. Everything else in your questions (SMP threads, vtkMultiThreader, vtkConditionVariable, setjmp/longjmp, IO/MEMFS, RTTI, exception-catching flags) is already clean for this exact module subset.