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
incry
incrlabel
generator
generator_version

View File

@ -24,6 +24,7 @@
*/
#include <charconv>
#include <fmt/format.h>
#include <wx/base64.h>
#include <wx/ffile.h>
#include <wx/log.h>
@ -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
{

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;
requiredGenerator = aRequiredGenerator;
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<const FUTURE_FORMAT_ERROR*>( &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();

View File

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

View File

@ -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

View File

@ -91,7 +91,7 @@ void SCH_SEXPR_PLUGIN_CACHE::Save( const std::optional<bool>& aOpt )
// Write through symlinks, don't replace them.
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",
SEXPR_SYMBOL_LIB_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() );

View File

@ -29,6 +29,7 @@
// base64 code.
#include <charconv>
#include <fmt/format.h>
#define wxUSE_BASE64 1
#include <wx/base64.h>
#include <wx/mstream.h>
@ -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();

View File

@ -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.

View File

@ -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

View File

@ -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 */

View File

@ -31,6 +31,7 @@
#include <charconv>
#include <confirm.h>
#include <macros.h>
#include <fmt/format.h>
#include <title_block.h>
#include <trigo.h>
@ -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<BOARD_ITEM*> 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;

View File

@ -386,6 +386,7 @@ private:
std::vector<int> 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.