Skip to content

feat: STEP write under wasm (pyodide 0.29.4 / native wasm-EH)#28

Closed
Krande wants to merge 1 commit into
mainfrom
fix/wasm-step-write
Closed

feat: STEP write under wasm (pyodide 0.29.4 / native wasm-EH)#28
Krande wants to merge 1 commit into
mainfrom
fix/wasm-step-write

Conversation

@Krande

@Krande Krande commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Summary

backend.write_step (STEPCAFControl_Writer via OCCT XCAF) fatally aborted the pyodide module with RuntimeError: unreachable / bare Aborted(). This fixes it, and migrates the wasm toolchain to pyodide 0.29.4 / emscripten 4.0.9 / Python 3.13 with native WebAssembly exception handling (required for the fix).

Root cause (two parts)

  1. step_writer.cpp / helpers.cpp were never compiled into the wasm side module. The BUILD_WASM branch of CMakeLists.txt still carried the stale "OCCT not available in wasm" source list (Color.cpp + Models.cpp only). So write_shapes_to_step was emitted as an unresolved env import whose function-table slot is a trap stub — the first call trapped with unreachable. GLB / STEP-read / make_box worked only because they are self-contained in cad_py_wrap.cpp.
  2. The OCAF document used "XmlOcaf", whose driver isn't registered under OCCT_NO_PLUGINS (TKXml* isn't even linked for wasm). Switched to BinXCAFDrivers::DefineFormat + a "BinXCAF" document (TKBinXCAF is linked). write_shapes_to_step now also translates Standard_Failure into a std::runtime_error so nanobind surfaces a Python exception instead of std::terminate.

Toolchain migration (pyodide 0.29.4 / wasm-EH)

The old -fexceptions JS-trampoline EH model fatally trapped in STEPCAFControl_Writer; the fix needs native wasm exception handling:

  • OCCT + adacpp + nanobind all compile with -fwasm-exceptions (+ -sSUPPORT_LONGJMP=wasm).
  • OCC_CONVERT_SIGNALS dropped on wasm — setjmp inside a catch is invalid under wasm-EH and made emscripten emit invalid wasm (br_table: label arity inconsistent CompileError).
  • wheel tag cp313 / pyodide_2025_0_wasm32; Python_SOABI = cpython-313-wasm32-emscripten.
  • xbuildenv path resolved dynamically via pyodide config get python_include_dir instead of a machine-specific content-hash path (CI-portable).
  • native test env + linux preset bumped to Python 3.13 to match feature.common.

Verification

  • WASM (node-pyodide): SAT → {glb, obj, stl, xml, step} all ✅ (step = 6422 bytes) and FEM bake (manifest + mesh + edges + elements + 3 result fields) ✅.
  • WASM, from-scratch build with the dynamic xbuildenv path (full OCCT cross-compile) — wheel builds and all targets pass, validating the CI flow.
  • Native: full suite 62 passed, including test_cad_write_step / test_basic_write_step (the BinXCAF path) — works outside wasm too.

CI note

This PR touches cmake/wasm_occt.cmake / pixi.toml / CMakePresets.json, so merging triggers publish-occt-wasm-base to rebuild the OCCT-wasm base image with the new EH model (em4.0.9, -fwasm-exceptions, no OCC_CONVERT_SIGNALS). ci-wasm-tests / publish-wasm-base consume the base via :latest; if they run before the rebuilt base finishes they may pick up the stale (em3.1.58) base and need a re-run once the base publishes. The fallback (build OCCT from source) still produces a correct result.

🤖 Generated with Claude Code

backend.write_step (STEPCAFControl_Writer via OCCT XCAF) fatally aborted
the pyodide module with `RuntimeError: unreachable`. Two root causes:

1. step_writer.cpp / helpers.cpp were never compiled into the wasm side
   module — the BUILD_WASM source list still carried the stale "OCCT not
   available in wasm" set. write_shapes_to_step was therefore emitted as
   an unresolved `env` import whose function-table slot is a trap stub,
   so the first call trapped. GLB / STEP-read / make_box worked only
   because they are self-contained in cad_py_wrap.cpp.
2. The OCAF document used "XmlOcaf", whose driver isn't registered under
   OCCT_NO_PLUGINS (TKXml* isn't even linked for wasm). Switched to
   BinXCAFDrivers::DefineFormat + a "BinXCAF" document (TKBinXCAF is
   linked). write_shapes_to_step now also translates Standard_Failure
   into a std::runtime_error so nanobind surfaces a Python exception
   instead of std::terminate.

Requires the wasm toolchain to move to pyodide 0.29.4 / emscripten 4.0.9
/ Python 3.13 with native WebAssembly exception handling (the old
-fexceptions JS-trampoline model trapped in STEPCAFControl_Writer):
- OCCT + adacpp + nanobind all compile with -fwasm-exceptions
  (+ -sSUPPORT_LONGJMP=wasm); OCC_CONVERT_SIGNALS dropped on wasm
  (setjmp inside a catch is invalid under wasm-EH → "br_table: label
  arity inconsistent" CompileError).
- wheel tag cp313 / pyodide_2025_0_wasm32; Python_SOABI cpython-313.
- xbuildenv path resolved dynamically via `pyodide config get` instead
  of a machine-specific content hash (CI-portable).
- native test env + linux preset bumped to python 3.13 to match.

Verified: SAT -> {glb,obj,stl,xml,step} and FEM bake all succeed under
pyodide (node-pyodide harness); native suite 62 passed including
test_cad_write_step / test_basic_write_step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Krande Krande closed this Jun 15, 2026
@Krande Krande deleted the fix/wasm-step-write branch June 15, 2026 16:17
@Krande Krande changed the title fix: STEP write under wasm (pyodide 0.29.4 / native wasm-EH) feat: STEP write under wasm (pyodide 0.29.4 / native wasm-EH) Jun 15, 2026
@github-actions

Copy link
Copy Markdown

👋 Hi there! I have checked your PR and found the following:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant