Fix KiCad WASM build and runtime errors

Build improvements:
- Change -O0 to -O1 to fix "local count too large" asyncify error
- Add wasm-emscripten-finalize to host (Docker OOMs on large WASM)
- Use -gseparate-dwarf for smaller main binary with debug info
- Build native protoc for code generation
- Add more functions to asyncify removelist

Runtime fixes:
- Add inject-dyncall-shims.sh to fix "dynCall_* is not defined" in Emscripten 4.x
- Update wxwidgets with browserInfo.name fix

New stubs and bindings:
- Add Embind bindings for JavaScript interop
- Add stubs for scripting, API plugin, PCB frame, navlib

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-12-22 16:30:53 +01:00
commit 9c271996c2
17 changed files with 699 additions and 13 deletions

View file

@ -0,0 +1,120 @@
/*
* API Plugin stubs for KiCad WASM build
* API plugins and server are not available in WASM (no sockets/IPC)
*/
#include <api/api_plugin_manager.h>
#include <api/api_server.h>
// Forward declare and provide minimal definition for KINNG_REQUEST_SERVER
// This is needed because unique_ptr requires a complete type for deletion
class KINNG_REQUEST_SERVER {};
// Event definitions
wxDEFINE_EVENT( API_REQUEST_EVENT, wxCommandEvent );
wxDEFINE_EVENT( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, wxCommandEvent );
wxDEFINE_EVENT( EDA_EVT_PLUGIN_AVAILABILITY_CHANGED, wxCommandEvent );
// Static member initialization
wxString KICAD_API_SERVER::s_logFileName;
// KICAD_API_SERVER stub implementation
KICAD_API_SERVER::KICAD_API_SERVER() :
m_readyToReply( false )
{
// m_server intentionally left as nullptr - API server not available in WASM
}
KICAD_API_SERVER::~KICAD_API_SERVER()
{
// m_server is nullptr, nothing to clean up
}
void KICAD_API_SERVER::Start()
{
// No-op: API server not available in WASM
}
void KICAD_API_SERVER::Stop()
{
// No-op
}
bool KICAD_API_SERVER::Running() const
{
return false;
}
void KICAD_API_SERVER::RegisterHandler( API_HANDLER* aHandler )
{
(void)aHandler;
}
void KICAD_API_SERVER::DeregisterHandler( API_HANDLER* aHandler )
{
(void)aHandler;
}
std::string KICAD_API_SERVER::SocketPath() const
{
return "";
}
void KICAD_API_SERVER::onApiRequest( std::string* aRequest )
{
(void)aRequest;
}
void KICAD_API_SERVER::handleApiEvent( wxCommandEvent& aEvent )
{
(void)aEvent;
}
void KICAD_API_SERVER::log( const std::string& aOutput )
{
(void)aOutput;
}
// API_PLUGIN_MANAGER stub implementation
API_PLUGIN_MANAGER::API_PLUGIN_MANAGER( wxEvtHandler* aParent ) :
m_parent( aParent ),
m_lastPid( 0 ),
m_raiseTimer( nullptr )
{
}
void API_PLUGIN_MANAGER::ReloadPlugins()
{
// No-op: plugins not available in WASM
}
void API_PLUGIN_MANAGER::RecreatePluginEnvironment( const wxString& aIdentifier )
{
(void)aIdentifier;
}
void API_PLUGIN_MANAGER::InvokeAction( const wxString& aIdentifier )
{
(void)aIdentifier;
}
std::optional<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetAction( const wxString& aIdentifier )
{
(void)aIdentifier;
return std::nullopt;
}
std::vector<const PLUGIN_ACTION*> API_PLUGIN_MANAGER::GetActionsForScope( PLUGIN_ACTION_SCOPE aScope )
{
(void)aScope;
return {};
}
void API_PLUGIN_MANAGER::processPluginDependencies()
{
}
void API_PLUGIN_MANAGER::processNextJob( wxCommandEvent& aEvent )
{
(void)aEvent;
}

View file

@ -0,0 +1,26 @@
/*
* SpaceNavigator plugin stubs for KiCad WASM build
* 3D mouse support is not available in WASM
*/
// Minimal definition for NL_PCBNEW_PLUGIN_IMPL
// Required because unique_ptr destructor needs a complete type
class NL_PCBNEW_PLUGIN_IMPL {};
#include <navlib/nl_pcbnew_plugin.h>
// NL_PCBNEW_PLUGIN stub implementation
NL_PCBNEW_PLUGIN::NL_PCBNEW_PLUGIN( PCB_DRAW_PANEL_GAL* aViewport )
{
(void)aViewport;
// No implementation - 3D mouse not available in WASM
}
NL_PCBNEW_PLUGIN::~NL_PCBNEW_PLUGIN()
{
}
void NL_PCBNEW_PLUGIN::SetFocus( bool aFocus )
{
(void)aFocus;
}

View file

