sym_convert wasm diet: 31.3->11.05 MB via kiface prune + link-order shims (ysync 0009)

New wasm/cli/sym_convert_stubs.cpp: first-definition-wins overrides
(--allow-multiple-definition) severing schematic load/save, the KIFONT
factory (drops newstroke + freetype/harfbuzz), and the four UI virtuals
whose bodies reference pruned typeinfo/data. New
wasm/bindings/sym_convert_embind.cpp: no-op kicadCollabOnSave so the
converter stops linking eeschema's embind object (and --bind) that rooted
the editor surface from .init_array. build-kicad-target.sh: per-app
EMBIND_LINK_FLAG + sym_convert now uses its own embind TU. Bumps kicad
(kiface prune, gated). Output byte-identical on the qa corpus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H58sC87w12FotXEmxrJrQV
This commit is contained in:
Gergő Törcsvári 2026-07-09 15:15:33 +02:00
commit f8da88472c
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
4 changed files with 144 additions and 9 deletions

2
kicad

@ -1 +1 @@
Subproject commit ca8877324ce78d1bc9cb32bf634f091e0e76fb29
Subproject commit 2759de60c2acc537781aed6edb9b286f4a755201

View file

@ -87,18 +87,27 @@ case "$APP_NAME" in
;;
esac
# Which app's embind bindings to compile + link. Most apps use their own; the
# sym_convert CLI links the eeschema kiface objects, which reference eeschema's
# embind symbols (kicadCollabOnSave et al.) — reuse eeschema's embind object.
# Which app's embind bindings to compile + link. Most apps use their own.
# NOTE sym_convert deliberately does NOT reuse eeschema's embind object even
# though it links the eeschema kiface: the kiface references exactly one embind
# symbol (kicadCollabOnSave), and linking the real bindings TU for it roots the
# whole editor surface from .init_array (~4x binary size — ysync 0009 size
# research). Its own wasm/bindings/sym_convert_embind.cpp provides a no-op hook.
case "$APP_NAME" in
sym_convert) EMBIND_APP="eeschema" ;;
# occ_service links the pcbnew kiface objects → pcbnew's embind object, same
# reason (its own embind entry points live in occ_service_main.cpp, compiled
# inside the CMake target).
# occ_service links the pcbnew kiface objects → pcbnew's embind object (its
# own embind entry points live in occ_service_main.cpp, compiled inside the
# CMake target).
occ_service) EMBIND_APP="pcbnew" ;;
*) EMBIND_APP="$APP_NAME" ;;
esac
# Embind linker support (--bind). sym_convert has no bindings at all (see
# above) — dropping --bind keeps the embind JS/native runtime out entirely.
case "$APP_NAME" in
sym_convert) EMBIND_LINK_FLAG="" ;;
*) EMBIND_LINK_FLAG="--bind" ;;
esac
# Which app's WASM stub libraries (scripting/frame placeholders) to link.
# sym_convert links the eeschema kiface objects, so it needs eeschema's frame
# stub (eeschema_frame_stub.cpp). kicad_editor links pcbnew's kiface objects, which
@ -515,7 +524,7 @@ emcmake cmake "${KICAD_DIR}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -Xclang -fno-pch-timestamp -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1${DIAG_DEFINES} -I${SYSROOT}/include -I${STUBS_DIR} -include ${STUBS_DIR}/char_traits_uint16_workaround.h" \
-DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sMALLOC=mimalloc -sPTHREAD_POOL_SIZE='${PTHREAD_POOL_EXPR}' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 ${GL3D_LINK_FLAGS} ${NANOSLEEP_YIELD_LINK} ${MALLINFO_STUB_LINK} -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a${APP_STUB_LINK} ${STUBS_BUILD}/libnng_stub.a ${EMBIND_OBJ}" \
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sMALLOC=mimalloc -sPTHREAD_POOL_SIZE='${PTHREAD_POOL_EXPR}' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 ${GL3D_LINK_FLAGS} ${NANOSLEEP_YIELD_LINK} ${MALLINFO_STUB_LINK} -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] ${EMBIND_LINK_FLAG} -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a${APP_STUB_LINK} ${STUBS_BUILD}/libnng_stub.a ${EMBIND_OBJ}" \
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
\

View file

@ -0,0 +1,11 @@
// sym_convert's "embind" object — deliberately embind-free (ysync 0009 size
// research, step (a)).
//
// The eeschema kiface references exactly ONE symbol from the editor's embind
// TU: kicadCollabOnSave (files-io.cpp, called from SCH_EDIT_FRAME::SaveEEFile —
// a path the headless converter never executes). Linking the real
// eeschema_embind.o for that one symbol roots the ENTIRE editor surface from
// .init_array (EMSCRIPTEN_BINDINGS takes the address of every bound function:
// kicadOpenFile → frames/tools/dialogs, collab bridge, presence → GAL), which
// GC cannot remove. This no-op hook satisfies the reference instead.
extern "C" void kicadCollabOnSave( const char* /* aPath */ ) {}

View file

