subagent transcript

session 17f09f89 · workflow wf_54002329
delegated task
CONTEXT — FreeCAD is being ported to WebAssembly (emscripten). Toolchain facts you MUST assume: - Emscripten 4.0.12 (clang/LLVM 22), Binaryen v123, at
▸ Magik
CONTEXT — FreeCAD is being ported to WebAssembly (emscripten). Toolchain facts you MUST assume: - Emscripten 4.0.12 (clang/LLVM 22), Binaryen v123, at /opt/toolchains/emsdk. - Native wasm exceptions (exnref): compile with -fwasm-exceptions -sWASM_LEGACY_EXCEPTIONS=0. NO legacy asyncify-EH for new TUs. - NO pthreads/std::thread (single-threaded wasm build). NO real sockets. Filesystem is emscripten MEMFS. - No subprocess/popen/system (external solvers like ccx/calculix, gmsh are NOT available at runtime). - The build already ships OCCT, Qt6 (widgets), Python 3.14, PySide6, and many FreeCAD Mods (Part, Mesh, Draft, BIM, CAM, Import, etc.). Mesh module was built WITHOUT SMESH (uses OCCT mesher). - FreeCAD source root: /home/magik6k/lcad-wasm/freecad-port/FreeCAD - GOAL: load the example file data/examples/FEMExample.FCStd in the browser. Its objects are: Fem::FemAnalysis(3), Fem::ConstraintForce/Fixed/Displacement, Fem::FemSolverObjectPython(3), Fem::FemMeshObjectPython(3), Fem::FemMeshShapeBaseObjectPython(3), Fem::FemResultObjectPython(3), Fem::FemPostPipeline(3), Fem::FemPostWarpVectorFilter(3), Fem::FeaturePython(2), App::MaterialObjectPython(3), Part::Box/Plane/Line, App::TextDocument, App::DocumentObjectGroup. - Restore currently fails: "No module named 'Fem'" (C++ Fem module not built, BUILD_FEM=OFF) AND a security block in src/App/PropertyPythonObject.cpp isAllowedModule() blocks 'femobjects.material_common'. - KEY: src/Mod/Fem/CMakeLists.txt gates all VTK behind BUILD_FEM_VTK (adds -DFC_USE_VTK). FemPost*/VTKExtensions are VTK-only. You have Read/Grep/Glob/Bash (read-only intent — do NOT modify files, do NOT run long builds; quick greps/find are fine). Return ONLY the requested structured fields. Be concrete: cite exact files, CMake option names, #ifdef guards, class names. You are the porting lead. Below are 6 structured assessment reports from parallel investigators into whether/how to build SMESH + FEM (+ maybe a VTK subset) for the FreeCAD-wasm build so that data/examples/FEMExample.FCStd LOADS in the browser. REPORTS (JSON): [ { "area": "In-tree SMESH (src/3rdParty/salomesmesh) VTK dependency & whether FemMesh needs SMESH to RESTORE the FEMExample", "verdict": "FemMesh cannot restore standalone and SMESH cannot be built without VTK — VTK is baked into the SMDS data model, so the minimal path is: build the bundled SMESH against a VTK data-model subset (CommonCore+CommonDataModel+FiltersVerdict+IOLegacy), which is separate from the BUILD_FEM_VTK/FC_USE_VTK gate that only controls the FemPost pipeline.", "vtk_needed": "data-model-subset", "key_findings": [ "NO VTK on/off option in salomesmesh/CMakeLists.txt: ${VTK_LIBRARIES} is unconditionally linked into SMDS (line ~ADD_LIBRARY(SMDS)) and SMESH targets, and ${VTK_INCLUDE_DIRS} is always on the include path. VTK_OPTIONS only adds extra -D defines if set. There is no #ifdef guard around VTK in the core.", "cMake/FreeCAD_Helpers/SetupSalomeSMESH.cmake:14-15 states verbatim 'if we use smesh we definitely also need vtk, no matter of external or internal smesh' and does find_package(VTK ... REQUIRED) (lines 34/59/62), then set(BUILD_FEM_VTK ON) at line 65. VTK is a HARD requirement of building SMESH.", "VTK is the mesh STORAGE backend, not visualization: inc/SMDS_UnstructuredGrid.hxx:64 'class SMDS_UnstructuredGrid: public vtkUnstructuredGrid' and :54 'SMDS_CellLinks: public vtkCellLinks'. inc/SMDS_Mesh.hxx:75-76,811-812: 'actual nodes coordinates, cells definition and reverse connectivity are stored in a vtkUnstructuredGrid' — member SMDS_UnstructuredGrid* myGrid.", "Node coordinates literally live in VTK: src/SMDS/SMDS_MeshNode.cpp:70-71 X()/Y()/Z() do 'grid = mesh->getGrid(); vtkPoints* points = grid->GetPoints();'. The 24 vtk-including files are CORE SMDS_* mesh-data, not optional export/viz — they cannot be stubbed without rewriting the data model.", "VTK classes actually used are the data-model subset: vtkUnstructuredGrid/vtkCellLinks/vtkIdList/vtkIdType/vtkCellType/vtkCellArray/vtkPoints/vtkIdTypeArray/vtkDoubleArray/vtkUnsignedCharArray/vtkCellData/vtkDataSet (CommonCore+CommonDataModel), vtkMeshQuality (FiltersVerdict, in src/Controls/SMESH_Controls.cpp), vtkUnstructuredGridWriter (IOLegacy, debug write in SMDS_UnstructuredGrid.cpp). NO rendering/OpenGL classes.", "FemMesh IS an SMESH_Mesh wrapper: src/Mod/Fem/App/FemMesh.h:252 'SMESH_Mesh* myMesh'; every constructor calls getGenerator()->CreateMesh() (FemMesh.cpp:92) so even an EMPTY FemMesh needs SMESH_Gen->SMESH_Mesh->SMESHDS_Mesh->SMDS_Mesh->vtkUnstructuredGrid.", "RESTORE path is SMESH-only: FemMesh::RestoreDocFile (FemMesh.cpp:2462-2477) writes the zip entry to a temp file then calls myMesh->UNVToMesh(); Save/SaveDocFile call myMesh->ExportUNV(). The mesh persists as a UNV file inside the FCStd and is reloaded into SMESH_Mesh via DriverUNV+SMESHDS+SMDS. SMESH is required to HOLD/RESTORE, not just to GENERATE.", "Netgen is optional and generation-only: NETGENPlugin target is gated behind if(BUILD_FEM_NETGEN) in salomesmesh/CMakeLists.txt; BUILD_FEM_NETGEN defaults OFF on non-MSVC (InitializeFreeCADBuildOptions.cmake:214). Not needed to restore. MEFISTO2 (2D f2c triangulator) is always built but is self-contained (no VTK).", "Mesh module avoids SMESH by using a totally independent kernel: src/Mod/Mesh/App/Core/MeshKernel.h — zero SMESH_Mesh/SMDS_Mesh/vtkUnstructuredGrid references anywhere under src/Mod/Mesh/App. Its representation is NOT interchangeable with FemMesh's SMESH_Mesh, so it does not help FemMesh restore.", "Building the bundled (internal) SMESH also pulls MED/HDF5: src/SMESH/SMESH_Mesh.cpp:54-55 includes DriverMED_R/W and implements ExportMED (line 1431); SetupSalomeSMESH.cmake does find_package(MEDFile REQUIRED) + HDF5 for the internal build. These are link-time co-deps of SMESH, separate from VTK." ], "wasm_blockers": [ "src/3rdParty/salomesmesh/ …
Fable
thinking
I'm pulling together the staged build plan from these six reports and need to reconcile some disagreements, particularly around whether SMESH requires VTK—most sources agree it needs a subset of VTK's data model, though a couple reports initially said none.

