Parse generator_version and improve error output on mismatch

Also bump symbol format version for generator_version
This commit is contained in:
Jon Evans 2023-11-19 09:02:52 -05:00
parent b13e244dc5
commit 4ff127b452
12 changed files with 222 additions and 51 deletions

View File

@ -51,3 +51,5 @@ repeat
incrx incrx
incry incry
incrlabel incrlabel
generator
generator_version

View File

@ -24,6 +24,7 @@
*/ */
#include <charconv> #include <charconv>
#include <fmt/format.h>
#include <wx/base64.h> #include <wx/base64.h>
#include <wx/ffile.h> #include <wx/ffile.h>
#include <wx/log.h> #include <wx/log.h>
@ -53,6 +54,7 @@ public:
private: private:
int m_requiredVersion; int m_requiredVersion;
wxString m_generatorVersion;
/** /**
* Parse the data specified at the very beginning of the file, like version and the * 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 ); parseHeader( token );
aLayout->SetFileFormatVersionAtLoad( m_requiredVersion ); 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() ) for( token = NextTok(); token != T_RIGHT && token != EOF; token = NextTok() )
{ {
if( token == T_LEFT ) if( token == T_LEFT )
@ -216,7 +228,22 @@ void DRAWING_SHEET_PARSER::Parse( DS_DATA_MODEL* aLayout )
switch( token ) 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 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 ); parseSetup( aLayout );
break; break;
@ -285,21 +312,12 @@ void DRAWING_SHEET_PARSER::parseHeader( T aHeaderType )
{ {
m_requiredVersion = parseInt(); m_requiredVersion = parseInt();
if( m_requiredVersion > SEXPR_WORKSHEET_FILE_VERSION )
throw FUTURE_FORMAT_ERROR( FromUTF8() );
NeedRIGHT(); NeedRIGHT();
} }
else else
{ {
Expecting( T_version ); Expecting( T_version );
} }
// Ignore generator info.
NeedLEFT();
NeedSYMBOL();
NeedSYMBOL();
NeedRIGHT();
} }
else else
{ {

View File

@ -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; requiredVersion = aRequiredVersion;
requiredGenerator = aRequiredGenerator;
problem.Printf( _( "KiCad was unable to open this file because it was created with a more " if( requiredGenerator.IsEmpty() )
"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 " problem.Printf( _( "KiCad was unable to open this file because it was created with a more "
"later." ), "recent version than the one you are running.\n\n"
aRequiredVersion ); "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() PARSE_ERROR()
{ {
init( aRequiredVersion ); init( aRequiredVersion, aRequiredGenerator );
lineNumber = 0; lineNumber = 0;
byteIndex = 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, FUTURE_FORMAT_ERROR::FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError,
const wxString& aRequiredVersion ) : const wxString& aRequiredVersion,
const wxString& aRequiredGenerator ) :
PARSE_ERROR() PARSE_ERROR()
{ {
if( const FUTURE_FORMAT_ERROR* ffe = dynamic_cast<const FUTURE_FORMAT_ERROR*>( &aParseError ) ) if( const FUTURE_FORMAT_ERROR* ffe = dynamic_cast<const FUTURE_FORMAT_ERROR*>( &aParseError ) )
{ {
requiredVersion = ffe->requiredVersion; requiredVersion = ffe->requiredVersion;
requiredGenerator = ffe->requiredGenerator;
problem = ffe->Problem(); problem = ffe->Problem();
} }
else else
{ {
init( aRequiredVersion ); init( aRequiredVersion, aRequiredGenerator );
if( !aParseError.Problem().IsEmpty() ) if( !aParseError.Problem().IsEmpty() )
problem += wxS( "\n\n" ) + _( "Full error text:" ) + wxS( "\n" ) + aParseError.Problem(); problem += wxS( "\n\n" ) + _( "Full error text:" ) + wxS( "\n" ) + aParseError.Problem();

View File

@ -136,6 +136,7 @@ free
full full
general general
generator generator
generator_version
generated generated
grid_origin grid_origin
group group

View File

@ -48,7 +48,8 @@
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220331 // Text colors. //#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 // Symbol unit display names.
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220914 // Don't save property ID //#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. * Schematic file version.
@ -100,4 +101,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20230409 // Add exclude_from_sim markup //#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 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 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

View File

@ -91,7 +91,7 @@ void SCH_SEXPR_PLUGIN_CACHE::Save( const std::optional<bool>& aOpt )
// Write through symlinks, don't replace them. // Write through symlinks, don't replace them.
wxFileName fn = GetRealFile(); wxFileName fn = GetRealFile();
auto formatter = std::make_unique<FILE_OUTPUTFORMATTER>( fn.GetFullPath() ); auto formatter = std::make_unique<PRETTIFIED_FILE_OUTPUTFORMATTER>( fn.GetFullPath() );
formatter->Print( 0, "(kicad_symbol_lib (version %d) (generator \"kicad_symbol_editor\") (generator_version \"%s\")\n", 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() ); SEXPR_SYMBOL_LIB_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() );

View File

@ -29,6 +29,7 @@
// base64 code. // base64 code.
#include <charconv> #include <charconv>
#include <fmt/format.h>
#define wxUSE_BASE64 1 #define wxUSE_BASE64 1
#include <wx/base64.h> #include <wx/base64.h>
#include <wx/mstream.h> #include <wx/mstream.h>
@ -141,6 +142,20 @@ void SCH_SEXPR_PARSER::ParseLib( LIB_SYMBOL_MAP& aSymbolLibMap )
NextTok(); NextTok();
parseHeader( T_kicad_symbol_lib, SEXPR_SYMBOL_LIB_FILE_VERSION ); 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() ) for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{ {
if( token != T_LEFT ) if( token != T_LEFT )
@ -148,16 +163,53 @@ void SCH_SEXPR_PARSER::ParseLib( LIB_SYMBOL_MAP& aSymbolLibMap )
token = NextTok(); 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_unit = 1;
m_convert = 1; m_convert = 1;
LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap ); LIB_SYMBOL* symbol = parseLibSymbol( aSymbolLibMap );
aSymbolLibMap[symbol->GetName()] = symbol; aSymbolLibMap[symbol->GetName()] = symbol;
break;
} }
else
{ default:
Expecting( "symbol" ); 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 ) if( tok == T_version )
{ {
m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) ); 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(); NeedRIGHT();
} }
else else
{ {
m_requiredVersion = aFileVersion; 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; bool fileHasUuid = false;
auto checkVersion =
[&]()
{
if( m_requiredVersion > SEXPR_SCHEMATIC_FILE_VERSION )
{
throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
m_generatorVersion );
}
};
T token; T token;
if( !aIsCopyableOnly ) 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. // to generate one for the root schematic for instance paths.
if( m_requiredVersion < 20210406 ) if( m_requiredVersion < 20210406 )
m_rootUuid = screen->GetUuid(); 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 ); screen->SetFileFormatVersionAtLoad( m_requiredVersion );
@ -2416,6 +2463,32 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
switch( token ) 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: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
screen->m_uuid = parseKIID(); screen->m_uuid = parseKIID();

View File

@ -207,6 +207,7 @@ private:
void parseBusAlias( SCH_SCREEN* aScreen ); void parseBusAlias( SCH_SCREEN* aScreen );
int m_requiredVersion; ///< Set to the symbol library file version required. int m_requiredVersion; ///< Set to the symbol library file version required.
wxString m_generatorVersion;
int m_unit; ///< The current unit being parsed. int m_unit; ///< The current unit being parsed.
int m_convert; ///< The current body style being parsed. int m_convert; ///< The current body style being parsed.
wxString m_symbolName; ///< The current symbol name. wxString m_symbolName; ///< The current symbol name.

View File

@ -42,11 +42,14 @@ fill
font font
footprint footprint
free free
generator
generator_version
global_label global_label
hide hide
hierarchical_label hierarchical_label
hint_alt_swap hint_alt_swap
hint_pin_swap hint_pin_swap
host
href href
id id
image image

View File

@ -174,13 +174,17 @@ protected:
*/ */
struct KICOMMON_API FUTURE_FORMAT_ERROR : public PARSE_ERROR 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 wxString& aRequiredVersion,
FUTURE_FORMAT_ERROR( const PARSE_ERROR& aParseError, 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 () {} ~FUTURE_FORMAT_ERROR() throw () {}
void init( const wxString& aRequiredVersion ); void init( const wxString& aRequiredVersion,
const wxString& aRequiredGenerator = wxEmptyString );
}; };
/** @} exception_types */ /** @} exception_types */

