From 4ff127b4529d141c5de843dc890d15699ced2483 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sun, 19 Nov 2023 09:02:52 -0500 Subject: [PATCH] Parse generator_version and improve error output on mismatch Also bump symbol format version for generator_version --- common/drawing_sheet/drawing_sheet.keywords | 2 + common/drawing_sheet/drawing_sheet_parser.cpp | 36 ++++-- common/exceptions.cpp | 36 ++++-- common/pcb.keywords | 1 + eeschema/sch_file_versions.h | 6 +- .../kicad/sch_sexpr_lib_plugin_cache.cpp | 2 +- .../sch_plugins/kicad/sch_sexpr_parser.cpp | 119 ++++++++++++++---- eeschema/sch_plugins/kicad/sch_sexpr_parser.h | 1 + eeschema/schematic.keywords | 3 + include/ki_exception.h | 12 +- pcbnew/plugins/kicad/pcb_parser.cpp | 54 +++++++- pcbnew/plugins/kicad/pcb_parser.h | 1 + 12 files changed, 222 insertions(+), 51 deletions(-) diff --git a/common/drawing_sheet/drawing_sheet.keywords b/common/drawing_sheet/drawing_sheet.keywords index 79560adb7b..00a5158187 100644 --- a/common/drawing_sheet/drawing_sheet.keywords +++ b/common/drawing_sheet/drawing_sheet.keywords @@ -51,3 +51,5 @@ repeat incrx incry incrlabel +generator +generator_version diff --git a/common/drawing_sheet/drawing_sheet_parser.cpp b/common/drawing_sheet/drawing_sheet_parser.cpp index cc313a495a..ea4e54ea94 100644 --- a/common/drawing_sheet/drawing_sheet_parser.cpp +++ b/common/drawing_sheet/drawing_sheet_parser.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -53,6 +54,7 @@ public: private: int m_requiredVersion; + wxString m_generatorVersion; /** * Parse the data specified at the very beginning of the file, like version and the @@ -209,6 +211,16 @@ void DRAWING_SHEET_PARSER::Parse( DS_DATA_MODEL* aLayout ) parseHeader( token ); aLayout->SetFileFormatVersionAtLoad( m_requiredVersion ); + auto checkVersion = + [&]() + { + if( m_requiredVersion > SEXPR_WORKSHEET_FILE_VERSION ) + { + throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ), + m_generatorVersion ); + } + }; + for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() ) { if( token == T_LEFT ) @@ -216,7 +228,22 @@ void DRAWING_SHEET_PARSER::Parse( DS_DATA_MODEL* aLayout ) switch( token ) { + case T_generator: + // (generator "genname"); we don't care about it at the moment. + NeedSYMBOL(); + NeedRIGHT(); + break; + + case T_generator_version: + NextTok(); + m_generatorVersion = FromUTF8(); + NeedRIGHT(); + break; + case T_setup: // Defines default values for graphic items + // Check the version here, because the generator and generator_version (if available) + // will have been parsed by now given the order the formatter writes them in + checkVersion(); parseSetup( aLayout ); break; @@ -285,21 +312,12 @@ void DRAWING_SHEET_PARSER::parseHeader( T aHeaderType ) { m_requiredVersion = parseInt(); - if( m_requiredVersion > SEXPR_WORKSHEET_FILE_VERSION ) - throw FUTURE_FORMAT_ERROR( FromUTF8() ); - NeedRIGHT(); } else { Expecting( T_version ); } - - // Ignore generator info. - NeedLEFT(); - NeedSYMBOL(); - NeedSYMBOL(); - NeedRIGHT(); } else { diff --git a/common/exceptions.cpp b/common/exceptions.cpp index 6446fc2361..c75a1fd623 100644 --- a/common/exceptions.cpp +++ b/common/exceptions.cpp @@ -94,22 +94,36 @@ void PARSE_ERROR::init( const wxString& aProblem, const char* aThrowersFile, } -void FUTURE_FORMAT_ERROR::init( const wxString& aRequiredVersion ) +void FUTURE_FORMAT_ERROR::init( const wxString& aRequiredVersion, + const wxString& aRequiredGenerator ) { requiredVersion = aRequiredVersion; + requiredGenerator = aRequiredGenerator; - problem.Printf( _( "KiCad was unable to open this file because it was created with a more " - "recent version than the one you are running.\n\n" - "To open it you will need to upgrade KiCad to a version dated %s or " - "later." ), - aRequiredVersion ); + if( requiredGenerator.IsEmpty() ) + { + problem.Printf( _( "KiCad was unable to open this file because it was created with a more " + "recent version than the one you are running.\n\n" + "To open it you will need to upgrade KiCad to a version dated %s or " + "later." ), + aRequiredVersion ); + } + else + { + problem.Printf( _( "KiCad was unable to open this file because it was created with a more " + "recent version than the one you are running.\n\n" + "To open it you will need to upgrade KiCad to version %s or " + "later (file format dated %s or later)." ), + aRequiredGenerator, aRequiredVersion ); + } } -FUTURE_FORMAT_ERROR::FUTURE_FORMAT_ERROR( const wxString& aRequiredVersion ) : +FUTURE_FORMAT_ERROR::FUTURE_FORMAT_ERROR( const wxString& aRequiredVersion, + const wxString& aRequiredGenerator ) : PARSE_ERROR() { - init( aRequiredVersion ); + init( aRequiredVersion, aRequiredGenerator ); lineNumber = 0; byteIndex = 0; @@ -117,17 +131,19 @@ FUTURE_FORMAT_ERROR::FUTURE_FORMAT_ERROR( const wxString& aRequiredVersion ) : FUTURE_FORMAT_ERROR::FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError, - const wxString& aRequiredVersion ) : + const wxString& aRequiredVersion, + const wxString& aRequiredGenerator ) : PARSE_ERROR() { if( const FUTURE_FORMAT_ERROR* ffe = dynamic_cast( &aParseError ) ) { requiredVersion = ffe->requiredVersion; + requiredGenerator = ffe->requiredGenerator; problem = ffe->Problem(); } else { - init( aRequiredVersion ); + init( aRequiredVersion, aRequiredGenerator ); if( !aParseError.Problem().IsEmpty() ) problem += wxS( "\n\n" ) + _( "Full error text:" ) + wxS( "\n" ) + aParseError.Problem(); diff --git a/common/pcb.keywords b/common/pcb.keywords index 7697157f33..810a9f3391 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -136,6 +136,7 @@ free full general generator +generator_version generated grid_origin group diff --git a/eeschema/sch_file_versions.h b/eeschema/sch_file_versions.h index cb1cf09e4a..f0bf9e6391 100644 --- a/eeschema/sch_file_versions.h +++ b/eeschema/sch_file_versions.h @@ -48,7 +48,8 @@ //#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220331 // Text colors. //#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220914 // Symbol unit display names. //#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220914 // Don't save property ID -#define SEXPR_SYMBOL_LIB_FILE_VERSION 20230620 // ki_description -> Description Field +//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20230620 // ki_description -> Description Field +#define SEXPR_SYMBOL_LIB_FILE_VERSION 20231120 // generator_version; V8 cleanups /** * Schematic file version. @@ -100,4 +101,5 @@ //#define SEXPR_SCHEMATIC_FILE_VERSION 20230409 // Add exclude_from_sim markup //#define SEXPR_SCHEMATIC_FILE_VERSION 20230620 // ki_description -> Description Field //#define SEXPR_SCHEMATIC_FILE_VERSION 20230808 // Move Sim.Enable field to exclude_from_sim attr -#define SEXPR_SCHEMATIC_FILE_VERSION 20230819 // Allow multiple library symbol inheritance depth. +//#define SEXPR_SCHEMATIC_FILE_VERSION 20230819 // Allow multiple library symbol inheritance depth. +#define SEXPR_SCHEMATIC_FILE_VERSION 20231120 // generator_version; V8 cleanups diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp index 0a0b16effb..0816865d82 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp +++ b/eeschema/sch_plugins/kicad/sch_sexpr_lib_plugin_cache.cpp @@ -91,7 +91,7 @@ void SCH_SEXPR_PLUGIN_CACHE::Save( const std::optional& aOpt ) // Write through symlinks, don't replace them. wxFileName fn = GetRealFile(); - auto formatter = std::make_unique( fn.GetFullPath() ); + auto formatter = std::make_unique( fn.GetFullPath() ); formatter->Print( 0, "(kicad_symbol_lib (version %d) (generator \"kicad_symbol_editor\") (generator_version \"%s\")\n", SEXPR_SYMBOL_LIB_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() ); diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp index ed3f90f2ec..5ec6abad70 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp +++ b/eeschema/sch_plugins/kicad/sch_sexpr_parser.cpp @@ -29,6 +29,7 @@ // base64 code. #include +#include #define wxUSE_BASE64 1 #include #include @@ -141,6 +142,20 @@ void SCH_SEXPR_PARSER::ParseLib( LIB_SYMBOL_MAP& aSymbolLibMap ) NextTok(); parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION ); + bool versionChecked = false; + + auto checkVersion = + [&]() + { + if( !versionChecked && m_requiredVersion > SEXPR_SYMBOL_LIB_FILE_VERSION ) + { + throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ), + m_generatorVersion ); + } + + versionChecked = true; + }; + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) { if( token != T_LEFT ) @@ -148,16 +163,53 @@ void SCH_SEXPR_PARSER::ParseLib( LIB_SYMBOL_MAP& aSymbolLibMap ) token = NextTok(); - if( token == T_symbol ) + switch( token ) { + case T_generator: + // (generator "genname"); we don't care about it at the moment. + NeedSYMBOL(); + NeedRIGHT(); + break; + + case T_host: + { + // (host eeschema ["5.99"]); old version of generator token + NeedSYMBOL(); + + // Really old versions also included a host version + if( m_requiredVersion < 20200827 ) + NeedSYMBOL(); + + NeedRIGHT(); + break; + } + + case T_generator_version: + { + NextTok(); + m_generatorVersion = FromUTF8(); + NeedRIGHT(); + + // If the format includes a generator version, by this point we have enough info to + // do the version check here + checkVersion(); + break; + } + + case T_symbol: + { + // By the time we get to the first symbol, we can check the version + checkVersion(); + m_unit = 1; m_convert = 1; LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap ); aSymbolLibMap[symbol->GetName()] = symbol; + break; } - else - { - Expecting( "symbol" ); + + default: + Expecting( "symbol, generator, or generator_version" ); } } } @@ -740,30 +792,11 @@ void SCH_SEXPR_PARSER::parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersio if( tok == T_version ) { m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) ); - - if( m_requiredVersion > aFileVersion ) - throw FUTURE_FORMAT_ERROR( FromUTF8() ); - - NeedRIGHT(); - - // Skip the host name and host build version information. - NeedLEFT(); - NeedSYMBOL(); - NeedSYMBOL(); - - if( m_requiredVersion < 20200827 ) - NeedSYMBOL(); - NeedRIGHT(); } else { m_requiredVersion = aFileVersion; - - // Skip the host name and host build version information. - NeedSYMBOL(); - NeedSYMBOL(); - NeedRIGHT(); } } @@ -2379,6 +2412,16 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, bool fileHasUuid = false; + auto checkVersion = + [&]() + { + if( m_requiredVersion > SEXPR_SCHEMATIC_FILE_VERSION ) + { + throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ), + m_generatorVersion ); + } + }; + T token; if( !aIsCopyableOnly ) @@ -2395,6 +2438,10 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, // to generate one for the root schematic for instance paths. if( m_requiredVersion < 20210406 ) m_rootUuid = screen->GetUuid(); + + // Prior to version 20231120, generator_version was not present, so check the date here + if( m_requiredVersion < 20231120 ) + checkVersion(); } screen->SetFileFormatVersionAtLoad( m_requiredVersion ); @@ -2416,6 +2463,32 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly, switch( token ) { + case T_generator: + // (generator "genname"); we don't care about it at the moment. + NeedSYMBOL(); + NeedRIGHT(); + break; + + case T_host: + { + // (host eeschema ["5.99"]); old version of generator token + NeedSYMBOL(); + + // Really old versions also included a host version + if( m_requiredVersion < 20200827 ) + NeedSYMBOL(); + + NeedRIGHT(); + break; + } + + case T_generator_version: + NextTok(); + m_generatorVersion = FromUTF8(); + NeedRIGHT(); + checkVersion(); + break; + case T_uuid: NeedSYMBOL(); screen->m_uuid = parseKIID(); diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_parser.h b/eeschema/sch_plugins/kicad/sch_sexpr_parser.h index cd7739abdd..9b1483cf35 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_parser.h +++ b/eeschema/sch_plugins/kicad/sch_sexpr_parser.h @@ -207,6 +207,7 @@ private: void parseBusAlias( SCH_SCREEN* aScreen ); int m_requiredVersion; ///< Set to the symbol library file version required. + wxString m_generatorVersion; int m_unit; ///< The current unit being parsed. int m_convert; ///< The current body style being parsed. wxString m_symbolName; ///< The current symbol name. diff --git a/eeschema/schematic.keywords b/eeschema/schematic.keywords index c1540283ba..1b27293f3b 100644 --- a/eeschema/schematic.keywords +++ b/eeschema/schematic.keywords @@ -42,11 +42,14 @@ fill font footprint free +generator +generator_version global_label hide hierarchical_label hint_alt_swap hint_pin_swap +host href id image diff --git a/include/ki_exception.h b/include/ki_exception.h index 45e259395d..1449f7efa9 100644 --- a/include/ki_exception.h +++ b/include/ki_exception.h @@ -174,13 +174,17 @@ protected: */ struct KICOMMON_API FUTURE_FORMAT_ERROR : public PARSE_ERROR { - wxString requiredVersion; ///< version or date of KiCad required to open file + wxString requiredVersion; ///< Date of KiCad file format required to open file + wxString requiredGenerator; ///< Version of KiCad required to open file - FUTURE_FORMAT_ERROR( const wxString& aRequiredVersion ); - FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError, const wxString& aRequiredVersion ); + FUTURE_FORMAT_ERROR( const wxString& aRequiredVersion, + const wxString& aRequiredGenerator = wxEmptyString ); + FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError, const wxString& aRequiredVersion, + const wxString& aRequiredGenerator = wxEmptyString ); ~FUTURE_FORMAT_ERROR() throw () {} - void init( const wxString& aRequiredVersion ); + void init( const wxString& aRequiredVersion, + const wxString& aRequiredGenerator = wxEmptyString ); }; /** @} exception_types */ diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp index 7055176a8a..72d8febf21 100644 --- a/pcbnew/plugins/kicad/pcb_parser.cpp +++ b/pcbnew/plugins/kicad/pcb_parser.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -877,6 +878,16 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() parseHeader(); + auto checkVersion = + [&]() + { + if( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ) + { + throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ), + m_generatorVersion ); + } + }; + std::vector bulkAddedItems; BOARD_ITEM* item = nullptr; @@ -911,7 +922,23 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() NeedRIGHT(); break; + case T_generator_version: + { + NeedSYMBOL(); + m_generatorVersion = FromUTF8(); + NeedRIGHT(); + + // If the format includes a generator version, by this point we have enough info to + // do the version check here + checkVersion(); + + break; + } + case T_general: + // Do another version check here, for older files that do not include generator_version + checkVersion(); + parseGeneralSection(); break; @@ -1248,16 +1275,16 @@ void PCB_PARSER::parseHeader() if( tok == T_version ) { m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) ); - m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); NeedRIGHT(); } else { m_requiredVersion = 20201115; // Last version before we started writing version #s // in footprint files as well as board files. - m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); } + m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ); + m_board->SetFileFormatVersionAtLoad( m_requiredVersion ); } @@ -3866,6 +3893,16 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments CurSource(), CurLineNumber(), CurOffset() ) ); } + auto checkVersion = + [&]() + { + if( m_requiredVersion > SEXPR_BOARD_FILE_VERSION ) + { + throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ), + m_generatorVersion ); + } + }; + for( token = NextTok(); token != T_RIGHT; token = NextTok() ) { if( token == T_LEFT ) @@ -3893,6 +3930,19 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments NeedRIGHT(); break; + case T_generator_version: + { + NeedSYMBOL(); + m_generatorVersion = FromUTF8(); + NeedRIGHT(); + + // If the format includes a generator version, by this point we have enough info to + // do the version check here + checkVersion(); + + break; + } + case T_locked: footprint->SetLocked( true ); break; diff --git a/pcbnew/plugins/kicad/pcb_parser.h b/pcbnew/plugins/kicad/pcb_parser.h index 19bc4dff39..dcc6853c87 100644 --- a/pcbnew/plugins/kicad/pcb_parser.h +++ b/pcbnew/plugins/kicad/pcb_parser.h @@ -386,6 +386,7 @@ private: std::vector m_netCodes; ///< net codes mapping for boards being loaded bool m_tooRecent; ///< true if version parses as later than supported int m_requiredVersion; ///< set to the KiCad format version this board requires + wxString m_generatorVersion; ///< Set to the generator version this board requires bool m_appendToExisting; ///< reading into an existing board; reset UUIDs ///< if resetting UUIDs, record new ones to update groups with.