diff --git a/docker/Dockerfile b/docker/Dockerfile index 69034be..57a73c1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,6 +13,10 @@ RUN apt-get update && apt-get install -y \ curl \ git \ python3 \ + python3-dev \ + protobuf-compiler \ + swig \ + unixodbc-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /workspace diff --git a/docs/KICAD_WASM_BUILD.md b/docs/KICAD_WASM_BUILD.md new file mode 100644 index 0000000..d95fb3d --- /dev/null +++ b/docs/KICAD_WASM_BUILD.md @@ -0,0 +1,198 @@ +# KiCad WASM Build State + +## Build Output + +| File | Size | Description | +|------|------|-------------| +| `pcbnew.wasm` | 15MB | WebAssembly binary | +| `pcbnew.js` | 146KB | JS loader/glue | + +Location: `build-wasm/kicad-pcbnew/pcbnew/` + +## Build Command + +```bash +# Full build (clean) +./scripts/kicad/build-pcbnew.sh + +# Incremental build +./scripts/kicad/build-pcbnew.sh --no-clean --skip-deps + +# Inside Docker +docker compose -f docker/docker-compose.yml exec kicad-wasm-builder \ + /workspace/scripts/kicad/build-pcbnew.sh +``` + +## Build Configuration + +``` +Memory: 256MB initial, 4GB max (ALLOW_MEMORY_GROWTH) +Threads: pthreads with pool of 4 +Async: ASYNCIFY enabled (64KB stack) +Graphics: wxUniversal + Cairo GAL +``` + +--- + +## KiCad Fork Modifications (`/kicad`) + +68 files modified. Grouped by category: + +### CMake Find Modules (`cmake/Find*.cmake`) + +Cross-compilation support - `find_library()` fails in WASM, need direct paths. + +| File | Change | +|------|--------| +| `FindCairo.cmake` | Direct path lookup for WASM | +| `FindFontconfig.cmake` | Mark unavailable (no system fonts in browser) | +| `FindGLEW.cmake` | Mark not needed (WebGL handles extensions) | +| `FindGLM.cmake` | Direct path lookup | +| `FindOCC.cmake` | OpenCASCADE direct path | +| `FindPixman.cmake` | Direct path lookup | +| `FindSPNAV.cmake` | 3D mouse not available in browser | +| `FindZSTD.cmake` | Direct path for cross-compilation | +| `Findlibgit2.cmake` | Headers only (functions stubbed) | +| `Findngspice.cmake` | SPICE not available | +| `Findnng.cmake` | IPC not supported in browser | +| `FindwxWidgets.cmake` | Skip library existence checks for WASM | + +### Main CMakeLists.txt + +- WASM port detection (`if(EMSCRIPTEN)`) +- `KICAD_SCRIPTING` OFF by default for EMSCRIPTEN +- `KICAD_IPC_API` OFF by default for EMSCRIPTEN +- Fontconfig disabled, sets `KICAD_USE_FONTCONFIG=0` +- Python/SWIG only when `KICAD_SCRIPTING` enabled +- 3D viewer subdirectory excluded for WASM + +### IPC API Guards (`#ifdef KICAD_IPC_API`) + +Protobuf serialization code disabled when IPC API off. + +**Headers:** +- `include/api/api_utils.h` +- `pcbnew/api/api_pcb_utils.h` + +**Common:** +- `common/eda_shape.cpp` - Serialize/Deserialize methods +- `common/eda_text.cpp` - Serialize/Deserialize methods +- `common/netclass.cpp` - Protobuf includes + +**PCBnew objects:** +- `pcbnew/board_connected_item.cpp` +- `pcbnew/board_item.cpp` +- `pcbnew/board_stackup_manager/board_stackup.cpp` +- `pcbnew/footprint.cpp` +- `pcbnew/pad.cpp` +- `pcbnew/padstack.cpp` +- `pcbnew/pcb_dimension.cpp` +- `pcbnew/pcb_field.cpp` +- `pcbnew/pcb_group.cpp` +- `pcbnew/pcb_shape.cpp` +- `pcbnew/pcb_text.cpp` +- `pcbnew/pcb_textbox.cpp` +- `pcbnew/pcb_track.cpp` +- `pcbnew/zone.cpp` + +### Python Scripting Guards (`#ifdef KICAD_SCRIPTING`) + +Python.h and pybind11 not available in WASM. + +- `pcbnew/pcbnew.cpp` - PyInit, KIFACE_SCRIPTING_LEGACY +- `pcbnew/pcbnew_settings.cpp` - pybind11 include +- `pcbnew/pcb_edit_frame.cpp` - Python sync functions, scripting includes +- `pcbnew/menubar_pcb_editor.cpp` - Scripting menu items +- `pcbnew/toolbars_pcb_editor.cpp` - Scripting toolbar + +### Altium Plugin Disabled (`#ifndef __EMSCRIPTEN__`) + +`char_traits` specialization issue in Emscripten. + +- `pcbnew/CMakeLists.txt` - `add_subdirectory(pcb_io/altium)` conditional +- `pcbnew/pcb_io/pcb_io_mgr.cpp` - Altium includes and plugin registration + +### 3D Viewer Disabled + +Requires GLU and OpenGL fixed-function pipeline (not in WebGL). + +- `CMakeLists.txt` - `add_subdirectory(3d-viewer)` conditional +- `pcbnew/CMakeLists.txt` - `3d-viewer` library link conditional + +### Fontconfig + +No system font access in browser. + +- `common/font/fontconfig.cpp` - Wrapped with `#if KICAD_USE_FONTCONFIG` +- `include/font/fontconfig.h` - Conditional compilation + +### Third-party Libraries + +- `thirdparty/libcontext/libcontext.cpp` - WASM stub for coroutines (returns nullptr) +- `thirdparty/libcontext/libcontext.h` - `LIBCONTEXT_PLATFORM_wasm32` detection +- `thirdparty/lemon/CMakeLists.txt` - Build fixes + +### Other Fixes + +- `pcbnew/dialogs/panel_setup_layers.cpp` - `GetCurrentSelection()` → `GetSelection()` (wxUniversal) +- `include/gal/opengl/kiglew.h` - GLEW version stub for WASM +- `common/gal/CMakeLists.txt` - GAL library adjustments +- `common/CMakeLists.txt` - Common library adjustments +- `libs/kiplatform/CMakeLists.txt` - WASM platform support +- `scripting/CMakeLists.txt` - Conditional on KICAD_SCRIPTING + +--- + +## wxWidgets Fork Modifications (`/wxwidgets`) + +| File | Change | Reason | +|------|--------|--------| +| `include/wx/wasm/glcanvas.h` | Moved `Show()` from protected to public | KiCad calls `Show()` on wxGLCanvas | +| `src/wasm/utils.cpp` | Added `wxFindWindowAtPointer()` | Missing function used by cshelp | + +--- + +## Stub Libraries (`/wasm/stubs/`) + +| File | Symbols | Reason | +|------|---------|--------| +| `libgit2_stub.c` | `git_libgit2_init`, `git_repository_*`, `git_reference_*`, `git_oid_*`, `git_error_last` | Git not available in browser | +| `curl_stub.c` | `curl_global_init`, `curl_global_cleanup` | Network via JS fetch instead | + +wxWidgets stubs (created by build script): +- `libwx_wasmunivu_richtext-3.2.a` - Empty, not used by KiCad +- `libwx_wasmunivu_webview-3.2.a` - Empty, not used by KiCad + +--- + +## Disabled Features Summary + +| Feature | Reason | +|---------|--------| +| Python Scripting | pybind11/Python.h not available in WASM | +| 3D Viewer | GLU and OpenGL fixed-function pipeline not in WebGL | +| Altium Plugin | `char_traits` Emscripten issue | +| SPICE | ngspice not ported | +| IPC API | IPC not supported in browser | +| GitHub Plugin | Network needs JS bridge | +| Fontconfig | No system font access | +| 3D Mouse (SPNAV) | Hardware not accessible | + +--- + +## Dependencies + +Built via `scripts/deps/build-all-deps.sh`: +- zstd, glm, freetype, harfbuzz, pixman, cairo +- OpenCASCADE (geometry kernel) +- Protobuf (headers only) +- Boost (headers only) + +--- + +## Next Steps + +1. Create HTML wrapper to load pcbnew.js +2. Implement file system access (MEMFS or IDBFS) +3. Test basic UI rendering +4. Bridge network operations to JS fetch diff --git a/kicad b/kicad index 4bfed3f..e6a59ab 160000 --- a/kicad +++ b/kicad @@ -1 +1 @@ -Subproject commit 4bfed3f1746e8cc0a7d942767770f56fa28b393c +Subproject commit e6a59ab94a325f5df44bca6f8b7b7aa9d75fbf03 diff --git a/scripts/build-wxuniversal-wasm.sh b/scripts/build-wxuniversal-wasm.sh index 5d2bfd8..9a5013d 100755 --- a/scripts/build-wxuniversal-wasm.sh +++ b/scripts/build-wxuniversal-wasm.sh @@ -95,6 +95,27 @@ echo "" echo "=== Building wxWidgets ===" emmake make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu) +# Create library symlinks (remove -emscripten suffix for CMake compatibility) +echo "" +echo "=== Creating library symlinks ===" +cd "$BUILD_DIR/lib" +for lib in *-emscripten.a; do + if [ -f "$lib" ]; then + newname="${lib/-emscripten/}" + ln -sf "$lib" "$newname" + fi +done + +# Create stub libraries for components wx-config reports but we didn't build +# KiCad doesn't use these directly +echo "Creating stub libraries..." +for stub in richtext webview; do + emar rcs "libwx_wasmunivu_${stub}-3.2.a" + ln -sf "libwx_wasmunivu_${stub}-3.2.a" "libwx_wasmunivu_${stub}-3.2-emscripten.a" +done + +cd "$BUILD_DIR" + echo "" echo "=== Build complete ===" ls -lh "$BUILD_DIR"/lib/*.a 2>/dev/null || echo "Libraries built in $BUILD_DIR/lib" diff --git a/scripts/deps/build-boost.sh b/scripts/deps/build-boost.sh index 9e4ed29..d40d54f 100755 --- a/scripts/deps/build-boost.sh +++ b/scripts/deps/build-boost.sh @@ -57,10 +57,10 @@ log_info "Building Boost ${BOOST_VERSION} for WASM..." cd "${BOOST_DIR}" -# Bootstrap b2 if needed +# Bootstrap b2 if needed (use gcc for native build of b2) if [ ! -f "b2" ]; then log_info "Bootstrapping b2..." - ./bootstrap.sh --with-toolset=clang + ./bootstrap.sh --with-toolset=gcc fi # Create user-config.jam for Emscripten diff --git a/scripts/deps/build-cairo.sh b/scripts/deps/build-cairo.sh index 40a11e4..15f52ea 100755 --- a/scripts/deps/build-cairo.sh +++ b/scripts/deps/build-cairo.sh @@ -78,6 +78,9 @@ sys_root = '${SYSROOT}' pkg_config_libdir = '${SYSROOT}/lib/pkgconfig' [built-in options] +default_library = 'static' +b_staticpic = false +b_pie = false c_args = ['-pthread', '-I${SYSROOT}/include', '-I${SYSROOT}/include/freetype2', '-I${SYSROOT}/include/pixman-1'] c_link_args = ['-pthread', '-L${SYSROOT}/lib'] pkg_config_path = '${SYSROOT}/lib/pkgconfig' @@ -101,7 +104,9 @@ meson setup "${CAIRO_DIR}" \ -Dzlib=enabled \ -Dtests=disabled \ -Dspectre=disabled \ - -Dsymbol-lookup=disabled + -Dsymbol-lookup=disabled \ + -Dfreetype2:default_library=static \ + -Dlibpng:default_library=static JOBS=${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)} ninja -j${JOBS} diff --git a/scripts/deps/build-ngspice.sh b/scripts/deps/build-ngspice.sh index 436558b..2a5e5dc 100755 --- a/scripts/deps/build-ngspice.sh +++ b/scripts/deps/build-ngspice.sh @@ -58,15 +58,18 @@ export CFLAGS="-pthread" export CXXFLAGS="-pthread" export LDFLAGS="-pthread" -# Configure ngspice as a shared library for KiCad integration +# Configure ngspice as a static library for WASM +# Note: --with-ngshared requires shared libs which WASM doesn't support +# We build static lib instead emconfigure "${NGSPICE_DIR}/configure" \ --prefix="${SYSROOT}" \ --host=wasm32-unknown-emscripten \ --build=$(uname -m)-linux-gnu \ - --enable-shared \ - --with-ngshared \ + --disable-shared \ + --enable-static \ --disable-debug \ --disable-dependency-tracking \ + --disable-openmp \ --enable-cider \ --enable-xspice \ --without-x \ diff --git a/scripts/kicad/build-pcbnew.sh b/scripts/kicad/build-pcbnew.sh index 5409acf..2ebd7a8 100755 --- a/scripts/kicad/build-pcbnew.sh +++ b/scripts/kicad/build-pcbnew.sh @@ -85,6 +85,38 @@ fi mkdir -p "${KICAD_BUILD}" cd "${KICAD_BUILD}" +# Step 6.1: Build stub libraries for missing symbols +STUBS_DIR="${PROJECT_ROOT}/wasm/stubs" +STUBS_BUILD="${BUILD_ROOT}/stubs" +mkdir -p "${STUBS_BUILD}" + +log_info "Building stub libraries..." +# Compile libgit2 stub +emcc -c "${STUBS_DIR}/libgit2_stub.c" -o "${STUBS_BUILD}/libgit2_stub.o" +emar rcs "${STUBS_BUILD}/libgit2_stub.a" "${STUBS_BUILD}/libgit2_stub.o" + +# Compile curl stub +emcc -c "${STUBS_DIR}/curl_stub.c" -o "${STUBS_BUILD}/curl_stub.o" +emar rcs "${STUBS_BUILD}/libcurl_stub.a" "${STUBS_BUILD}/curl_stub.o" + +log_info "Stub libraries built" + +# Step 6.5: Verify WASM support is in KiCad fork +# The kicad submodule should already have WASM port detection and kiplatform support +KICAD_CMAKE="${KICAD_DIR}/CMakeLists.txt" +if ! grep -q "msw|qt|gtk|osx|wasm" "${KICAD_CMAKE}"; then + log_error "KiCad fork is missing WASM port detection support." + log_error "Please ensure the kicad submodule has WASM modifications." + exit 1 +fi +KIPLATFORM_CMAKE="${KICAD_DIR}/libs/kiplatform/CMakeLists.txt" +if ! grep -q "KICAD_WX_PORT STREQUAL wasm" "${KIPLATFORM_CMAKE}"; then + log_error "KiCad fork is missing kiplatform WASM support." + log_error "Please ensure the kicad submodule has WASM modifications." + exit 1 +fi +log_info "KiCad WASM support verified" + # Step 7: Configure KiCad with CMake # We use CMAKE_MODULE_PATH to inject our compatibility layer log_info "Configuring KiCad with CMake..." @@ -92,19 +124,16 @@ emcmake cmake "${KICAD_DIR}" \ -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DCMAKE_INSTALL_PREFIX="${SYSROOT}" \ -DCMAKE_MODULE_PATH="${WASM_LAYER}/cmake" \ + -DSYSROOT="${SYSROOT}" \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -DKICAD_USE_PLATFORM_WASM=1 -I${SYSROOT}/include" \ - -DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -I${SYSROOT}/include" \ - -DCMAKE_EXE_LINKER_FLAGS="-pthread -sASYNCIFY=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -L${SYSROOT}/lib" \ + -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1 -I${SYSROOT}/include" \ + -DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include" \ + -DCMAKE_EXE_LINKER_FLAGS="-pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a" \ -DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \ -DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \ \ -DKICAD_BUILD_QA_TESTS=OFF \ - -DKICAD_SCRIPTING=OFF \ - -DKICAD_SCRIPTING_PYTHON3=OFF \ - -DKICAD_SCRIPTING_WXPYTHON=OFF \ -DKICAD_SPICE=OFF \ - -DKICAD_USE_OCC=OFF \ -DKICAD_USE_EGL=OFF \ -DKICAD_USE_BUNDLED_GLEW=ON \ \ @@ -112,6 +141,26 @@ emcmake cmake "${KICAD_DIR}" \ -DZSTD_INCLUDE_DIR="${SYSROOT}/include" \ -DZSTD_LIBRARY="${SYSROOT}/lib/libzstd.a" \ -DGLM_INCLUDE_DIR="${SYSROOT}/include" \ + -DBOOST_ROOT="${SYSROOT}" \ + -DBoost_INCLUDE_DIR="${SYSROOT}/include" \ + -DBoost_LIBRARY_DIR="${SYSROOT}/lib" \ + -DBoost_NO_SYSTEM_PATHS=ON \ + -DBoost_NO_BOOST_CMAKE=ON \ + -DFREETYPE_INCLUDE_DIR_ft2build="${SYSROOT}/include/freetype2" \ + -DFREETYPE_INCLUDE_DIR_freetype2="${SYSROOT}/include/freetype2" \ + -DFREETYPE_LIBRARY="${SYSROOT}/lib/libfreetype.a" \ + -DHarfBuzz_INCLUDE_DIR="${SYSROOT}/include/harfbuzz" \ + -DHarfBuzz_LIBRARY="${SYSROOT}/lib/libharfbuzz.a" \ + -DOCC_INCLUDE_DIR="${SYSROOT}/include/opencascade" \ + -DOCC_LIBRARY_DIR="${SYSROOT}/lib" \ + -DProtobuf_INCLUDE_DIR="${SYSROOT}/include" \ + -DProtobuf_LIBRARY="${SYSROOT}/lib/libprotobuf.a" \ + -DProtobuf_LITE_LIBRARY="${SYSROOT}/lib/libprotobuf-lite.a" \ + -DODBC_CONFIG:STRING="stub-for-wasm" \ + -DODBCLIB:STRING="" \ + -DODBC_CFLAGS:STRING="" \ + -DODBC_LINK_FLAGS:STRING="" \ + -DODBC_LIBRARIES:STRING="" \ \ -DBUILD_GITHUB_PLUGIN=OFF \ -DKICAD_PCM=OFF diff --git a/wasm/cmake/FindFontconfig.cmake b/wasm/cmake/FindFontconfig.cmake new file mode 100644 index 0000000..52740a8 --- /dev/null +++ b/wasm/cmake/FindFontconfig.cmake @@ -0,0 +1,29 @@ +# FindFontconfig.cmake - Stub for WASM builds +# Fontconfig is not available in WASM - system fonts are not accessible from browser +# We provide stub values so CMake configuration succeeds + +if(EMSCRIPTEN) + message(STATUS "Fontconfig not available for WASM build (system fonts not accessible in browser)") + + # Set variables to indicate Fontconfig is "found" but disabled + set(Fontconfig_FOUND TRUE) + set(FONTCONFIG_FOUND TRUE) + + # Provide empty values - actual code should be conditionally compiled + set(Fontconfig_INCLUDE_DIR "") + set(Fontconfig_LIBRARY "") + set(Fontconfig_LIBRARIES "") + set(FONTCONFIG_INCLUDE_DIR "") + set(FONTCONFIG_LIBRARY "") + set(FONTCONFIG_LIBRARIES "") + + # Create an imported target + if(NOT TARGET Fontconfig::Fontconfig) + add_library(Fontconfig::Fontconfig INTERFACE IMPORTED) + endif() + + mark_as_advanced(Fontconfig_INCLUDE_DIR Fontconfig_LIBRARY) +else() + # For non-WASM builds, use the system FindFontconfig + include(${CMAKE_ROOT}/Modules/FindFontconfig.cmake OPTIONAL) +endif() diff --git a/wasm/cmake/FindGLEW.cmake b/wasm/cmake/FindGLEW.cmake new file mode 100644 index 0000000..9b115d7 --- /dev/null +++ b/wasm/cmake/FindGLEW.cmake @@ -0,0 +1,27 @@ +# FindGLEW.cmake - Stub for WASM builds +# GLEW is not needed for WASM/WebGL builds - OpenGL ES / WebGL is used directly +# Emscripten provides OpenGL ES 2.0/3.0 support through WebGL + +if(EMSCRIPTEN) + message(STATUS "GLEW stubbed for WASM build (using Emscripten WebGL)") + + # Set variables to indicate GLEW is "found" (stubbed) + set(GLEW_FOUND TRUE) + set(GLEW_LIBRARY "") + set(GLEW_LIBRARIES "") + set(GLEW_INCLUDE_DIR "") + set(GLEW_INCLUDE_DIRS "") + + # For WASM/WebGL, we use OpenGL ES 2.0 via Emscripten + # The flags are added in linker options + # -sUSE_WEBGL2=1 or -sFULL_ES2=1 / -sFULL_ES3=1 + + mark_as_advanced( + GLEW_FOUND + GLEW_LIBRARY + GLEW_INCLUDE_DIR + ) +else() + # For non-WASM builds, use the system FindGLEW + include(${CMAKE_ROOT}/Modules/FindGLEW.cmake OPTIONAL) +endif() diff --git a/wasm/cmake/FindOCC.cmake b/wasm/cmake/FindOCC.cmake new file mode 100644 index 0000000..b4367ab --- /dev/null +++ b/wasm/cmake/FindOCC.cmake @@ -0,0 +1,27 @@ +# FindOCC.cmake - Find OpenCASCADE for WASM builds +# Uses our pre-built OpenCASCADE from the sysroot + +if(EMSCRIPTEN) + # For WASM builds, use our built OpenCASCADE + set(OCC_INCLUDE_DIR "${SYSROOT}/include/opencascade") + set(OCC_LIBRARY_DIR "${SYSROOT}/lib") + + # Find all OCC libraries + file(GLOB OCC_LIBRARIES "${SYSROOT}/lib/libTK*.a") + + if(EXISTS "${OCC_INCLUDE_DIR}" AND OCC_LIBRARIES) + set(OCC_FOUND TRUE) + set(OpenCASCADE_FOUND TRUE) + # Version from Standard_Version.hxx + set(OCC_VERSION_STRING "7.8.0") + message(STATUS "Found OpenCASCADE ${OCC_VERSION_STRING} for WASM: ${OCC_INCLUDE_DIR}") + else() + set(OCC_FOUND FALSE) + message(FATAL_ERROR "OpenCASCADE not found in sysroot. Run: ./scripts/deps/build-opencascade.sh") + endif() + + mark_as_advanced(OCC_INCLUDE_DIR OCC_LIBRARIES OCC_LIBRARY_DIR) +else() + # For non-WASM builds, use the original FindOCC + # This should not be reached for WASM builds +endif() diff --git a/wasm/cmake/FindPythonLibs.cmake b/wasm/cmake/FindPythonLibs.cmake new file mode 100644 index 0000000..bd76952 --- /dev/null +++ b/wasm/cmake/FindPythonLibs.cmake @@ -0,0 +1,45 @@ +# FindPythonLibs.cmake - Stub for WASM builds +# Python libraries are not available/needed for WASM builds (no Python scripting) +# We provide stub values so CMake configuration succeeds + +if(EMSCRIPTEN) + message(STATUS "Python libraries stubbed for WASM build (Python scripting disabled)") + + # Find the Python interpreter first (we need it for some build scripts) + find_program(PYTHON_EXECUTABLE NAMES python3 python) + + if(PYTHON_EXECUTABLE) + # Get Python version + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" + OUTPUT_VARIABLE PYTHON_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REPLACE "." ";" VERSION_LIST "${PYTHON_VERSION_STRING}") + list(GET VERSION_LIST 0 PYTHON_VERSION_MAJOR) + list(GET VERSION_LIST 1 PYTHON_VERSION_MINOR) + else() + set(PYTHON_VERSION_MAJOR "3") + set(PYTHON_VERSION_MINOR "10") + set(PYTHON_VERSION_STRING "3.10") + endif() + + # Set variables to indicate PythonLibs is "found" (stubbed) + set(PYTHONLIBS_FOUND TRUE) + set(PythonLibs_FOUND TRUE) + + # Provide stub values - the actual libraries won't be linked + set(PYTHON_LIBRARIES "") + set(PYTHON_INCLUDE_DIRS "/usr/include/python${PYTHON_VERSION_STRING}") + set(PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIRS}") + set(PYTHON_LIBRARY "") + + mark_as_advanced( + PYTHON_LIBRARIES + PYTHON_INCLUDE_DIRS + PYTHON_LIBRARY + ) +else() + # For non-WASM builds, use the system FindPythonLibs + include(${CMAKE_ROOT}/Modules/FindPythonLibs.cmake OPTIONAL) +endif() diff --git a/wasm/cmake/FindZLIB.cmake b/wasm/cmake/FindZLIB.cmake new file mode 100644 index 0000000..1fde8cf --- /dev/null +++ b/wasm/cmake/FindZLIB.cmake @@ -0,0 +1,38 @@ +# FindZLIB.cmake - WASM builds use Emscripten's zlib port +# Emscripten provides zlib via -sUSE_ZLIB=1 flag + +if(EMSCRIPTEN) + message(STATUS "Using Emscripten's zlib port (-sUSE_ZLIB=1)") + + # Find the Emscripten cache sysroot + execute_process( + COMMAND em-config CACHE + OUTPUT_VARIABLE EMSCRIPTEN_CACHE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Set ZLIB variables + set(ZLIB_FOUND TRUE) + set(ZLIB_INCLUDE_DIR "${EMSCRIPTEN_CACHE}/sysroot/include") + set(ZLIB_INCLUDE_DIRS "${ZLIB_INCLUDE_DIR}") + + # The library will be linked via -sUSE_ZLIB=1 + # We set a placeholder for CMake checks + set(ZLIB_LIBRARY "-sUSE_ZLIB=1") + set(ZLIB_LIBRARIES "-sUSE_ZLIB=1") + set(ZLIB_VERSION_STRING "1.3.1") + + # Create imported target + if(NOT TARGET ZLIB::ZLIB) + add_library(ZLIB::ZLIB INTERFACE IMPORTED) + set_target_properties(ZLIB::ZLIB PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "-sUSE_ZLIB=1" + ) + endif() + + mark_as_advanced(ZLIB_INCLUDE_DIR ZLIB_LIBRARY) +else() + # For non-WASM builds, use the system FindZLIB + include(${CMAKE_ROOT}/Modules/FindZLIB.cmake OPTIONAL) +endif() diff --git a/wasm/cmake/Findngspice.cmake b/wasm/cmake/Findngspice.cmake new file mode 100644 index 0000000..49c0123 --- /dev/null +++ b/wasm/cmake/Findngspice.cmake @@ -0,0 +1,25 @@ +# Findngspice.cmake - Stub for WASM builds +# ngspice is not available in WASM builds (KICAD_SPICE=OFF) +# We provide stub values so CMake configuration succeeds + +if(EMSCRIPTEN OR NOT KICAD_SPICE) + message(STATUS "ngspice not available for WASM build (SPICE disabled)") + + # Set variables to indicate ngspice is "found" but disabled + set(ngspice_FOUND TRUE) + set(NGSPICE_FOUND TRUE) + + # Provide empty values + set(NGSPICE_INCLUDE_DIR "") + set(NGSPICE_LIBRARY "") + set(NGSPICE_LIBRARIES "") + + mark_as_advanced(NGSPICE_INCLUDE_DIR NGSPICE_LIBRARY) +else() + # For non-WASM builds, use the system findngspice + find_path(NGSPICE_INCLUDE_DIR ngspice/sharedspice.h) + find_library(NGSPICE_LIBRARY NAMES ngspice) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(ngspice DEFAULT_MSG NGSPICE_LIBRARY NGSPICE_INCLUDE_DIR) +endif() diff --git a/wasm/cmake/Findnng.cmake b/wasm/cmake/Findnng.cmake new file mode 100644 index 0000000..c2c0f86 --- /dev/null +++ b/wasm/cmake/Findnng.cmake @@ -0,0 +1,41 @@ +# Findnng.cmake - Stub for WASM builds +# nng (NanoMsg Next Gen) is used for IPC in KiCad +# For WASM builds, we stub this out as IPC works differently in the browser + +if(EMSCRIPTEN) + message(STATUS "nng stubbed for WASM build (IPC handled differently in browser)") + + # Set variables to indicate nng is "found" (stubbed) + set(NNG_FOUND TRUE) + set(nng_FOUND TRUE) + set(NNG_LIBRARY "") + set(NNG_LIBRARIES "") + set(NNG_INCLUDE_DIR "") + set(NNG_INCLUDE_DIRS "") + + # Create an imported target for nng + if(NOT TARGET nng::nng) + add_library(nng::nng INTERFACE IMPORTED) + endif() + + mark_as_advanced( + NNG_FOUND + NNG_LIBRARY + NNG_INCLUDE_DIR + ) +else() + # For non-WASM builds, use the system Findnng or search for it + find_path(NNG_INCLUDE_DIR nng/nng.h) + find_library(NNG_LIBRARY NAMES nng) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(nng DEFAULT_MSG NNG_LIBRARY NNG_INCLUDE_DIR) + + if(NNG_FOUND AND NOT TARGET nng::nng) + add_library(nng::nng UNKNOWN IMPORTED) + set_target_properties(nng::nng PROPERTIES + IMPORTED_LOCATION "${NNG_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${NNG_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/wasm/cmake/FindwxWidgets.cmake b/wasm/cmake/FindwxWidgets.cmake new file mode 100644 index 0000000..b488da9 --- /dev/null +++ b/wasm/cmake/FindwxWidgets.cmake @@ -0,0 +1,111 @@ +# FindwxWidgets.cmake - WASM build version +# Uses wx-config to find wxWidgets for WASM cross-compilation + +if(EMSCRIPTEN AND wxWidgets_CONFIG_EXECUTABLE) + message(STATUS "Finding wxWidgets using wx-config for WASM...") + + # Get version + execute_process( + COMMAND "${wxWidgets_CONFIG_EXECUTABLE}" --version + OUTPUT_VARIABLE wxWidgets_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE RET + ) + if(NOT RET EQUAL 0) + message(FATAL_ERROR "wx-config --version failed") + endif() + + # Parse version + string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _match "${wxWidgets_VERSION_STRING}") + set(wxWidgets_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(wxWidgets_VERSION_MINOR "${CMAKE_MATCH_2}") + set(wxWidgets_VERSION_PATCH "${CMAKE_MATCH_3}") + + # Get include directories + execute_process( + COMMAND "${wxWidgets_CONFIG_EXECUTABLE}" --cxxflags + OUTPUT_VARIABLE wxWidgets_CXX_FLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # Extract include directories from cxxflags + string(REGEX MATCHALL "-I[^ ]+" _includes "${wxWidgets_CXX_FLAGS}") + set(wxWidgets_INCLUDE_DIRS "") + foreach(_inc ${_includes}) + string(REGEX REPLACE "^-I" "" _dir "${_inc}") + list(APPEND wxWidgets_INCLUDE_DIRS "${_dir}") + endforeach() + + # Get libraries + execute_process( + COMMAND "${wxWidgets_CONFIG_EXECUTABLE}" --libs all + OUTPUT_VARIABLE _wx_libs_output + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Parse libraries + set(wxWidgets_LIBRARIES "") + set(wxWidgets_LIBRARY_DIRS "") + + # Split into list + string(REPLACE " " ";" _wx_libs_list "${_wx_libs_output}") + + foreach(_item ${_wx_libs_list}) + if(_item MATCHES "^-L(.+)") + list(APPEND wxWidgets_LIBRARY_DIRS "${CMAKE_MATCH_1}") + elseif(_item MATCHES "^-l(.+)") + list(APPEND wxWidgets_LIBRARIES "${CMAKE_MATCH_1}") + elseif(_item MATCHES "\\.a$") + # Full path to static library + list(APPEND wxWidgets_LIBRARIES "${_item}") + elseif(_item MATCHES "^-s(.+)") + # Emscripten linker flags like -sUSE_LIBPNG=1 + list(APPEND wxWidgets_LIBRARIES "${_item}") + elseif(_item MATCHES "^-pthread") + # Pthread flag + list(APPEND wxWidgets_LIBRARIES "-pthread") + endif() + endforeach() + + # Get the wxWidgets root directory + get_filename_component(wxWidgets_ROOT_DIR "${wxWidgets_CONFIG_EXECUTABLE}" DIRECTORY) + + # Set the wx configuration for port detection + # For WASM, we use our own port implementation + set(_wx_selected_config "wasm-wasmuniv-unicode-static-3.2" CACHE INTERNAL "") + set(wxWidgets_FIND_STYLE "unix" CACHE INTERNAL "") + + # Set KICAD_WX_PORT to "wasm" - kiplatform will need our custom source files + set(KICAD_WX_PORT "wasm" CACHE STRING "wxWidgets port for WASM" FORCE) + + # Pre-set PLATFORM_SRCS with our WASM implementation + # This will be used by kiplatform if it doesn't recognize the port + set(KIPLATFORM_WASM_SRCS "${SYSROOT}/../../../wasm/kiplatform/ui.cpp" CACHE PATH "WASM kiplatform sources") + + # Get definitions + execute_process( + COMMAND "${wxWidgets_CONFIG_EXECUTABLE}" --cppflags + OUTPUT_VARIABLE _wx_cpp_flags + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX MATCHALL "-D[^ ]+" wxWidgets_DEFINITIONS "${_wx_cpp_flags}") + + # Set found + set(wxWidgets_FOUND TRUE) + + # Create the use file path + set(wxWidgets_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UsewxWidgets.cmake") + + message(STATUS "Found wxWidgets ${wxWidgets_VERSION_STRING} for WASM") + message(STATUS " Include dirs: ${wxWidgets_INCLUDE_DIRS}") + message(STATUS " Library dirs: ${wxWidgets_LIBRARY_DIRS}") + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(wxWidgets + REQUIRED_VARS wxWidgets_LIBRARIES wxWidgets_INCLUDE_DIRS + VERSION_VAR wxWidgets_VERSION_STRING + ) + +else() + # For non-WASM builds, use the standard FindwxWidgets + include(${CMAKE_ROOT}/Modules/FindwxWidgets.cmake OPTIONAL) +endif() diff --git a/wasm/cmake/UsewxWidgets.cmake b/wasm/cmake/UsewxWidgets.cmake new file mode 100644 index 0000000..a141e3f --- /dev/null +++ b/wasm/cmake/UsewxWidgets.cmake @@ -0,0 +1,24 @@ +# UsewxWidgets.cmake - WASM build version +# This file is included after FindwxWidgets to set up wxWidgets for the build + +if(wxWidgets_FOUND) + # Add include directories + if(wxWidgets_INCLUDE_DIRS) + include_directories(SYSTEM ${wxWidgets_INCLUDE_DIRS}) + endif() + + # Add library directories + if(wxWidgets_LIBRARY_DIRS) + link_directories(${wxWidgets_LIBRARY_DIRS}) + endif() + + # Add definitions + if(wxWidgets_DEFINITIONS) + add_definitions(${wxWidgets_DEFINITIONS}) + endif() + + # Add compiler flags + if(wxWidgets_CXX_FLAGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${wxWidgets_CXX_FLAGS}") + endif() +endif() diff --git a/wasm/kiplatform/printing.cpp b/wasm/kiplatform/printing.cpp new file mode 100644 index 0000000..862151d --- /dev/null +++ b/wasm/kiplatform/printing.cpp @@ -0,0 +1,22 @@ +/* + * WASM implementation of printing.h + * Printing is not directly supported in browser environment + */ + +#include +#include + +namespace KIPLATFORM +{ +namespace PRINTING +{ + +PRINT_RESULT PrintPDF( const std::string& aFile ) +{ + // Direct printing is not supported in browser + // User can use browser's print functionality or download the PDF + return PRINT_RESULT::UNSUPPORTED; +} + +} // namespace PRINTING +} // namespace KIPLATFORM diff --git a/wasm/patches/kicad-wasm-port.patch b/wasm/patches/kicad-wasm-port.patch new file mode 100644 index 0000000..9bb6abb --- /dev/null +++ b/wasm/patches/kicad-wasm-port.patch @@ -0,0 +1,15 @@ +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1107,7 +1107,9 @@ if( wxWidgets_FIND_STYLE STREQUAL "win32" ) + add_compile_definitions( __WXGTK__ ) + endif() + elseif( _wx_selected_config ) +- string(REGEX MATCH "(msw|qt|gtk|osx)" KICAD_WX_PORT "${_wx_selected_config}") ++ string(REGEX MATCH "(msw|qt|gtk|osx|wasm)" KICAD_WX_PORT "${_wx_selected_config}") + endif() + +-if( KICAD_WX_PORT ) ++if( KICAD_WX_PORT STREQUAL "wasm" ) ++ message( STATUS "Detected wxWidgets port: wasm (WebAssembly)") ++elseif( KICAD_WX_PORT ) + message( STATUS "Detected wxWidgets port: ${KICAD_WX_PORT}") diff --git a/wasm/patches/kiplatform-wasm.patch b/wasm/patches/kiplatform-wasm.patch new file mode 100644 index 0000000..2d758b3 --- /dev/null +++ b/wasm/patches/kiplatform-wasm.patch @@ -0,0 +1,15 @@ +--- a/libs/kiplatform/CMakeLists.txt ++++ b/libs/kiplatform/CMakeLists.txt +@@ -37,6 +37,12 @@ elseif( KICAD_WX_PORT STREQUAL gtk ) + message( STATUS "Configuring KiCad not to hide any GTK error messages" ) + string( APPEND PLATFORM_COMPILE_DEFS "-DKICAD_SHOW_GTK_MESSAGES" ) + endif() ++elseif( KICAD_WX_PORT STREQUAL wasm ) ++ # WASM port - uses stub implementations from wasm layer ++ set( PLATFORM_SRCS ++ ${PROJECT_SOURCE_DIR}/../wasm/kiplatform/ui.cpp ++ ) ++ message( STATUS "Using WASM kiplatform stub implementation" ) + endif() + + diff --git a/wasm/stubs/curl_stub.c b/wasm/stubs/curl_stub.c new file mode 100644 index 0000000..dee1af8 --- /dev/null +++ b/wasm/stubs/curl_stub.c @@ -0,0 +1,15 @@ +/* CURL stub for WebAssembly builds + * Network operations need to go through JavaScript fetch API + */ + +typedef int CURLcode; +#define CURLE_OK 0 + +CURLcode curl_global_init(long flags) { + (void)flags; + return CURLE_OK; +} + +void curl_global_cleanup(void) { + /* Nothing to clean up */ +} diff --git a/wasm/stubs/libgit2_stub.c b/wasm/stubs/libgit2_stub.c new file mode 100644 index 0000000..fb39c55 --- /dev/null +++ b/wasm/stubs/libgit2_stub.c @@ -0,0 +1,64 @@ +/* libgit2 stub for WebAssembly builds + * Git functionality is not available in browser, but KiCad needs these symbols + */ + +#include + +/* Error handling */ +typedef struct { + char *message; + int klass; +} git_error; + +static git_error stub_error = { "Git not available in WebAssembly", 0 }; + +int git_libgit2_init(void) { + return 1; /* Return 1 to indicate "initialized" */ +} + +int git_libgit2_shutdown(void) { + return 0; +} + +const git_error* git_error_last(void) { + return &stub_error; +} + +/* Repository operations - all return failure */ +int git_repository_open(void **out, const char *path) { + *out = NULL; + return -1; +} + +int git_repository_head(void **out, void *repo) { + *out = NULL; + return -1; +} + +void git_repository_free(void *repo) { + (void)repo; +} + +/* Reference operations */ +const void* git_reference_target(const void *ref) { + (void)ref; + return NULL; +} + +void git_reference_free(void *ref) { + (void)ref; +} + +/* OID operations */ +char* git_oid_tostr(char *out, size_t n, const void *id) { + if (out && n > 0) { + out[0] = '\0'; + } + return out; +} + +int git_oid_cmp(const void *a, const void *b) { + (void)a; + (void)b; + return 0; +} diff --git a/wxwidgets b/wxwidgets index 3580674..40bf50f 160000 --- a/wxwidgets +++ b/wxwidgets @@ -1 +1 @@ -Subproject commit 35806746109f6aad0783bc42b2ef0738ae97d3a3 +Subproject commit 40bf50fe4ee6938894464e6039acacb38d55a227