View File

@ -31,6 +31,7 @@
#include <charconv> #include <charconv>
#include <confirm.h> #include <confirm.h>
#include <macros.h> #include <macros.h>
#include <fmt/format.h>
#include <title_block.h> #include <title_block.h>
#include <trigo.h> #include <trigo.h>
@ -877,6 +878,16 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
parseHeader(); parseHeader();
auto checkVersion =
[&]()
{
if( m_requiredVersion > SEXPR_BOARD_FILE_VERSION )
{
throw FUTURE_FORMAT_ERROR( fmt::format( "{}", m_requiredVersion ),
m_generatorVersion );
}
};
std::vector<BOARD_ITEM*> bulkAddedItems; std::vector<BOARD_ITEM*> bulkAddedItems;
BOARD_ITEM* item = nullptr; BOARD_ITEM* item = nullptr;
@ -911,7 +922,23 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
NeedRIGHT(); NeedRIGHT();
break; 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: case T_general:
// Do another version check here, for older files that do not include generator_version
checkVersion();
parseGeneralSection(); parseGeneralSection();
break; break;
@ -1248,16 +1275,16 @@ void PCB_PARSER::parseHeader()
if( tok == T_version ) if( tok == T_version )
{ {
m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) ); m_requiredVersion = parseInt( FromUTF8().mb_str( wxConvUTF8 ) );
m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
NeedRIGHT(); NeedRIGHT();
} }
else else
{ {
m_requiredVersion = 20201115; // Last version before we started writing version #s m_requiredVersion = 20201115; // Last version before we started writing version #s
// in footprint files as well as board files. // 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 ); m_board->SetFileFormatVersionAtLoad( m_requiredVersion );
} }
@ -3866,6 +3893,16 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
CurSource(), CurLineNumber(), CurOffset() ) ); 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() ) for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{ {
if( token == T_LEFT ) if( token == T_LEFT )
@ -3893,6 +3930,19 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
NeedRIGHT(); NeedRIGHT();
break; 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: case T_locked:
footprint->SetLocked( true ); footprint->SetLocked( true );
break; break;

View File

@ -386,6 +386,7 @@ private:
std::vector<int> m_netCodes; ///< net codes mapping for boards being loaded std::vector<int> m_netCodes; ///< net codes mapping for boards being loaded
bool m_tooRecent; ///< true if version parses as later than supported bool m_tooRecent; ///< true if version parses as later than supported
int m_requiredVersion; ///< set to the KiCad format version this board requires 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 bool m_appendToExisting; ///< reading into an existing board; reset UUIDs
///< if resetting UUIDs, record new ones to update groups with. ///< if resetting UUIDs, record new ones to update groups with.