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.
YOUR AREA: How does upstream FreeCAD build FEM, and are there existing wasm/emscripten FEM efforts or a documented minimal-FEM config to copy?
Investigate:
1. Read the top-level CMakeLists.txt and cMake/ for BUILD_FEM, BUILD_FEM_NETGEN, BUILD_FEM_VTK, FREECAD_USE_* options and their defaults/dependencies. What does turning BUILD_FEM=ON pull in transitively (find_package requirements that would fail in wasm)?
2. Search the repo/docs for any conditional that disables FEM sub-features (e.g. a 'minimal' or 'no-solver' path). Is there precedent for FEM-without-Netgen or FEM-without-VTK in upstream CI?
3. Check how OTHER already-ported modules that had heavy 3rd-party deps were handled in THIS repo — look for FC_OS_WASM / __EMSCRIPTEN__ guards already added across src/Mod (grep -rl __EMSCRIPTEN__ src/Mod, src/3rdParty). What's the established pattern for disabling a feature at runtime vs compile time here?
4. Is salomesmesh even in the current wasm build's CMake, or fully excluded? Check the build dir if present (look for a build/ or the deploy build config) and any -DBUILD_* the port uses.
5. WebSearch briefly: 'FreeCAD FEM minimal build without VTK Netgen', 'SMESH emscripten wasm', 'VTK emscripten build' for prior art.
Answer: the recommended top-level CMake flag set to get a minimal-but-loadable FEM, and any prior-art shortcuts.
Fable
thinking
I'm diving into the FreeCAD build system to understand how the FEM module is structured, starting with some parallel searches through the codebase.
Fable
I'll investigate how FreeCAD builds FEM and look for wasm/minimal-FEM precedent. Let me start with parallel exploration.
Fable
⚙ Bash {"command": "grep -n \"BUILD_FEM\\b\\|BUILD_FEM_NETGEN\\|BUILD_FEM_VTK\\|option(BUILD_FEM\" CMakeLists.txt", "description": "Find FEM build options in top-level CMakeLists"}
Fable
⚙ Bash {"command": "grep -rn \"BUILD_FEM\" CMakeLists.txt | head -50", "description": "All BUILD_FEM refs in top CMakeLists"}
Fable
⚙ Bash {"command": "grep -rln \"__EMSCRIPTEN__\\|FC_OS_WASM\" src/Mod src/3rdParty 2>/dev/null | head -60", "description": "Find modules with wasm guards already"}
Fable
⚙ Bash {"command": "ls -d build* 2>/dev/null; find . -maxdepth 2 -name \"CMakeCache.txt\" 2>/dev/null | head", "description": "Look for build dirs and CMakeCache"}
Fable
thinking
I'm examining the top-level CMakeLists file, specifically the FEM section and the available CMake options.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/CMakeLists.txt", "offset": 100, "limit": 120}
Fable
⚙ Bash {"command": "grep -rn \"BUILD_FEM\\|FEM_NETGEN\\|FEM_VTK\\|FREECAD_USE_\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/*.cmake /home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/**/*.cmake 2>/dev/null | head -60", "description": "Find…
Fable
⚙ Bash {"command": "grep -rn \"option(BUILD_FEM\\|BUILD_FEM \" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/CMakeLists.txt; echo \"---defaults---\"; grep -rn \"BUILD_FEM_NETGEN\\|BUILD_FEM_VTK\\|BUILD_SMESH\\|NETGEN\" /home/magik6k/lcad-wasm/freec…
Fable
thinking
I need to track down where the BUILD_FEM, BUILD_FEM_NETGEN, and BUILD_FEM_VTK options are defined—they're probably in a CMake helper file for build configuration. Let me search through the CMake tree and source files to locate them.
Fable
Let me find where the FEM options are defined and their defaults, plus the FEM CMakeLists and SMESH setup.
Fable
⚙ Bash {"command": "grep -rn \"option(BUILD_FEM\\|BUILD_FEM_NETGEN\\|BUILD_FEM_VTK\\|BUILD_SMESH\\|FREECAD_USE_SMESH\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/ /home/magik6k/lcad-wasm/freecad-port/FreeCAD/CMakeLists.txt 2>/dev/null", "d…
Fable
⚙ Bash {"command": "grep -rn \"BUILD_FEM\\|FEM\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake", "description": "FEM options in InitializeFreeCADBuildOptions"}
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/FreeCAD_Helpers/SetupSalomeSMESH.cmake"}
Fable
thinking
I need to dig into the InitializeFreeCADBuildOptions.cmake file, specifically the section around lines 140-230, to get a clearer picture of how the FEM, SMESH, and EMSCRIPTEN configurations interact.
Fable
This is very revealing. Let me read the key section of InitializeFreeCADBuildOptions.cmake around lines 140-230.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake", "offset": 140, "limit": 95}
Fable
⚙ Bash {"command": "grep -rn \"EMSCRIPTEN\\|WASM\\|__EMSCRIPTEN__\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake", "description": "EMSCRIPTEN gating in build options"}
Fable
thinking
I see that the port has already added `AND NOT EMSCRIPTEN` at line 218, which means SMESH and salomesmesh are completely disabled in the wasm build, and `BUILD_FEM_VTK` only gets enabled inside the `SetupSalomeSMESH` function that's guarded by the FREECAD_USE_ condition.
Now I need to check the Fem module's CMakeLists to see how these flags control which sources are included and how `SetupSalomeSMESH` is conditionally invoked.
Fable
Key finding: line 218 already has `AND NOT EMSCRIPTEN`. Let me read the Fem module CMakeLists and check the wasm build config.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/CMakeLists.txt"}
Fable
thinking
I'm looking at the Fem module's CMakeLists structure to understand how the App library builds and whether FemMesh depends on SMESH. The App directory is where the C++ Fem module gets compiled, and I need to check the App/CMakeLists.txt file to see how VTK and SMESH are gated for the library.
Fable
Now let me examine the Fem App C++ CMakeLists to see SMESH/VTK dependency, and check the wasm build config for the port.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/App/CMakeLists.txt"}
Fable
thinking
The Fem C++ App library has hard dependencies on SMESH components like StdMeshers, SMDS, and SMESHDS unless FREECAD_USE_EXTERNAL_SMESH is enabled, and this becomes problematic since SMESH gets excluded when building for Emscripten, affecting FemMesh and related implementation files that rely heavily on it.
This creates a fundamental incompatibility: the Fem App's CMakeLists unconditionally requires SMESH, but the Emscripten build disables it entirely, which means linking would fail due to missing SMESH libraries and headers. To make FEM viable under WebAssembly, I'd need to either port SMESH as well or add conditional compilation to strip out SMESH-dependent code and build just the core FEM …
Fable
SMESH is hard-linked in Fem/App (lines 26-28, 231). Since `BUILD_SMESH=OFF` under EMSCRIPTEN, this is a real conflict. Let me check the FEM C++ init, existing wasm guards in Fem, the port's build config, and do web searches.
Fable
⚙ Bash {"command": "grep -rln \"__EMSCRIPTEN__\\|FC_OS_WASM\\|EMSCRIPTEN\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/ 2>/dev/null; echo \"=== count SMESH includes in Fem App ===\"; grep -rl \"SMESH\\|SMDS\\|SMESHDS\\|StdMeshers\" /h…
Fable
⚙ Bash {"command": "grep -rn \"InitApplication\\|new .*::init\\|::init()\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/App/AppFem.cpp | head -80", "description": "List type registrations in AppFem.cpp"}
Fable
⚙ Bash {"command": "find /home/magik6k/lcad-wasm/freecad-port -maxdepth 3 -iname \"*.sh\" -path \"*wasm*\" -o -maxdepth 3 -iname \"*emscripten*\" 2>/dev/null | head; echo \"=== look for build/deploy scripts with -DBUILD_ ===\"; grep -rln \"DBUILD_…
Fable
thinking
I'm checking whether the FemPost types are conditionally compiled based on the VTK build flag, so I need to look at the AppFem.cpp registration code around lines 197-222 and see if there's a `#ifdef FC_USE_VTK` guard protecting them. I should also examine the build configuration scripts to understand how this is set up.
Fable
Let me check the AppFem.cpp VTK guards and find the FreeCAD wasm cmake configure invocation.
Fable
⚙ Bash {"command": "grep -n \"FC_USE_VTK\\|#ifdef\\|#ifndef\\|#else\\|#endif\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/App/AppFem.cpp | head -40", "description": "Check VTK ifdef guards in AppFem.cpp"}
Fable
⚙ Bash {"command": "grep -rln \"DBUILD_FEM\\|BUILD_FEM\\|emcmake\\|BUILD_MESH\\|DBUILD_GUI\\|configure\" /home/magik6k/lcad-wasm/freecad-port/ --include=\"*.sh\" 2>/dev/null | grep -iv \"/FreeCAD/\" | head -30; echo \"=== all sh at top ===\"; ls -…
Fable
thinking
AppFem.cpp already guards the FemPost* type registration behind `#ifdef FC_USE_VTK`, so under WebAssembly where that flag is undefined, these types won't be registered at all. This means when loading a document containing FemPostPipeline or FemPostWarpVectorFilter objects, the loader will fail since it can't find those types.
Now I need to trace through the WebAssembly build configuration to understand how FC_USE_VTK gets set or unset. Let me look at the emcmake/cmake invocation for the FreeCAD wasm build and check the configure-stage2.sh script to see where the actual FreeCAD configuration happens.
Fable
⚙ Read {"file_path": "/home/magik6k/lcad-wasm/freecad-port/toolchain/configure-stage2.sh"}
Fable
⚙ Bash {"command": "grep -rln \"BUILD_FEM\\|BUILD_MESH_PART\\|DBUILD_GUI\\|emcmake\\|EMSCRIPTEN\" /home/magik6k/lcad-wasm/freecad-port/ 2>/dev/null | grep -iv \"/FreeCAD/src/\\|/FreeCAD/cMake/\\|/FreeCAD/package\\|.git/\" | head -40", "description…
Fable
thinking
Looking at the actual build configuration, FEM is disabled and SMESH is excluded entirely. The port already has logic to skip SMESH when building for EMSCRIPTEN, which explains why it's not available in the current setup.
The real constraint is that upstream FEM unconditionally links against SMESH libraries in its CMakeLists.txt, so enabling FEM without SMESH would fail immediately due to missing targets and headers. Upstream doesn't support a minimal FEM configuration—it always bundles FEM with SMESH and VTK for post-processing, with only Netgen as an optional component. This means there's no clean decoupling path in the original codebase to work from.
For wasm support, I'd need to add EM …
Fable
Now I have the actual wasm build config. FEM is currently `-DBUILD_FEM=OFF`. Let me verify the depth of SMESH coupling in the always-compiled FemBase sources, and load WebSearch for prior art.
Fable
⚙ Bash {"command": "echo \"=== FemMesh.h SMESH includes ===\"; grep -n \"SMESH\\|SMDS\\|include\" /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/Mod/Fem/App/FemMesh.h | head -30; echo; echo \"=== CheckInterModuleDependencies FEM ===\"; grep -n \…
Fable
⚙ ToolSearch {"query": "select:WebSearch", "max_results": 3}
Fable
thinking
FemMesh.h directly includes headers for element types and version information, and since FemMesh is compiled as part of the core Fem library and used throughout the codebase via PropertyFemMesh, this creates a fundamental compile-time dependency on SMESH across the entire Fem App library.
However, SMESH_Version.h is generated through a configuration step that gets skipped when building for WebAssembly (FREECAD_USE_SMESH=OFF), which would cause the include to fail. Since BUILD_FEM only requires BUILD_PART and that's already enabled in the wasm build, the issue is specifically this hard dependency on SMESH headers that aren't available in that configuration.
Fable
Let me run the prior-art web searches.
Fable
⚙ WebSearch {"query": "FreeCAD FEM minimal build without VTK Netgen SMESH BUILD_FEM_VTK"}
Fable
⚙ WebSearch {"query": "SMESH salome mesh emscripten webassembly build"}
Fable
thinking
The web searches confirm there's no prior art for SMESH running on emscripten, and the FreeCAD community has documented that disabling BUILD_FEM_NETGEN causes cascading DLL failures since SMESH is a fundamental data structure that can't be toggled off. BUILD_FEM_VTK is the only real user-facing flag available, which controls FEM post-processing and VTK visualization. I have what I need for a comprehensive answer now. I should quickly scan the salomesmesh CMakeLists to confirm those transitive dependencies on MED and HDF5 that would block wasm compilation.
Fable
Let me confirm the transitive deps that BUILD_FEM/SMESH pulls (MED, HDF5) via the bundled salomesmesh, which is the wasm-blocker rationale.
Fable
⚙ Bash {"command": "echo \"=== salomesmesh dir present? ===\"; ls -d /home/magik6k/lcad-wasm/freecad-port/FreeCAD/src/3rdParty/salomesmesh 2>/dev/null && echo PRESENT; echo \"=== MED/HDF5/pthread refs in salomesmesh CMake ===\"; grep -rn \"MED\\|H…
Fable
thinking
So salomesmesh depends on HDF5, MED, VTK, and OCCT libraries, and it's controlled by the `BUILD_SMESH` flag which is disabled for EMSCRIPTEN builds, meaning the entire mesh component gets excluded from the WebAssembly version. Now I have the full picture of what's being built and what's not.
For FEM specifically, the `BUILD_FEM` option defaults to ON and only requires BUILD_PART, which is already enabled in the wasm build. The Netgen mesher (`BUILD_FEM_NETGEN`) defaults to OFF on non-MSVC platforms, so it's already excluded from wasm. However, `BUILD_FEM_VTK` is automatically enabled whenever SMESH is used, which adds VTK-dependent post-processing tools and registers additional C++ types fo …
Fable
I now have a complete picture. Let me compile the structured findings.
Fable
⚙ StructuredOutput {"area": "Upstream FEM build system + minimal/wasm-FEM prior art", "verdict": "Upstream FEM is hard-wired to SMESH (mandatory) + VTK + MED/HDF5, with only Netgen and VTK-post as optional knobs; there is no flag-only \"minimal FEM\" and no S…