@ -0,0 +1,115 @@
// sym_convert link-time diet stubs (ysync 0009 size research, steps (b)+(c)).
//
// This TU is listed in add_executable(sym_convert …) BEFORE the kiface object
// library and archives, and the target links with --allow-multiple-definition:
// wasm-ld resolves duplicate strong symbols to the FIRST definition, so these
// override the real out-of-line definitions without touching KiCad sources.
// The losing definitions become unreferenced and post-link GC drops them plus
// everything only they reached.
//
// Safety (verified in 0009-kicad-lint-size-research.md §3): ConvertLibrary uses
// only the symbol-library entry points (EnumerateSymbolLib/SaveLibrary/
// SaveSymbol); parse never resolves fonts (SetUnresolvedFontName) and the
// writer prints only already-set font names. Every stub below either throws or
// aborts LOUDLY — a violated assumption fails the conversion, never corrupts it.
#include <cstdio>
#include <cstdlib>
#include <ki_exception.h>
#include <font/font.h>
#include <sch_field.h>
#include <sch_io/kicad_legacy/sch_io_kicad_legacy.h>
#include <sch_io/kicad_sexpr/sch_io_kicad_sexpr.h>
#include <sch_pin.h>
#include <sch_text.h>
#include <sch_textbox.h>
// ── (b) schematic-file entry points ──────────────────────────────────────────
// Severs the schematic half of both instantiated plugins: ParseSchematic,
// SCHEMATIC/SCH_SCREEN/sheets, SCH_SYMBOL/SCH_LINE/labels/groups vtables (and
// their Plot/font/GUI edges), Fontconfig()->ListFonts and Pgm().
SCH_SHEET* SCH_IO_KICAD_SEXPR::LoadSchematicFile( const wxString& aFileName, SCHEMATIC*,
SCH_SHEET*,
const std::map<std::string, UTF8>* )
{
THROW_IO_ERROR( wxString::Format(
wxS( "sym_convert: schematic loading is compiled out (stub); cannot load '%s'" ),
aFileName ) );
}
void SCH_IO_KICAD_SEXPR::SaveSchematicFile( const wxString& aFileName, SCH_SHEET*, SCHEMATIC*,
const std::map<std::string, UTF8>* )
{
THROW_IO_ERROR( wxString::Format(
wxS( "sym_convert: schematic saving is compiled out (stub); cannot save '%s'" ),
aFileName ) );
}
SCH_SHEET* SCH_IO_KICAD_LEGACY::LoadSchematicFile( const wxString& aFileName, SCHEMATIC*,
SCH_SHEET*,
const std::map<std::string, UTF8>* )
{
THROW_IO_ERROR( wxString::Format(
wxS( "sym_convert: schematic loading is compiled out (stub); cannot load '%s'" ),
aFileName ) );
}
void SCH_IO_KICAD_LEGACY::SaveSchematicFile( const wxString& aFileName, SCH_SHEET*, SCHEMATIC*,
const std::map<std::string, UTF8>* )
{
THROW_IO_ERROR( wxString::Format(
wxS( "sym_convert: schematic saving is compiled out (stub); cannot save '%s'" ),
aFileName ) );
}
// ── (e) UI virtuals whose real bodies reference pruned typeinfo/data ─────────
// The kiface prune (cut e) leaves these vtable-pinned bodies referencing
// typeinfo for SCH_NAVIGATE_TOOL / SCH_EDIT_FRAME and SCH_NAVIGATE_TOOL::
// g_BackLink — DATA symbols, which (unlike functions) cannot become JS imports
// under -sERROR_ON_UNDEFINED_SYMBOLS=0. Overriding the whole virtual here makes
// the real body a discarded duplicate, so its relocations are never processed
// and the KiCad sources stay untouched. All four are user-interaction paths a
// headless converter cannot reach (hypertext clicks, the message panel).
void SCH_FIELD::DoHypertextAction( EDA_DRAW_FRAME*, const VECTOR2I& ) const
{
}
void SCH_TEXT::DoHypertextAction( EDA_DRAW_FRAME*, const VECTOR2I& ) const
{
}
void SCH_TEXTBOX::DoHypertextAction( EDA_DRAW_FRAME*, const VECTOR2I& ) const
{
}
void SCH_PIN::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector<MSG_PANEL_ITEM>& )
{
}
// ── (c) font-factory choke point ─────────────────────────────────────────────
// The single route into the font engine: severs STROKE_FONT::LoadFont (and the
// 2.7 MB newstroke glyph data), OUTLINE_FONT (freetype + harfbuzz), fontconfig.
// abort() (not a soft null) so any violated assumption surfaces immediately in
// the qa corpus instead of producing subtly wrong output.
namespace KIFONT
{
FONT* FONT::GetFont( const wxString&, bool, bool, const std::vector<wxString>*, bool )
{
std::fprintf( stderr,
"sym_convert: FATAL: KIFONT::FONT::GetFont called — the font engine is "
"compiled out (ysync 0009 diet stub)\n" );
std::abort();
}
} // namespace KIFONT