From 28388990eec01aaec1cc790238d27b6bd95ddc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20T=C3=B6rcsv=C3=A1ri?= Date: Fri, 10 Jul 2026 13:13:51 +0200 Subject: [PATCH] ysync 0009 phase 4: sym_convert --lint mode (kicad_lint folded into the converter) sym_convert --lint [--strict] : full parse for .kicad_sch (restored LoadSchematicFile + minimal headless PGM/SETTINGS_MANAGER runtime, aSetActive=false) and .kicad_sym/.lib; structure-only for other s-expr files; dup-uuid identity lints (walker) on every s-expr input; errors as file:line:offset, exit 0/1/2. Convert mode byte-identical (qa gate). Fixes en route: 64KB stack local overflowed the default wasm stack (mimalloc corruption); pre-js now inherits process.env into getenv (KICAD_CONFIG_HOME / SYM_CONVERT_TRACE); font abort stub retired (SCH_SCREEN::Append bbox resolves draw fonts) -> 18.0MB; six GetMsgPanelInfo typeinfo stubs; BUILD_3D_VIEWER defaults OFF for headless CLIs (gl1 shim needs glm). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0142yAmMGZ3Ejrq3wwVr2Yq2 --- docker/build.sh | 5 +- kicad | 2 +- scripts/kicad/build-kicad-target.sh | 10 +- wasm/cli/sym_convert_main.cpp | 500 +++++++++++++++++++++++++++- wasm/cli/sym_convert_pre.js | 11 + wasm/cli/sym_convert_stubs.cpp | 78 +++-- 6 files changed, 562 insertions(+), 44 deletions(-) diff --git a/docker/build.sh b/docker/build.sh index 518b4d2..af1fc1d 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -237,8 +237,11 @@ compile_app() { # -e EMSDK=/emsdk: `docker compose exec` bypasses the entrypoint that sources # emsdk_env.sh, so the build shell would lack emcc/embuilder on PATH. Setting # EMSDK lets scripts/common/env.sh source /emsdk/emsdk_env.sh and activate the toolchain. + # BUILD_3D_VIEWER passes through EMPTY when the host didn't set it, so + # build-kicad-target.sh can apply per-app defaults (ON for editors, OFF + # for headless CLIs like sym_convert — the gl1 shim needs glm). docker compose -f docker/docker-compose.yml exec -e EMSDK=/emsdk \ - -e BUILD_3D_VIEWER="${BUILD_3D_VIEWER:-ON}" \ + -e BUILD_3D_VIEWER="${BUILD_3D_VIEWER:-}" \ kicad-wasm-builder \ "/workspace/scripts/kicad/build-${app}.sh" "${ARGS[@]}" diff --git a/kicad b/kicad index 8364527..ca19e5d 160000 --- a/kicad +++ b/kicad @@ -1 +1 @@ -Subproject commit 83645275ac0da6d86c8526dfaa68c9e15d8d62a5 +Subproject commit ca19e5d6c85688937e08bcd4989b8c93e07be382 diff --git a/scripts/kicad/build-kicad-target.sh b/scripts/kicad/build-kicad-target.sh index 9c1c35e..807e480 100755 --- a/scripts/kicad/build-kicad-target.sh +++ b/scripts/kicad/build-kicad-target.sh @@ -67,11 +67,15 @@ case "$APP_NAME" in KICAD_SUBDIR="pcb_calculator" ;; sym_convert) - # Standalone .lib -> .kicad_sym converter (node CLI). Its add_executable - # lives in eeschema/CMakeLists.txt (gated by KICAD_SYM_CONVERTER_WASM), so - # artifacts land in the eeschema/ subdir of its own kicad-sym_convert tree. + # Standalone .lib -> .kicad_sym converter + --lint CLI (node). Its + # add_executable lives in eeschema/CMakeLists.txt (gated by + # KICAD_SYM_CONVERTER_WASM), so artifacts land in the eeschema/ subdir + # of its own kicad-sym_convert tree. A headless CLI has no 3D viewer: + # skip the wasm/gl1 FFP shim compile (it needs glm, which the libs + # sysroot doesn't guarantee) unless the caller forces it. KICAD_TARGET="sym_convert" KICAD_SUBDIR="eeschema" + BUILD_3D_VIEWER="${BUILD_3D_VIEWER:-OFF}" ;; occ_service) # Standalone OpenCASCADE 3D service (worker embind module). Target lives diff --git a/wasm/cli/sym_convert_main.cpp b/wasm/cli/sym_convert_main.cpp index 82b872b..959cc92 100644 --- a/wasm/cli/sym_convert_main.cpp +++ b/wasm/cli/sym_convert_main.cpp @@ -1,11 +1,29 @@ /* - * sym_convert — standalone CLI for legacy (.lib) -> S-expression (.kicad_sym) - * symbol-library conversion, built as a WebAssembly module. + * sym_convert — standalone KiCad file CLI, built as a WebAssembly module. * - * It drives SCH_IO_MGR::ConvertLibrary directly from main(): no GUI, no - * renderer, no embind bindings, no JS host logic. Wired into + * Two modes, one binary (ysync 0009 §7 — the lint tier rides the dieted + * converter instead of shipping a second wasm): + * + * convert: sym_convert + * legacy (.lib) -> S-expression (.kicad_sym) symbol-library + * conversion via SCH_IO_MGR::ConvertLibrary. Unchanged behavior. + * + * lint: sym_convert --lint [--strict] [...] + * "OK" = KiCad will load it (not necessarily load it UNCHANGED — + * KiCad normalizes while parsing). Per extension: + * .kicad_sch full parse (SCH_IO_KICAD_SEXPR) + * .kicad_sym / .lib full library parse (EnumerateSymbolLib) + * other s-expr files structure-only (parens/strings/atoms) + * Every s-expr input additionally gets the uuid lints: duplicate + * (uuid) fields inside one node (KiCad keeps the last) and one + * uuid value on multiple nodes (eeschema silently Increment()s the + * second, pcbnew keeps both) — identity hazards for uuid-keyed + * sync. Warnings don't fail the run unless --strict. + * Exit: 0 ok, 1 any file failed, 2 usage. + * + * No GUI, no renderer, no embind bindings, no JS host logic. Wired into * eeschema/CMakeLists.txt behind the KICAD_SYM_CONVERTER_WASM option - * (see scripts/kicad/build-sym-convert-wasm.sh). + * (see scripts/kicad/build-sym_convert.sh). * * Targets: * - Node: built with -sENVIRONMENT=node -sNODERAWFS=1 (the default here); @@ -18,23 +36,444 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include + +#include +#include #include +#include +#include +#include + +// ── minimal KiCad runtime for the schematic-load path ───────────────────────── +// LoadSchematicFile needs a SCHEMATIC with a PROJECT (settings manager), and +// ParseSchematic's tail calls Pgm().GetLanguageTag() (feeding the wasm +// no-fontconfig ListFonts, which is a cheap string walk). A minimal concrete +// PGM_BASE + headless SETTINGS_MANAGER — the qa fixtures' recipe +// (qa/schematic_utils/eeschema_test_utils.cpp) — is all that takes. + +namespace +{ + +/** SYM_CONVERT_TRACE=1: stage prints for diagnosing hangs/traps in the field. */ +void trace( const char* aMsg ) +{ + if( std::getenv( "SYM_CONVERT_TRACE" ) ) + std::fprintf( stderr, "[trace] %s\n", aMsg ); +} + + +class LINT_PGM : public PGM_BASE +{ +public: + void MacOpenFile( const wxString& ) override {} + + void CreateSettingsManager() + { + m_settings_manager = std::make_unique(); + } +}; + + +SETTINGS_MANAGER& kiRuntime() +{ + static SETTINGS_MANAGER* s_manager = nullptr; + + if( !s_manager ) + { + // JSON settings need a writable config dir; keep it away from any real + // user config (0 = don't overwrite an explicit override). + setenv( "KICAD_CONFIG_HOME", "/tmp/sym_convert-config", 0 ); + + // Deliberately leaked: ~PGM_BASE runs Destroy() (curl/sentry cleanup) + // from the EXIT_RUNTIME static-dtor pass, which the diet stubs out. + trace( "kiRuntime: constructing LINT_PGM" ); + LINT_PGM* pgm = new LINT_PGM(); + SetPgm( pgm ); + trace( "kiRuntime: constructing SETTINGS_MANAGER" ); + pgm->CreateSettingsManager(); + s_manager = &pgm->GetSettingsManager(); + trace( "kiRuntime: ready" ); + } + + return *s_manager; +} + +// ── structure-only s-expr walk + uuid lints ─────────────────────────────────── +// Self-contained (no KiCad parser): balanced parens, terminated strings, and +// the two uuid pathologies. Runs on every s-expr input, INCLUDING fully parsed +// ones — the full parsers *normalize* these instead of reporting them. + +struct LINT_REPORT +{ + std::vector errors; + std::vector warnings; +}; + + +struct SEXPR_NODE +{ + std::string head; // first atom after '(' + std::string firstArg; // second atom (a (uuid "X") node's value) + int line = 0; + int directUuidFields = 0; + bool sawHead = false; +}; + + +void walkSexpr( const std::string& aText, const char* aPath, LINT_REPORT& aOut ) +{ + std::vector stack; + std::map> uuidLines; // value -> lines seen on a node + size_t i = 0; + int line = 1; + int topLevelForms = 0; + + auto err = [&]( int aLine, const std::string& aMsg ) + { + char buf[512]; + std::snprintf( buf, sizeof( buf ), "%s:%d: error: %s", aPath, aLine, aMsg.c_str() ); + aOut.errors.push_back( buf ); + }; + auto warn = [&]( int aLine, const std::string& aMsg ) + { + char buf[512]; + std::snprintf( buf, sizeof( buf ), "%s:%d: warning: %s", aPath, aLine, aMsg.c_str() ); + aOut.warnings.push_back( buf ); + }; + + auto atomInto = [&]( SEXPR_NODE* aNode, const std::string& aAtom ) + { + if( !aNode ) + return; + + if( !aNode->sawHead ) + { + aNode->head = aAtom; + aNode->sawHead = true; + } + else if( aNode->firstArg.empty() ) + { + aNode->firstArg = aAtom; + } + }; + + while( i < aText.size() ) + { + const char c = aText[i]; + + if( c == '\n' ) + { + line++; + i++; + } + else if( c == ' ' || c == '\t' || c == '\r' ) + { + i++; + } + else if( c == '(' ) + { + if( stack.empty() ) + topLevelForms++; + + SEXPR_NODE node; + node.line = line; + stack.push_back( node ); + i++; + } + else if( c == ')' ) + { + if( stack.empty() ) + { + err( line, "unbalanced ')'" ); + return; + } + + SEXPR_NODE closed = stack.back(); + stack.pop_back(); + SEXPR_NODE* parent = stack.empty() ? nullptr : &stack.back(); + + if( closed.head == "uuid" && parent ) + { + parent->directUuidFields++; + + if( parent->directUuidFields == 2 ) + { + warn( closed.line, + "multiple (uuid) fields directly in one (" + parent->head + + ") node — KiCad keeps only the last" ); + } + + if( !closed.firstArg.empty() ) + { + std::string value = closed.firstArg; + + if( value.size() >= 2 && value.front() == '"' && value.back() == '"' ) + value = value.substr( 1, value.size() - 2 ); + + uuidLines[value].push_back( closed.line ); + } + } + + i++; + } + else if( c == '"' ) + { + const int start = line; + std::string atom; + atom += c; + i++; + + while( i < aText.size() && aText[i] != '"' ) + { + if( aText[i] == '\\' && i + 1 < aText.size() ) + { + atom += aText[i]; + i++; + } + + if( aText[i] == '\n' ) + line++; + + atom += aText[i]; + i++; + } + + if( i >= aText.size() ) + { + err( start, "unterminated string" ); + return; + } + + atom += '"'; + i++; + atomInto( stack.empty() ? nullptr : &stack.back(), atom ); + } + else + { + std::string atom; + + while( i < aText.size() && !std::strchr( " \t\r\n()\"", aText[i] ) ) + { + atom += aText[i]; + i++; + } + + if( stack.empty() ) + { + err( line, "atom outside any (…) form: '" + atom + "'" ); + return; + } + + atomInto( &stack.back(), atom ); + } + } + + if( !stack.empty() ) + err( stack.back().line, "unbalanced '(' — " + std::to_string( stack.size() ) + + " form(s) never closed" ); + + if( topLevelForms == 0 && aOut.errors.empty() ) + err( 1, "no s-expression form found" ); + + for( const auto& [value, lines] : uuidLines ) + { + if( lines.size() < 2 ) + continue; + + std::string msg = "uuid \"" + value + "\" appears on " + std::to_string( lines.size() ) + + " nodes (also line"; + msg += lines.size() > 2 ? "s" : ""; + + for( size_t n = 1; n < lines.size(); n++ ) + msg += ( n > 1 ? ", " : " " ) + std::to_string( lines[n] ); + + msg += ") — eeschema silently regenerates the duplicate, pcbnew keeps both"; + warn( lines[0], msg ); + } +} + +// ── full-fidelity lints (the linked eeschema parsers) ───────────────────────── + +void lintSchematicFile( const wxString& aAbsPath, LINT_REPORT& aOut ) +{ + SETTINGS_MANAGER& manager = kiRuntime(); + trace( "lintSchematicFile: LoadProject" ); + // aSetActive=false: the set-active tail calls Pgm().GetLibraryManager() + // (never constructed on the minimal LINT_PGM — a null vtable call) and the + // kiway/env plumbing; none of it exists headless. Prj() still resolves to + // the first loaded project. + manager.LoadProject( wxEmptyString, false ); + manager.Prj().SetElem( PROJECT::ELEM::LEGACY_SYMBOL_LIBS, nullptr ); + + trace( "lintSchematicFile: constructing SCHEMATIC" ); + SCHEMATIC schematic( &manager.Prj() ); + trace( "lintSchematicFile: Reset" ); + schematic.Reset(); + SCH_SHEET* defaultSheet = schematic.GetTopLevelSheet( 0 ); + + trace( "lintSchematicFile: LoadSchematicFile" ); + IO_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) ); + SCH_SHEET* root = pi->LoadSchematicFile( aAbsPath, &schematic ); + trace( "lintSchematicFile: loaded" ); + schematic.AddTopLevelSheet( root ); // the SCHEMATIC dtor owns the hierarchy + schematic.RemoveTopLevelSheet( defaultSheet ); + delete defaultSheet; + + // Sub-sheet problems (e.g. a missing child .kicad_sch when linting one + // materialized sheet) are queued by loadHierarchy, not thrown — only the + // root file's own parse failure throws. Surface them as warnings. + if( !pi->GetError().IsEmpty() ) + { + for( const wxString& msgLine : wxSplit( pi->GetError(), '\n' ) ) + { + if( !msgLine.IsEmpty() ) + aOut.warnings.push_back( std::string( aAbsPath.ToUTF8() ) + ": warning: sub-sheet: " + + std::string( msgLine.ToUTF8() ) ); + } + } +} + + +int lintSymbolLib( const wxString& aAbsPath ) +{ + const SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::GuessPluginTypeFromLibPath( aAbsPath ); + + if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN ) + THROW_IO_ERROR( wxS( "unrecognized symbol library format" ) ); + + IO_RELEASER pi( SCH_IO_MGR::FindPlugin( type ) ); + + // The vector overload is the one ConvertLibrary exercises (the wxArrayString + // flavor crashed a dieted build once — an indirectly-reachable-only helper). + // The symbols stay owned by the plugin's cache; do not delete them. + std::vector symbols; + pi->EnumerateSymbolLib( symbols, aAbsPath ); + return (int) symbols.size(); +} + +// ── lint driver ─────────────────────────────────────────────────────────────── + +bool lintOneFile( const char* aPath, bool aStrict ) +{ + wxFileName fn( wxString::FromUTF8( aPath ) ); + fn.MakeAbsolute(); // LoadSchematicFile asserts an absolute path + const wxString absPath = fn.GetFullPath(); + const wxString ext = fn.GetExt().Lower(); + + LINT_REPORT report; + const char* tier = "structure only"; + int symbolCount = -1; + + // The structural walk + uuid lints run on every s-expr format; the legacy + // .lib format is not an s-expr, so it gets the full parse only. + if( ext != wxS( "lib" ) ) + { + std::string text; + + if( FILE* f = std::fopen( aPath, "rb" ) ) + { + // Heap, not stack: a 64 KB stack buffer equals the default wasm + // stack SIZE — it silently overflowed into the heap and corrupted + // mimalloc's structures (trap deep inside _mi_malloc_generic). + std::vector buf( 65536 ); + size_t got; + + while( ( got = std::fread( buf.data(), 1, buf.size(), f ) ) > 0 ) + text.append( buf.data(), got ); + + std::fclose( f ); + walkSexpr( text, aPath, report ); + } + else + { + report.errors.push_back( std::string( aPath ) + ": error: cannot open file" ); + } + } + + if( report.errors.empty() ) + { + trace( "lintOneFile: structural walk done, dispatching full parse" ); + try + { + if( ext == wxS( "kicad_sch" ) ) + { + lintSchematicFile( absPath, report ); + tier = "full parse"; + } + else if( ext == wxS( "kicad_sym" ) || ext == wxS( "lib" ) ) + { + symbolCount = lintSymbolLib( absPath ); + tier = "full parse"; + } + // Anything else (kicad_pcb, kicad_wks, kicad_pro …) stays + // structure-only: this binary links the eeschema parsers, not + // pcbnew's. Native kicad-cli covers boards in container contexts. + } + catch( PARSE_ERROR& pe ) // non-const: ParseProblem() is unqualified + { + char buf[1024]; + std::snprintf( buf, sizeof( buf ), "%s:%d:%d: error: %s", aPath, pe.lineNumber, + pe.byteIndex, (const char*) pe.ParseProblem().ToUTF8() ); + report.errors.push_back( buf ); + } + catch( const IO_ERROR& ioe ) + { + report.errors.push_back( std::string( aPath ) + ": error: " + + std::string( ioe.Problem().ToUTF8() ) ); + } + catch( const std::exception& e ) + { + report.errors.push_back( std::string( aPath ) + ": error: " + e.what() ); + } + } + + for( const std::string& msg : report.errors ) + std::fprintf( stderr, "%s\n", msg.c_str() ); + + for( const std::string& msg : report.warnings ) + std::fprintf( stderr, "%s\n", msg.c_str() ); + + const bool failed = !report.errors.empty() || ( aStrict && !report.warnings.empty() ); + + if( failed ) + std::fprintf( stderr, "%s: FAIL\n", aPath ); + else if( symbolCount >= 0 ) + std::fprintf( stderr, "%s: OK (%s, %d symbols)\n", aPath, tier, symbolCount ); + else + std::fprintf( stderr, "%s: OK (%s)\n", aPath, tier ); + + return !failed; +} + +} // namespace + int main( int argc, char** argv ) { - if( argc < 3 ) - { - std::fprintf( stderr, "usage: sym_convert \n" ); - return 2; - } + // Non-tty stderr is fully buffered under emscripten/musl, so a trap or + // hang eats every diagnostic printed before it. A CLI's stderr must be + // unbuffered — losing the error report is worse than the syscall cost. + setvbuf( stderr, nullptr, _IONBF, 0 ); // Bring up wxBase (no GUI): wxString / wxFileName / wxFFile rely on the - // library being initialised. If the conversion path turns out to need more - // global state (PGM_BASE, settings), the run will surface it here. + // library being initialised. wxInitializer initializer( argc, argv ); if( !initializer.IsOk() ) @@ -43,6 +482,43 @@ int main( int argc, char** argv ) return 3; } + if( argc >= 2 && std::strcmp( argv[1], "--lint" ) == 0 ) + { + // The minimal headless runtime never registers app settings; + // GetAppSettings fails SOFT to defaults but wxFAIL_MSGs on every call, + // flooding stderr. Lint output must stay parseable — drop the asserts. + wxDisableAsserts(); + + int firstFile = 2; + bool strict = false; + + if( argc > firstFile && std::strcmp( argv[firstFile], "--strict" ) == 0 ) + { + strict = true; + firstFile++; + } + + if( argc <= firstFile ) + { + std::fprintf( stderr, "usage: sym_convert --lint [--strict] [...]\n" ); + return 2; + } + + bool allOk = true; + + for( int n = firstFile; n < argc; n++ ) + allOk = lintOneFile( argv[n], strict ) && allOk; + + return allOk ? 0 : 1; + } + + if( argc < 3 ) + { + std::fprintf( stderr, "usage: sym_convert \n" + " sym_convert --lint [--strict] [...]\n" ); + return 2; + } + const wxString inPath = wxString::FromUTF8( argv[1] ); const wxString outPath = wxString::FromUTF8( argv[2] ); diff --git a/wasm/cli/sym_convert_pre.js b/wasm/cli/sym_convert_pre.js index 236649d..fea0091 100644 --- a/wasm/cli/sym_convert_pre.js +++ b/wasm/cli/sym_convert_pre.js @@ -7,6 +7,17 @@ // with an in-memory store: every read returns "absent" → KiCad falls back to // defaults; writes live only for the process lifetime. Semantics mirror // wxwidgets/build/wasm/wx.js exactly (sans persistence). +// Emscripten does NOT inherit the host environment into the wasm getenv() +// table (node included) — without this, KICAD_CONFIG_HOME / SYM_CONVERT_TRACE +// set by the caller silently never arrive. Copy process.env in before main. +Module['preRun'] = Module['preRun'] || []; +Module['preRun'].push( function() { + if( typeof process !== 'undefined' && process.env && typeof ENV !== 'undefined' ) + { + for( var k in process.env ) ENV[k] = process.env[k]; + } +} ); + (function( g ) { if( !g.localStorage ) { diff --git a/wasm/cli/sym_convert_stubs.cpp b/wasm/cli/sym_convert_stubs.cpp index ab36272..eec2936 100644 --- a/wasm/cli/sym_convert_stubs.cpp +++ b/wasm/cli/sym_convert_stubs.cpp @@ -18,27 +18,26 @@ #include #include +#include #include #include #include +#include +#include +#include #include +#include +#include #include #include // ── (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* ) -{ - THROW_IO_ERROR( wxString::Format( - wxS( "sym_convert: schematic loading is compiled out (stub); cannot load '%s'" ), - aFileName ) ); -} - +// Originally BOTH plugins' load+save were stubbed, severing the whole +// schematic half. The --lint mode (ysync 0009 §7) needs the REAL s-expr +// LoadSchematicFile back, so only three of the four stay stubbed: the s-expr +// SAVE (lint/convert never write schematics) and the LEGACY load+save (.sch +// lint unsupported). The re-rooted schematic object model costs binary size — +// the price of the lint tier riding this binary instead of a second wasm. void SCH_IO_KICAD_SEXPR::SaveSchematicFile( const wxString& aFileName, SCH_SHEET*, SCHEMATIC*, const std::map* ) @@ -95,21 +94,46 @@ void SCH_PIN::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) { } -// ── (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. +// The --lint mode re-roots the schematic object model (real LoadSchematicFile); +// these model TUs' GetMsgPanelInfo overrides dynamic_cast to SCH_EDIT_FRAME, +// whose typeinfo the kiface prune removed. Same treatment as SCH_PIN above — +// message-panel population is unreachable without a frame. -namespace KIFONT +void SCH_BUS_ENTRY_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) { - -FONT* FONT::GetFont( const wxString&, bool, bool, const std::vector*, 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 + +void SCH_JUNCTION::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) +{ +} + + +void SCH_LABEL_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) +{ +} + + +void SCH_LINE::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) +{ +} + + +void SCH_SHEET::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) +{ +} + + +void SCH_SYMBOL::GetMsgPanelInfo( EDA_DRAW_FRAME*, std::vector& ) +{ +} + +// ── (c) font-factory choke point — RETIRED by the --lint mode ───────────────── +// The abort stub on KIFONT::FONT::GetFont severed STROKE_FONT::LoadFont (and +// the 2.7 MB newstroke glyph data), OUTLINE_FONT (freetype + harfbuzz) and +// fontconfig while this binary only converted symbol libraries (whose parse +// never resolves fonts). Schematic lint changed that: SCH_SCREEN::Append +// RTree-inserts every parsed item by bounding box, and any text bbox resolves +// the draw font — so the REAL font engine is linked again (wasm fontconfig is +// the no-op variant; unknown names fall back to the stroke font). This is the +// bulk of the lint tier's size cost over the pure converter.