@ -0,0 +1,73 @@
/*
* PCB Frame stubs for KiCad WASM build
* Python action plugins are not available in WASM
*
* These stubs provide implementations for methods that are declared in headers
* but whose implementations depend on scripting features not available in WASM.
*/
#include <pcb_edit_frame.h>
#include <pcb_base_frame.h>
#include <footprint.h>
#include <action_plugin.h>
#include <board.h>
class ACTION_MENU;
// PCB_EDIT_FRAME static method implementations
// These are called from panel_pcbnew_action_plugins.cpp
std::vector<LEGACY_OR_API_PLUGIN> PCB_EDIT_FRAME::GetOrderedActionPlugins()
{
// Return empty list: no plugins available in WASM
return {};
}
bool PCB_EDIT_FRAME::GetActionPluginButtonVisible( const wxString& aPluginPath, bool aPluginDefault )
{
(void)aPluginPath;
// Return the default value since we can't check settings for non-existent plugins
return aPluginDefault;
}
void PCB_EDIT_FRAME::buildActionPluginMenus( ACTION_MENU* aActionMenu )
{
(void)aActionMenu;
// No-op: action plugins not available in WASM
}
// PCB_BASE_FRAME protected method implementation
// The declaration is NOT guarded by wxUSE_FSWATCHER but the implementation IS,
// so we need a stub when filesystem watcher is not available.
#if !wxUSE_FSWATCHER
void PCB_BASE_FRAME::setFPWatcher( FOOTPRINT* aFootprint )
{
(void)aFootprint;
// No-op: filesystem watcher not available in WASM
}
#endif
// Scripting helper functions stub
// These are in pcbnew_scripting_helpers.cpp which is only compiled with KICAD_SCRIPTING
BOARD* LoadBoard( const wxString& aFileName, bool aSetActive )
{
(void)aFileName;
(void)aSetActive;
// Scripting not available in WASM
return nullptr;
}
bool SaveBoard( wxString& aFileName, BOARD* aBoard, bool aSkipSettings )
{
(void)aFileName;
(void)aBoard;
(void)aSkipSettings;
// Scripting not available in WASM
return false;
}
BOARD* CreateEmptyBoard()
{
// Scripting not available in WASM
return nullptr;
}

View file

@ -0,0 +1,43 @@
/*
* Python Manager stubs for KiCad WASM build
* Python scripting is not available in WASM
*/
#include <python_manager.h>
// Implementation
PYTHON_MANAGER::PYTHON_MANAGER( const wxString& aInterpreterPath ) :
m_interpreterPath( aInterpreterPath )
{
}
long PYTHON_MANAGER::Execute( const std::vector<wxString>& aArgs,
const std::function<void(int, const wxString&, const wxString&)>& aCallback,
const wxExecuteEnv* aEnv,
bool aSaveOutput )
{
(void)aArgs;
(void)aCallback;
(void)aEnv;
(void)aSaveOutput;
// Return 0: no process created (Python not available in WASM)
return 0;
}
wxString PYTHON_MANAGER::FindPythonInterpreter()
{
return wxEmptyString;
}
std::optional<wxString> PYTHON_MANAGER::GetPythonEnvironment( const wxString& aNamespace )
{
(void)aNamespace;
return std::nullopt;
}
std::optional<wxString> PYTHON_MANAGER::GetVirtualPython( const wxString& aNamespace )
{
(void)aNamespace;
return std::nullopt;
}

View file

@ -0,0 +1,65 @@
/*
* Scripting stubs for KiCad WASM build
* Python scripting is not available in WASM
*
* These stubs provide implementations for methods declared in pcb_scripting_tool.h
* that are called from panel_pcbnew_action_plugins.cpp
*/
#include <python/scripting/pcb_scripting_tool.h>
// Constructor/Destructor implementations
SCRIPTING_TOOL::SCRIPTING_TOOL() :
PCB_TOOL_BASE( "pcbnew.ScriptingTool" )
{
}
SCRIPTING_TOOL::~SCRIPTING_TOOL()
{
}
// Virtual method implementations
bool SCRIPTING_TOOL::Init()
{
return true;
}
void SCRIPTING_TOOL::Reset( RESET_REASON aReason )
{
(void)aReason;
}
// Static method implementations - these are called from panel_pcbnew_action_plugins.cpp
void SCRIPTING_TOOL::ReloadPlugins()
{
// No-op: Python plugins not available in WASM
}
void SCRIPTING_TOOL::ShowPluginFolder()
{
// No-op: Plugin folder not accessible in WASM
}
// Private method implementations
int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent )
{
(void)aEvent;
return 0;
}
int SCRIPTING_TOOL::showPluginFolder( const TOOL_EVENT& aEvent )
{
(void)aEvent;
return 0;
}
void SCRIPTING_TOOL::setTransitions()
{
// No transitions: scripting not available
}
// Private static method - must be provided since it's declared in header
void SCRIPTING_TOOL::callLoadPlugins()
{
// No-op: Python not available in WASM
}

View file

@ -0,0 +1,34 @@
#!/bin/bash
# Stub wasm-emscripten-finalize that just copies input to output
# The real finalize runs on the host where more RAM is available
# Handle --version query
if [[ "$1" == "--version" ]]; then
echo "wasm-emscripten-finalize version 121"
exit 0
fi
# Parse arguments to find input/output files
# wasm-emscripten-finalize <input.wasm> -o <output.wasm> [options]
INPUT=""
OUTPUT=""
PREV_ARG=""
for arg in "$@"; do
if [ "$PREV_ARG" = "-o" ]; then
OUTPUT="$arg"
elif [[ "$arg" != -* ]] && [[ "$arg" == *.wasm ]]; then
INPUT="$arg"
fi
PREV_ARG="$arg"
done
# Copy input to output (no transformation)
if [ -n "$INPUT" ] && [ -n "$OUTPUT" ]; then
cp "$INPUT" "$OUTPUT"
elif [ -n "$INPUT" ]; then
# In-place mode: nothing to do
:
fi
exit 0