Let me run a few targeted greps to verify the critical load-bearing facts: the boost::thread destructor in SMESH_Mesh.cpp, whether MED's find_package is marked REQUIRED, and if FemMesh's RestoreDocFile actually uses UNVToMesh. These details will determine whether Stage 1 is even feasible.
Fable
I'll verify the most decisive load-bearing claims cheaply before committing to the plan.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null || cd /home/magik6k/lcad-wasm; echo \"=== boost::thread in ~SMESH_Mesh ===\"; grep -n \"boost::thread\\|deleteMeshDS\" src/3rdParty/salomesmesh/src/SMESH/SMESH_Mesh.cp…
tool result
=== boost::thread in ~SMESH_Mesh ===
164:  void deleteMeshDS(SMESHDS_Mesh* meshDS)
166:    //cout << "deleteMeshDS( " << meshDS << endl;
170:  static void* deleteMeshDS(void* meshDS)
172:    //cout << "deleteMeshDS( " << meshDS << endl;
229:    boost::thread aThread(boost::bind( & deleteMeshDS, _myMeshDS ));
231:    boost::thread aThread(boost::bind( & deleteMeshDS, _myMeshDS ));
233://    int result=pthread_create(&thread, NULL, deleteMeshDS, (void*)_myMeshDS);
=== UNVToMesh restore ===
1643:    …[truncated]
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== EMSCRIPTEN gate ===\"; grep -n \"NOT EMSCRIPTEN\\|FREECAD_USE_SMESH\\|BUILD_SMESH\" cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake 2>/dev/null |…
tool result
=== EMSCRIPTEN gate ===
218:    if((BUILD_FEM OR BUILD_MESH_PART) AND NOT EMSCRIPTEN)
219:        set(FREECAD_USE_SMESH ON)
221:            set(BUILD_SMESH OFF)
223:            set(BUILD_SMESH ON)
226:        set(FREECAD_USE_SMESH OFF)
227:        set(BUILD_SMESH OFF)
=== Fem/App unconditional SMESH link ===
22:if (FREECAD_USE_EXTERNAL_SMESH)
23:   list(APPEND Fem_LIBS ${EXTERNAL_SMESH_LIBS})
26:       list(APPEND Fem_LIBS StdMeshers SMESH SMDS SMESHDS NETGENPlugin)
28:       list(APPEND Fem_LIB  …[truncated]
Fable
thinking
I've verified the reports and confirmed that boost::thread is used consistently at lines 229 and 231, with pthread_create commented out at line 233. Now I need to check whether UNVToMesh and DriverUNV depend on MED, and if there's a straightforward way to filter out MED files from the glob pattern.

Looking at the FemMesh RestoreDocFile calls at lines 1643 and 2475, I see there are two UNVToMesh invocations. The critical insight is that SMDS_Mesh.hxx has a vtkUnstructuredGrid member, which means VTK's data model is a hard compile dependency for SMESH itself—not optional. However, VTK rendering and FemPost are separate concerns.

This opens up an alternative: I could stub out PropertyFemMesh:  …
Fable
Let me gauge the stub-effort surface and confirm the alternative minimal path is real.
Fable
⚙ Bash {"command": "cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD 2>/dev/null; echo \"=== SMESH coupling surface in Fem/App ===\"; for f in App/FemMesh.cpp App/FemMesh.h App/FemMeshPyImp.cpp App/HypothesisPy.cpp App/AppFem.cpp; do n=$(grep -cE \…
tool result
=== SMESH coupling surface in Fem/App ===
App/FemMesh.cpp : 217 SMESH-ish lines
App/FemMesh.h : 23 SMESH-ish lines
App/FemMeshPyImp.cpp : 95 SMESH-ish lines
App/HypothesisPy.cpp : 495 SMESH-ish lines
App/AppFem.cpp : 34 SMESH-ish lines
=== FemBase_SRCS (always compiled) ===
39:    HypothesisPy.cpp
40:    HypothesisPy.h
112:SET(FemBase_SRCS
121:    FemMesh.cpp
136:SOURCE_GROUP("Base types" FILES ${FemBase_SRCS})
194:    ${FemBase_SRCS}
=== existing __EMSCRIPTEN__ prior art count ===
src/Mod/Mesh/  …[truncated]
Fable
thinking
The SMESH coupling is substantial—FemMesh.cpp alone is 217 lines, and HypothesisPy.cpp is nearly 500 lines of always-compiled code that wraps StdMeshers hypotheses. Stubbing this out would require significant refactoring, especially since guarding SMESH would break HypothesisPy's dependencies.

The stub approach is moderate-to-large C++ surgery across ~5 files with heavy conditional compilation, but avoids external wasm dependencies entirely. Against that, porting SMESH and VTK introduces a large external build effort with unknown wasm feasibility.

Given the tradeoffs, the minimal path is to exclude HypothesisPy.cpp from the Emscripten build entirely—hypotheses are only needed for meshing,   …
Fable
## Bottom line This is **two very different tasks depending on how much of the mesh you insist on materializing**, and the reports disagree only because they answered different questions. The truth: **restoring FemMesh with real geometry requires SMESH, and SMESH's data model (SMDS) *is* `vtkUnstructuredGrid` — so real mesh restore forces a VTK data-model-subset wasm port + MED/HDF5 handling: that is a mega-port** (the VTK-to-wasm cross-compile is the single biggest unknown). But **getting `FEMExample.FCStd` to *LOAD* does not require any of that.** The minimal path is to build `BUILD_FEM=ON` with SMESH/VTK **guarded out under `__EMSCRIPTEN__`** and FemMesh **stubbed to consume its embedded UNV blob opaquely** — this is the established repo pattern (existing `#ifdef __EMSCRIPTEN__` carve-outs in Mesh/Import/MeshPart). That gets **all 13 non-post object types loading** (mesh objects as empty shells), needs **zero new third-party wasm builds** (no VTK, no MED, no HDF5, no salomesmesh), and is a "few builds + bounded C++ surgery" task. Reserve the real SMESH+VTK port for Stage 2 only if you later need mesh geometry, display, or solving. **Verdict: Stage-1 minimal scope needs neither VTK nor SMESH — it needs the Fem C++ module to compile with SMESH severed, plus packaging the `femobjects` Python tree.** **Reconciling the reports:** Reports 1–4 say "SMESH needs VTK — unavoidable." Reports 5–6 say `vtk_needed: none`. Both are right in scope: **SMESH genuinely cannot compile without the VTK data-model subset** (verified: `SMDS_Mesh.hxx` member is `SMDS_UnstructuredGrid* myGrid` deriving from `vtkUnstructuredGrid`; `MeshNode::X/Y/Z` read `grid->GetPoints()`). So *if you build SMESH, VTK is mandatory.* Report 6's insight — echoed as an open question by Report 1 and Report 5's "empty meshes?" — is that **you don't have to build SMESH at all to load the file**; you stub FemMesh. I'm elevating that to the Stage-1 plan because it removes the VTK-wasm unknown, which is the only thing that could turn this into a multi-week port. **Cheap verification that stubbing is sufficient:** unzip the example and check whether the mesh objects carry a real UNV payload — `unzip -l FEMExample.FCStd | grep -i mesh` — a stub only needs to swallow those bytes to keep the reader in sync. ## Tier map | Capability | Needs | LOAD / DISPLAY / SOLVE | Cost | |---|---|---|---| | FemAnalysis, Constraint{Force,Fixed,Displacement}, FemSolverObjectPython, FemResultObject, MaterialObjectPython | Nothing beyond `BUILD_FEM=ON` + `BUILD_MATERIAL=ON` + packaging `femobjects/` | **LOAD** | Trivial — these are core C++ types with no VTK/SMESH includes (`AppFem.cpp:137-194`) | | **FemMesh restore (as empty shell)** | Stub FemMesh under `__EMSCRIPTEN__`; consume embedded UNV blob | **LOAD** | Bounded C++ surgery (~5 files) — **no external deps** | | **FemMesh restore (real geometry)** | salomesmesh + **VTK data-model subset (wasm)** + MED-patch + boost::thread patch | LOAD-with-geometry | **Mega-port** (VTK wasm build is the unknown) | | FemResult | Same as core (it's `FemResultObject`, no VTK in header) — Python proxy `result_mechanical.py` | **LOAD** | Trivial (packaging) | | **FemPost pipeline (3× Pipeline + 3× WarpVector) + DISPLAY** | `BUILD_FEM_VTK`/`FC_USE_VTK` → full VTK render + filters subset | **DISPLAY** | Mega-port; **fails gracefully per-object** if absent | | **SOLVE (CalculiX) / re-MESH (Netgen/gmsh)** | External subprocess solvers — **impossible in wasm** (no popen/fork) | **SOLVE** | Out of scope; degrades to logged error, never blocks LOAD | ## Stage 1 — minimal loadable FEM (target for "one shot / few builds") **Strategy: build Fem, sever SMESH/VTK, stub the mesh. No salomesmesh, no VTK, no MED/HDF5.** **1. Top-level CMake flags** (in `toolchain/configure-stage2.sh`, currently `-DBUILD_FEM=OFF`): ``` -DBUILD_FEM=ON -DBUILD_FEM_NETGEN=OFF # already the non-MSVC default -DBUILD_MATERIAL=ON # femobjects/material_common.py does 'import Materials' # Do NOT set FREECAD_USE_SMESH / BUILD_SMESH / BUILD_FEM_VTK — all stay auto-OFF under EMSCRIPTEN ``` The `AND NOT EMSCRIPTEN` gate at `InitializeFreeCADBuildOptions.cmake:218` (verified) already forces `FREECAD_USE_SMESH=OFF`, `BUILD_SMESH=OFF`, so `SetupSalomeSMESH.cmake` never runs, `FC_USE_VTK` stays undefined, and `salomesmesh` is never added (`src/3rdParty/CMakeLists.txt:4`). **Leave that gate as-is** — do not patch it ON (that's the mega-port door). **2. Source/CMake patches** (all guarded `#if !defined(__EMSCRIPTEN__)` / `#ifdef __EMSCRIPTEN__`, mirroring the existing pattern in `src/Mod/Mesh/Gui/Command.cpp` etc.): - **`src/Mod/Fem/App/CMakeLists.txt:26-28, 225, 230-231`** — wrap the `list(APPEND Fem_LIBS StdMeshers SMESH SMDS SMESHDS)`, `${SMESH_INCLUDE_DIR}`, `target_link_directories(... SMESH_LIB_PATH)`, and drop `${VTK_LIBRARIES}` from the `target_link_libraries(Fem ...)` in an `if(NOT EMSCRIPTEN)`. (Verified these are unconditional today.) - **`src/Mod/Fem/App/CMakeLists.txt:39, 121`** — under EMSCRIPTEN, replace `HypothesisPy.cpp` and the real `FemMesh.cpp` in `FemBase_SRCS` with wasm stub TUs (see below). `HypothesisPy.cpp` is **495 SMESH-coupled lines** (verified) and registers ~30 `StdMeshers_*Py` hypothesis types that are **meshing-only, not needed to restore** — under wasm compile it as an empty TU and remove its calls from `AppFem.cpp:94-127`. - **`src/Mod/Fem/App/FemMesh.h:29-30, 37-39, 252`** — `#ifdef __EMSCRIPTEN__` swap `SMESH_Mesh* myMesh` for an opaque `std::string myUnvBlob;` (or `std::vector<char>`), drop the `<SMDSAbs_ElementType.hxx>` / `<SMESH_Version.h>` includes. - **New `src/Mod/Fem/App/FemMesh_wasm.cpp`** — minimal implementation of the FemMesh class: constructor (no `CreateMesh`), destructor (no boost::thread), and critically **`RestoreDocFile`** reads the zip entry into `myUnvBlob` and **`SaveDocFile`** writes it back (round-trippable), so the document reader stays in sync. Geometry accessors return empty. (`FemMesh.cpp` real version is 217 SMESH lines — verified; do not try to `#ifdef` inside it, replace the TU.) - **`src/Mod/Fem/App/FemMeshPyImp.cpp`** (95 SMESH lines) — under EMSCRIPTEN, stub the SMESH-returning methods to raise `NotImplementedError` / return empty. Only needed so the Python type links; restore itself doesn't call them. - **`src/Mod/Fem/App/AppFem.cpp:25, 94-127`** — `#ifdef __EMSCRIPTEN__` out the `<SMESH_Version.h>` include and the hypothesis-type registrations. Keep all core `initType()` calls (`:137-194`). The `#ifdef FC_USE_VTK` block (`:196-223`) is already auto-excluded → FemPost types unregistered. **3. Python packages to package** into `/freecad/Mod/Fem/` (all VTK-independent, verified imports are clean): `femobjects/` (esp. `__init__.py`, `base_fempythonobject.py`, `base_femelement.py`, `material_common.py`, `mesh_gmsh.py`, `mesh_result.py`, `result_mechanical.py`, `solver_ccxtools.py`, `solver_calculix.py`, `element_geometry1D.py`, `element_geometry2D.py`), plus `femtools/ femmesh/ feminout/ femresult/ femsolver/ ObjectsFem.py Init.py`. These are copied by `FemScriptsTarget` (`src/Mod/Fem/CMakeLists.txt:634-637`) once `BUILD_FEM=ON`. **Exclude** the `post_*.py` / `base_fempost*.py` proxies (gated behind `BUILD_FEM_VTK_PYTHON`, and their objects are skipped anyway). **4. The isAllowedModule "fix" — no C++ edit needed.** `isAllowedModule()` (`PropertyPythonObject.cpp:89-189`) is a *positive path check*, not a denylist. It blocks `femobjects.material_common` today only because `/freecad/Mod/Fem` doesn't exist (BUILD_FEM=OFF) so it isn't in `FreeCAD.__ModDirs__` (`FreeCADInit.py:1460`). **Once Step 3 packages `Mod/Fem`, it's scanned → added to `__ModDirs__` → `find_spec` origin lives under it → allowed automatically.** Just ensure your pack-mods step includes the Fem mod dir. **5. Packaging tier:** whichever pack-mods tier already ships Part/Draft/BIM — add `Mod/Fem` to it (the App `Fem` shared lib + the `femobjects` tree). `BUILD_GUI` and `femviewprovider/` are **not required** for App-document restore (GuiDocument.xml view-provider imports fail per-object gracefully at `Document.cpp:1704`). **Expected result — of the 15 example object types:** - **Restore OK (13):** FemAnalysis×3, ConstraintForce/Fixed/Displacement, FemSolverObjectPython×3, FemResultObjectPython×3, FeaturePython×2, **FemMeshObjectPython×3 / FemMeshShapeBaseObjectPython×3 as empty shells**, plus non-Fem App::MaterialObjectPython×3, Part::Box/Plane/Line, App::TextDocument, App::DocumentObjectGroup. - **Fail gracefully / skipped (6):** FemPostPipeline×3, FemPostWarpVectorFilter×3 — C++ types unregistered (no `FC_USE_VTK`); `addObject` throws `Base::TypeError`, caught at `Document.cpp:1704`, doc still opens with dangling-link warnings. ## Stage 2 — VTK postprocessing + real mesh (optional follow-up) Only pursue if Stage 1 shows the empty-mesh shells / missing FemPost objects are unacceptable (i.e. you actually need mesh geometry or 3D result display). This is where the real cost lives. - **Minimal VTK subset (data-model only, no rendering):** `CommonCore, CommonDataModel, CommonExecutionModel, CommonMath, CommonMisc, CommonSystem, CommonTransforms, FiltersCore, FiltersGeneral, FiltersVerdict, FiltersExtraction, FiltersSources, FiltersGeometry, FiltersModeling, IOCore, IOXML, IOXMLParser, IOLegacy`. This covers both SMDS (mesh storage) and the FemPost restore path (`vtkXML*Reader` in `PropertyPostDataObject::RestoreDocFile`). - **Emscripten build approach:** VTK has official wasm support since 9.3/9.4. Configure with `emcmake cmake` + `-DVTK_GROUP_ENABLE_Rendering=DONT_WANT -DVTK_GROUP_ENABLE_MPI=DONT_WANT -DVTK_GROUP_ENABLE_Web=DONT_WANT -DVTK_SMP_IMPLEMENTATION_TYPE=Sequential -DVTK_ENABLE_WRAPPING=OFF -DVTK_WRAP_PYTHON=OFF -DVTK_BUILD_TESTING=OFF`. Sequential SMP avoids threads. Rendering (WebGL/GLES2) is the hard/unnecessary-for-restore piece — leave off; FemPost restores headless, display comes even later. - **Then salomesmesh:** patch `SetupSalomeSMESH.cmake:93/111` to skip `find_package(MEDFile/HDF5 REQUIRED)` under wasm; remove `MED_*.cpp`/`DriverMED_*.cpp` (2 files include `<med.h>`, verified) from the SMESH glob and guard `ImportMED/ExportMED/ExportSAUV` in `SMESH_Mesh.cpp` behind `-DFC_NO_MED`; **patch the boost::thread in `~SMESH_Mesh` (`SMESH_Mesh.cpp:229/231`, verified — both branches spawn a thread to `deleteMeshDS`) to a synchronous `deleteMeshDS(_myMeshDS)`**; verify the 8 `ADD_LIBRARY(SHARED)` targets go through the build's SHARED→STATIC shim. Restore path uses UNV (`FemMesh.cpp:1643/2475` `UNVToMesh`, verified), so DriverUNV+SMDS+VTK-subset is the exercised chain — MED can stay stubbed. - **`FC_USE_VTK` / FemPost:** patch `SetupSalomeSMESH.cmake:65` so it doesn't auto-force `BUILD_FEM_VTK ON`, then enable it deliberately once the VTK subset builds. Un-`#ifdef` the FemPost registrations. - **Rough effort:** VTK-subset wasm build = days (the real unknown); salomesmesh patches = 1-2 days once VTK links; boost::thread + MED excision are small. **Total: multi-week, not "few builds."** ## Risk register 1. **VTK-to-wasm cross-compile is an unbounded unknown** (Stage 2). *Mitigation:* keep it entirely out of Stage 1 via the stub path; only spike VTK CommonCore+CommonDataModel as a standalone `emcmake` build *before* committing to Stage 2. This is the one thing that turns "few builds" into a mega-port — don't let it into Stage 1. 2. **FemMesh stub surgery balloons** — `FemMesh.cpp` (217 SMESH lines), `HypothesisPy.cpp` (495), `FemMeshPyImp.cpp` (95) are deeply woven. *Mitigation:* replace whole TUs with wasm stubs rather than inline `#ifdef`; treat HypothesisPy as an empty TU (meshing-only, restore never touches it); only the FemMesh ctor/dtor/Restore/SaveDocFile need real bodies. 3. **The embedded UNV blob must be consumed to keep the reader in sync.** If the stub `RestoreDocFile` doesn't read the exact zip entry FemMesh wrote, the XML parser desyncs and the *whole* doc fails. *Mitigation:* the stub must read-and-store the file entry verbatim (round-trip via SaveDocFile), not skip it. Verify by unzipping the example first (`unzip -l`). 4. **`Materials` C++ module not importable** → all 3 MaterialCommon proxies restore as `None`. *Mitigation:* confirm `BUILD_MATERIAL=ON` is in the wasm config and the `Materials` module packages/imports; `material_common.py:35` does `import Materials` at module top. 5. **FemPost dangling links break the DocumentObjectGroup graph.** Skipping 6 FemPost objects may leave `App::Link`/group references dangling. *Mitigation:* per-object skip is already graceful (`Document.cpp:1704`); confirm the group/analysis objects tolerate missing children (they normally do — dangling-link warning only). If not, this is a small loader-side patch, not a port. ## Concrete first commands ```bash # 0. Verify the mesh payload the stub must consume (decides if stubbing is sufficient) cd /home/magik6k/lcad-wasm/freecad-port/FreeCAD unzip -l data/examples/FEMExample.FCStd | grep -iE 'mesh|\.unv|FemMesh' # 1. Confirm the EMSCRIPTEN gate keeps SMESH/VTK auto-off (already verified: line 218) grep -n "NOT EMSCRIPTEN" cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake # 2. Flip the module flags in the existing stage-2 configure (currently BUILD_FEM=OFF) grep -n "BUILD_FEM\|BUILD_MATERIAL\|BUILD_MESH" toolchain/configure-stage2.sh # edit: -DBUILD_FEM=ON -DBUILD_FEM_NETGEN=OFF -DBUILD_MATERIAL=ON (leave SMESH/VTK untouched) # 3. First configure pass — expect it to FAIL fast on FemMesh.h:29 <SMESH_Version.h> and the # Fem/App/CMakeLists.txt:26 SMESH link. Those failures are the exact list of #ifdef guards to add. bash toolchain/configure-stage2.sh 2>&1 | tee /tmp/claude-1000/-home-magik6k-lcad-wasm/17f09f89-ce7c-4f49-aa6a-cf0521026805/scratchpad/fem-configure.log # 4. Try building just the Fem App target to surface the full compile/link error set for the stubs cmake --build <wasm-build-dir> --target Fem 2>&1 | tee /tmp/claude-1000/-home-magik6k-lcad-wasm/17f09f89-ce7c-4f49-aa6a-cf0521026805/scratchpad/fem-build.log ``` The Step 3/4 error logs *are* your patch checklist: every `SMESH_*`/`SMDS_*` undefined-reference or missing-include maps 1:1 to a `#ifdef __EMSCRIPTEN__` stub in `FemMesh.{h,cpp}`, `FemMeshPyImp.cpp`, `HypothesisPy.cpp`, `AppFem.cpp`, and `Fem/App/CMakeLists.txt:26-28,225,230-231`. **Key file references:** gate `cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake:218`; SMESH link `src/Mod/Fem/App/CMakeLists.txt:26-28,231`; FemMesh member `src/Mod/Fem/App/FemMesh.h:252`; UNV restore `src/Mod/Fem/App/FemMesh.cpp:1643,2475`; boost::thread dtor (Stage 2) `src/3rdParty/salomesmesh/src/SMESH/SMESH_Mesh.cpp:229,231`; MED/HDF5 REQUIRED (Stage 2) `cMake/FreeCAD_Helpers/SetupSalomeSMESH.cmake:93,96,111`; isAllowedModule `src/App/PropertyPythonObject.cpp:89-189`; `__ModDirs__` `src/App/FreeCADInit.py:1460`.