kicad-validity A1: kicad_tools --resave — full-parse rewrite in current format
New --resave <file> <outdir> subcommand (root docs/features/kicad-validity/ 0001): .kicad_sch one file per sheet mirroring the hierarchy layout, .kicad_pcb via pcbToolsResaveBoard callback (mirror of the lint callback), .kicad_sym/.lib via ConvertLibrary. Exit 0 ok / 2 usage / 4 input invalid / 5 write failed — only 4 marks the input invalid for the upload gate. The s-expr schematic save stub is now #ifndef KICAD_TOOLS_COMBINED: the merged image links the real writer (+65 KB), standalone sym_convert keeps its diet. Board save needed no un-stubbing (pcb_io/ survives the CMake prune). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HLua64PCVwkQ1hpWdaf1Gm
This commit is contained in:
parent
985408ce9e
commit
c51ccf82ba
4 changed files with 237 additions and 3 deletions
|
|
@ -17,6 +17,12 @@
|
|||
* (.kicad_pcb files get a FULL parse here — the pcbnew
|
||||
* parser is linked; the lint driver calls back into
|
||||
* pcbToolsLintBoard on the pcb side)
|
||||
* kicad_tools --resave <file> <outdir>
|
||||
* (full parse + rewrite in the current file-format
|
||||
* version — kicad-validity 0001; .kicad_pcb via the
|
||||
* pcbToolsResaveBoard callback, .kicad_sch one file per
|
||||
* sheet, .kicad_sym/.lib via ConvertLibrary. Exit 4 =
|
||||
* input invalid, 5 = write failed)
|
||||
* kicad_tools --erc [--json] [--strict] <file.kicad_sch> [<out>]
|
||||
* kicad_tools --netlist [--xml] <file.kicad_sch> [<out>]
|
||||
* kicad_tools --bom <file.kicad_sch> [<out>]
|
||||
|
|
|
|||
|
|
@ -679,6 +679,68 @@ int pcbToolsLintBoard( const char* aInPath, std::string& aError )
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Full-parse board resave for the merged image's --resave driver (also on the
|
||||
// eeschema side): a bare load like pcbToolsLintBoard, then the s-expr writer
|
||||
// rewrites the board in the current format version. Zones are written
|
||||
// as-saved (no refill) — a format upgrade must not change geometry. Returns
|
||||
// 0 resaved / 4 load or parse failed / 5 write failed, with aError filled.
|
||||
int pcbToolsResaveBoard( const char* aInPath, const char* aOutPath, std::string& aError )
|
||||
{
|
||||
wxFileName fn( wxString::FromUTF8( aInPath ) );
|
||||
fn.MakeAbsolute();
|
||||
|
||||
kiRuntime();
|
||||
|
||||
std::unique_ptr<BOARD> brd;
|
||||
|
||||
try
|
||||
{
|
||||
brd.reset( PCB_IO_MGR::Load( PCB_IO_MGR::KICAD_SEXP, fn.GetFullPath() ) );
|
||||
|
||||
if( !brd )
|
||||
{
|
||||
aError = std::string( aInPath ) + ": error: failed to load board";
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
catch( PARSE_ERROR& pe )
|
||||
{
|
||||
char buf[1024];
|
||||
std::snprintf( buf, sizeof( buf ), "%s:%d:%d: error: %s", aInPath, pe.lineNumber,
|
||||
pe.byteIndex, (const char*) pe.ParseProblem().ToUTF8() );
|
||||
aError = buf;
|
||||
return 4;
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
aError = std::string( aInPath ) + ": error: " + std::string( ioe.Problem().ToUTF8() );
|
||||
return 4;
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
aError = std::string( aInPath ) + ": error: " + e.what();
|
||||
return 4;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PCB_IO_MGR::Save( PCB_IO_MGR::KICAD_SEXP, wxString::FromUTF8( aOutPath ), brd.get() );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
aError = std::string( aOutPath ) + ": error: " + std::string( ioe.Problem().ToUTF8() );
|
||||
return 5;
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
aError = std::string( aOutPath ) + ": error: " + e.what();
|
||||
return 5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // KICAD_TOOLS_COMBINED
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@
|
|||
// Merged image only: full-parse board lint via the pcbnew side
|
||||
// (pcb_convert_main.cpp) — the standalone eeschema tree has no pcbnew parser.
|
||||
int pcbToolsLintBoard( const char* aInPath, std::string& aError );
|
||||
// Merged image only: board load + s-expr rewrite for --resave (kicad-validity
|
||||
// 0001). 0 resaved / 4 load failed / 5 write failed.
|
||||
int pcbToolsResaveBoard( const char* aInPath, const char* aOutPath, std::string& aError );
|
||||
#endif
|
||||
|
||||
#include <connection_graph.h>
|
||||
|
|
@ -700,6 +703,141 @@ wxString defaultOutPath( const wxFileName& aIn, const wxString& aSuffix, const w
|
|||
return out.GetFullPath();
|
||||
}
|
||||
|
||||
// ── headless resave — format upgrade (kicad-validity 0001) ───────────────────
|
||||
// Full parse, then write back in the current file-format version. The write
|
||||
// goes to <outdir> as files (never stdout): the tools-job runner's contract is
|
||||
// "walk the output dir", and a hierarchical schematic produces one file per
|
||||
// sheet, mirroring the sheet files' layout relative to the root file's
|
||||
// directory (sheets outside it flatten to their basename). Zones/geometry are
|
||||
// written as-loaded — a format upgrade must not change content.
|
||||
// Exit: 0 resaved / 2 usage / 4 load or parse failed / 5 write failed. Only 4
|
||||
// means "the input is not valid KiCad" — the upload gate keys off it.
|
||||
|
||||
int resaveSchematic( const char* aInPath, const wxFileName& aInFn, const wxString& aOutDir )
|
||||
{
|
||||
std::unique_ptr<SCHEMATIC> schematic = loadSchematicHeadless( aInPath );
|
||||
|
||||
if( !schematic )
|
||||
return 4;
|
||||
|
||||
IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
|
||||
|
||||
const wxString rootDir = aInFn.GetPath( wxPATH_GET_SEPARATOR );
|
||||
|
||||
SCH_SHEET_LIST sheetList = schematic->BuildSheetListSortedByPageNumbers();
|
||||
std::unordered_set<SCH_SCREEN*> saved;
|
||||
int files = 0;
|
||||
|
||||
for( SCH_SHEET_PATH& path : sheetList )
|
||||
{
|
||||
SCH_SHEET* sheet = path.Last();
|
||||
SCH_SCREEN* screen = sheet ? sheet->GetScreen() : nullptr;
|
||||
|
||||
if( !screen || !saved.insert( screen ).second )
|
||||
continue;
|
||||
|
||||
wxFileName srcFn( screen->GetFileName() );
|
||||
srcFn.MakeAbsolute();
|
||||
|
||||
wxString rel;
|
||||
|
||||
if( srcFn.GetFullPath().StartsWith( rootDir, &rel ) )
|
||||
; // rel = path under the root file's directory
|
||||
else
|
||||
rel = srcFn.GetFullName();
|
||||
|
||||
wxFileName outFn( aOutDir + rel );
|
||||
|
||||
if( !outFn.DirExists() && !outFn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
std::fprintf( stderr, "%s: error: cannot create output directory %s\n", aInPath,
|
||||
(const char*) outFn.GetPath().ToUTF8() );
|
||||
return 5;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
pi->SaveSchematicFile( outFn.GetFullPath(), sheet, schematic.get() );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
std::fprintf( stderr, "%s: error: %s\n",
|
||||
(const char*) outFn.GetFullPath().ToUTF8(),
|
||||
(const char*) ioe.Problem().ToUTF8() );
|
||||
return 5;
|
||||
}
|
||||
|
||||
files++;
|
||||
}
|
||||
|
||||
std::fprintf( stderr, "%s: OK (resave, %d sheet files) -> %s\n", aInPath, files,
|
||||
(const char*) aOutDir.ToUTF8() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int runResave( const char* aInPath, const char* aOutDir )
|
||||
{
|
||||
wxFileName inFn( wxString::FromUTF8( aInPath ) );
|
||||
inFn.MakeAbsolute();
|
||||
const wxString ext = inFn.GetExt().Lower();
|
||||
|
||||
wxFileName outDirFn = wxFileName::DirName( wxString::FromUTF8( aOutDir ) );
|
||||
outDirFn.MakeAbsolute();
|
||||
const wxString outDir = outDirFn.GetPath( wxPATH_GET_SEPARATOR );
|
||||
|
||||
if( !outDirFn.DirExists() && !outDirFn.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
std::fprintf( stderr, "%s: error: cannot create output directory %s\n", aInPath,
|
||||
(const char*) outDir.ToUTF8() );
|
||||
return 5;
|
||||
}
|
||||
|
||||
if( ext == wxS( "kicad_sch" ) )
|
||||
return resaveSchematic( aInPath, inFn, outDir );
|
||||
|
||||
if( ext == wxS( "kicad_sym" ) || ext == wxS( "lib" ) )
|
||||
{
|
||||
// ConvertLibrary reads any recognized library format and writes the
|
||||
// current s-expr format — resave and legacy upgrade in one call.
|
||||
const wxString outPath = outDir + inFn.GetName() + wxS( ".kicad_sym" );
|
||||
|
||||
if( !SCH_IO_MGR::ConvertLibrary( nullptr, inFn.GetFullPath(), outPath ) )
|
||||
{
|
||||
std::fprintf( stderr, "%s: error: failed to convert symbol library\n", aInPath );
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::fprintf( stderr, "%s: OK (resave) -> %s\n", aInPath,
|
||||
(const char*) outPath.ToUTF8() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ext == wxS( "kicad_pcb" ) )
|
||||
{
|
||||
#ifdef KICAD_TOOLS_COMBINED
|
||||
const wxString outPath = outDir + inFn.GetFullName();
|
||||
std::string error;
|
||||
const int rc = pcbToolsResaveBoard( aInPath, (const char*) outPath.ToUTF8(), error );
|
||||
|
||||
if( rc != 0 )
|
||||
std::fprintf( stderr, "%s\n", error.c_str() );
|
||||
else
|
||||
std::fprintf( stderr, "%s: OK (resave) -> %s\n", aInPath,
|
||||
(const char*) outPath.ToUTF8() );
|
||||
|
||||
return rc;
|
||||
#else
|
||||
std::fprintf( stderr, "%s: error: board resave requires the merged kicad_tools build\n",
|
||||
aInPath );
|
||||
return 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::fprintf( stderr, "%s: error: unsupported extension for --resave\n", aInPath );
|
||||
return 2;
|
||||
}
|
||||
|
||||
// ── headless ERC ──────────────────────────────────────────────────────────────
|
||||
|
||||
int runErc( const char* aInPath, bool aJson, bool aStrict, const char* aOutPath )
|
||||
|
|
@ -1082,6 +1220,28 @@ int symConvertMain( int argc, char** argv )
|
|||
return allOk ? 0 : 1;
|
||||
}
|
||||
|
||||
if( argc >= 2 && std::strcmp( argv[1], "--resave" ) == 0 )
|
||||
{
|
||||
// Same rationale as --lint: headless runtime, parseable output.
|
||||
wxDisableAsserts();
|
||||
|
||||
if( argc < 4 )
|
||||
{
|
||||
std::fprintf( stderr, "usage: kicad_tools --resave <file> <outdir>\n" );
|
||||
return 2;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return runResave( argv[2], argv[3] );
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
std::fprintf( stderr, "%s: error: %s\n", argv[2], e.what() );
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
if( argc >= 2 && std::strcmp( argv[1], "--convert-lib" ) == 0 && argc >= 4 )
|
||||
{
|
||||
// The legacy plugin asserts on relative paths and (worse) writes an
|
||||
|
|
@ -1109,6 +1269,7 @@ int symConvertMain( int argc, char** argv )
|
|||
|
||||
std::fprintf( stderr, "usage: kicad_tools --convert-lib <input.lib> <output.kicad_sym>\n"
|
||||
" kicad_tools --lint [--strict] <file> [<file>...]\n"
|
||||
" kicad_tools --resave <file> <outdir>\n"
|
||||
" kicad_tools --erc [--json] [--strict] <file.kicad_sch> [<out>]\n"
|
||||
" kicad_tools --netlist [--xml] <file.kicad_sch> [<out>]\n"
|
||||
" kicad_tools --bom <file.kicad_sch> [<out>]\n"
|
||||
|
|
|
|||
|
|
@ -34,11 +34,15 @@
|
|||
// ── (b) schematic-file entry points ──────────────────────────────────────────
|
||||
// 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 —
|
||||
// LoadSchematicFile back, and the merged kicad_tools image additionally
|
||||
// restores the s-expr SAVE for --resave (kicad-validity 0001 format upgrade)
|
||||
// — its serializer largely rides TUs the lint tier already links. The
|
||||
// standalone sym_convert keeps the save stubbed (lint/convert never write
|
||||
// schematics), and the LEGACY load+save stay stubbed everywhere (.sch
|
||||
// unsupported). The re-rooted schematic object model costs binary size —
|
||||
// the price of the lint tier riding this binary instead of a second wasm.
|
||||
|
||||
#ifndef KICAD_TOOLS_COMBINED
|
||||
void SCH_IO_KICAD_SEXPR::SaveSchematicFile( const wxString& aFileName, SCH_SHEET*, SCHEMATIC*,
|
||||
const std::map<std::string, UTF8>* )
|
||||
{
|
||||
|
|
@ -46,6 +50,7 @@ void SCH_IO_KICAD_SEXPR::SaveSchematicFile( const wxString& aFileName, SCH_SHEET
|
|||
wxS( "sym_convert: schematic saving is compiled out (stub); cannot save '%s'" ),
|
||||
aFileName ) );
|
||||
}
|
||||
#endif // !KICAD_TOOLS_COMBINED
|
||||
|
||||
|
||||
SCH_SHEET* SCH_IO_KICAD_LEGACY::LoadSchematicFile( const wxString& aFileName, SCHEMATIC*,
|
||||
|
|
|
|||
Loading…
Reference in a new issue