Support env variables in spice library paths.

Also removes a bunch of std::string stuff from the file handling in the
simulator.  All our file handling, env variable expansion, project path,
etc. stuff is wxString based, and jumping through std::string in between
just makes it more complex and increases the potential bug surface.

Also fixes a bug where you'd get two error messages when a spice model
library wasn't found.

Also fixes a bug where you'd get a spice model library not found error
when the text field was empty.

Also fixes a bug where we'd try to absolutize a path starting with an
unresolved text or environment variable.  If the path starts with a
variable it's probably absolute, and tacking on the project path in the
error message just obfuscates things.

Fixes https://gitlab.com/kicad/code/kicad/issues/13082
This commit is contained in:
Jeff Young 2022-12-07 15:02:35 +00:00
parent 5aa204e194
commit b15913bd53
16 changed files with 111 additions and 103 deletions

View File

@ -264,7 +264,7 @@ wxString KIwxExpandEnvVars( const wxString& str, const PROJECT* aProject )
} }
const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject ) const wxString ExpandEnvVarSubstitutions( const wxString& aString, const PROJECT* aProject )
{ {
// wxGetenv( wchar_t* ) is not re-entrant on linux. // wxGetenv( wchar_t* ) is not re-entrant on linux.
// Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(), // Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(),

View File

@ -306,6 +306,11 @@ const wxString PROJECT::AbsolutePath( const wxString& aFileName ) const
{ {
wxFileName fn = aFileName; wxFileName fn = aFileName;
// Paths which start with an unresolved variable reference are more likely to be
// absolute than relative.
if( aFileName.StartsWith( wxT( "${" ) ) )
return aFileName;
if( !fn.IsAbsolute() ) if( !fn.IsAbsolute() )
{ {
wxString pro_dir = wxPathOnly( GetProjectFullName() ); wxString pro_dir = wxPathOnly( GetProjectFullName() );

View File

@ -55,7 +55,8 @@ DIALOG_SIM_MODEL<T>::DIALOG_SIM_MODEL( wxWindow* aParent, SCH_SYMBOL& aSymbol,
m_scintillaTricks( nullptr ), m_scintillaTricks( nullptr ),
m_wasCodePreviewUpdated( true ), m_wasCodePreviewUpdated( true ),
m_firstCategory( nullptr ), m_firstCategory( nullptr ),
m_prevParamGridSelection( nullptr ) m_prevParamGridSelection( nullptr ),
m_inKillFocus( false )
{ {
m_modelNameCombobox->SetValidator( m_modelNameValidator ); m_modelNameCombobox->SetValidator( m_modelNameValidator );
m_browseButton->SetBitmap( KiBitmap( BITMAPS::small_folder ) ); m_browseButton->SetBitmap( KiBitmap( BITMAPS::small_folder ) );
@ -262,7 +263,7 @@ bool DIALOG_SIM_MODEL<T>::TransferDataFromWindow()
if( ( library() && m_useLibraryModelRadioButton->GetValue() ) || isIbisLoaded() ) if( ( library() && m_useLibraryModelRadioButton->GetValue() ) || isIbisLoaded() )
{ {
path = library()->GetFilePath(); path = m_libraryPathText->GetValue();
wxFileName fn( path ); wxFileName fn( path );
if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( ".." ) ) if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( ".." ) )
@ -618,23 +619,16 @@ void DIALOG_SIM_MODEL<T>::loadLibrary( const wxString& aLibraryPath, bool aForce
if( !aForceReload && libraries.size() >= 1 && libraries.begin()->first == aLibraryPath ) if( !aForceReload && libraries.size() >= 1 && libraries.begin()->first == aLibraryPath )
return; return;
DIALOG_IBIS_PARSER_REPORTER dlg( this ); DIALOG_IBIS_PARSER_REPORTER dlg( this );
dlg.m_messagePanel->Clear(); dlg.m_messagePanel->Clear();
bool tryingToLoadIbis = false;
if( aLibraryPath.EndsWith( ".ibs" ) )
tryingToLoadIbis = true;
try try
{ {
m_libraryModelsMgr.SetLibrary( std::string( aLibraryPath.ToUTF8() ), m_libraryModelsMgr.CreateLibrary( aLibraryPath, &dlg.m_messagePanel->Reporter() );
&dlg.m_messagePanel->Reporter() );
} }
catch( const IO_ERROR& e ) catch( const IO_ERROR& e )
{ {
if( tryingToLoadIbis ) if( dlg.m_messagePanel->Reporter().HasMessage() )
{ {
dlg.m_messagePanel->Flush(); dlg.m_messagePanel->Flush();
dlg.ShowQuasiModal(); dlg.ShowQuasiModal();
@ -976,29 +970,47 @@ void DIALOG_SIM_MODEL<T>::onLibraryPathTextEnter( wxCommandEvent& aEvent )
if( m_useLibraryModelRadioButton->GetValue() ) if( m_useLibraryModelRadioButton->GetValue() )
{ {
wxString path = m_libraryPathText->GetValue(); wxString path = m_libraryPathText->GetValue();
wxFileName fn( path );
if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( ".." ) ) if( !path.IsEmpty() )
path = fn.GetFullPath();
try
{ {
loadLibrary( path ); try
updateWidgets(); {
} loadLibrary( path );
catch( const IO_ERROR& ) updateWidgets();
{ }
// TODO: Add an infobar to report the error? catch( const IO_ERROR& )
{
// TODO: Add an infobar to report the error?
}
} }
} }
} }
template <typename T>
void DIALOG_SIM_MODEL<T>::onLibraryPathText( wxCommandEvent& aEvent )
{
}
template <typename T>
void DIALOG_SIM_MODEL<T>::onLibraryPathTextSetFocus( wxFocusEvent& aEvent )
{
}
template <typename T> template <typename T>
void DIALOG_SIM_MODEL<T>::onLibraryPathTextKillFocus( wxFocusEvent& aEvent ) void DIALOG_SIM_MODEL<T>::onLibraryPathTextKillFocus( wxFocusEvent& aEvent )
{ {
wxCommandEvent dummy; if( !m_inKillFocus )
onLibraryPathTextEnter( dummy ); {
m_inKillFocus = true;
wxCommandEvent dummy;
onLibraryPathTextEnter( dummy );
m_inKillFocus = false;
}
} }

View File

@ -114,8 +114,10 @@ private:
int getModelPinIndex( const wxString& aModelPinString ) const; int getModelPinIndex( const wxString& aModelPinString ) const;
void onRadioButton( wxCommandEvent& aEvent ) override; void onRadioButton( wxCommandEvent& aEvent ) override;
void onLibraryPathText( wxCommandEvent& aEvent ) override;
void onLibraryPathTextEnter( wxCommandEvent& aEvent ) override; void onLibraryPathTextEnter( wxCommandEvent& aEvent ) override;
void onLibraryPathTextKillFocus( wxFocusEvent& aEvent ) override; void onLibraryPathTextKillFocus( wxFocusEvent& aEvent ) override;
void onLibraryPathTextSetFocus( wxFocusEvent& aEvent ) override;
void onBrowseButtonClick( wxCommandEvent& aEvent ) override; void onBrowseButtonClick( wxCommandEvent& aEvent ) override;
void onModelNameCombobox( wxCommandEvent& aEvent ) override; void onModelNameCombobox( wxCommandEvent& aEvent ) override;
void onModelNameComboboxKillFocus( wxFocusEvent& event ) override; void onModelNameComboboxKillFocus( wxFocusEvent& event ) override;
@ -157,6 +159,8 @@ private:
wxPGProperty* m_firstCategory; // Used to add principal parameters to root. wxPGProperty* m_firstCategory; // Used to add principal parameters to root.
wxPGProperty* m_prevParamGridSelection; wxPGProperty* m_prevParamGridSelection;
bool m_inKillFocus;
}; };
#endif /* DIALOG_SIM_MODEL_H */ #endif /* DIALOG_SIM_MODEL_H */

View File

@ -281,8 +281,9 @@ DIALOG_SIM_MODEL_BASE::DIALOG_SIM_MODEL_BASE( wxWindow* parent, wxWindowID id, c
m_useLibraryModelRadioButton->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onRadioButton ), NULL, this ); m_useLibraryModelRadioButton->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onRadioButton ), NULL, this );
m_pathLabel->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathLabelUpdate ), NULL, this ); m_pathLabel->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathLabelUpdate ), NULL, this );
m_libraryPathText->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextKillFocus ), NULL, this ); m_libraryPathText->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextKillFocus ), NULL, this );
m_libraryPathText->Connect( wxEVT_SET_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextSetFocus ), NULL, this );
m_libraryPathText->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathText ), NULL, this );
m_libraryPathText->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextEnter ), NULL, this ); m_libraryPathText->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextEnter ), NULL, this );
m_libraryPathText->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathUpdate ), NULL, this );
m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonClick ), NULL, this ); m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonClick ), NULL, this );
m_browseButton->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonUpdate ), NULL, this ); m_browseButton->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonUpdate ), NULL, this );
m_modelNameLabel->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onModelNameLabelUpdate ), NULL, this ); m_modelNameLabel->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onModelNameLabelUpdate ), NULL, this );
@ -322,8 +323,9 @@ DIALOG_SIM_MODEL_BASE::~DIALOG_SIM_MODEL_BASE()
m_useLibraryModelRadioButton->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onRadioButton ), NULL, this ); m_useLibraryModelRadioButton->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onRadioButton ), NULL, this );
m_pathLabel->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathLabelUpdate ), NULL, this ); m_pathLabel->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathLabelUpdate ), NULL, this );
m_libraryPathText->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextKillFocus ), NULL, this ); m_libraryPathText->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextKillFocus ), NULL, this );
m_libraryPathText->Disconnect( wxEVT_SET_FOCUS, wxFocusEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextSetFocus ), NULL, this );
m_libraryPathText->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathText ), NULL, this );
m_libraryPathText->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextEnter ), NULL, this ); m_libraryPathText->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathTextEnter ), NULL, this );
m_libraryPathText->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onLibraryPathUpdate ), NULL, this );
m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonClick ), NULL, this ); m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonClick ), NULL, this );
m_browseButton->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonUpdate ), NULL, this ); m_browseButton->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onBrowseButtonUpdate ), NULL, this );
m_modelNameLabel->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onModelNameLabelUpdate ), NULL, this ); m_modelNameLabel->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_SIM_MODEL_BASE::onModelNameLabelUpdate ), NULL, this );

View File

@ -407,8 +407,9 @@
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<event name="OnKillFocus">onLibraryPathTextKillFocus</event> <event name="OnKillFocus">onLibraryPathTextKillFocus</event>
<event name="OnSetFocus">onLibraryPathTextSetFocus</event>
<event name="OnText">onLibraryPathText</event>
<event name="OnTextEnter">onLibraryPathTextEnter</event> <event name="OnTextEnter">onLibraryPathTextEnter</event>
<event name="OnUpdateUI">onLibraryPathUpdate</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">

View File

@ -87,8 +87,9 @@ class DIALOG_SIM_MODEL_BASE : public DIALOG_SHIM
virtual void onRadioButton( wxCommandEvent& event ) { event.Skip(); } virtual void onRadioButton( wxCommandEvent& event ) { event.Skip(); }
virtual void onLibraryPathLabelUpdate( wxUpdateUIEvent& event ) { event.Skip(); } virtual void onLibraryPathLabelUpdate( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void onLibraryPathTextKillFocus( wxFocusEvent& event ) { event.Skip(); } virtual void onLibraryPathTextKillFocus( wxFocusEvent& event ) { event.Skip(); }
virtual void onLibraryPathTextSetFocus( wxFocusEvent& event ) { event.Skip(); }
virtual void onLibraryPathText( wxCommandEvent& event ) { event.Skip(); }
virtual void onLibraryPathTextEnter( wxCommandEvent& event ) { event.Skip(); } virtual void onLibraryPathTextEnter( wxCommandEvent& event ) { event.Skip(); }
virtual void onLibraryPathUpdate( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void onBrowseButtonClick( wxCommandEvent& event ) { event.Skip(); } virtual void onBrowseButtonClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onBrowseButtonUpdate( wxUpdateUIEvent& event ) { event.Skip(); } virtual void onBrowseButtonUpdate( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void onModelNameLabelUpdate( wxUpdateUIEvent& event ) { event.Skip(); } virtual void onModelNameLabelUpdate( wxUpdateUIEvent& event ) { event.Skip(); }

View File

@ -359,7 +359,7 @@ void NETLIST_EXPORTER_SPICE::ReadDirectives( unsigned aNetlistOptions, REPORTER&
try try
{ {
m_libMgr.CreateLibrary( path ); m_libMgr.CreateLibrary( path, &aReporter );
} }
catch( const IO_ERROR& e ) catch( const IO_ERROR& e )
{ {
@ -407,37 +407,34 @@ void NETLIST_EXPORTER_SPICE::readModel( SCH_SHEET_PATH& aSheet, SCH_SYMBOL& aSym
// FIXME: Don't have special cases for raw Spice models and KIBIS. // FIXME: Don't have special cases for raw Spice models and KIBIS.
if( auto rawSpiceModel = dynamic_cast<const SIM_MODEL_RAW_SPICE*>( aItem.model ) ) if( auto rawSpiceModel = dynamic_cast<const SIM_MODEL_RAW_SPICE*>( aItem.model ) )
{ {
int libParamIndex = static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB ); int libParamIndex = static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
std::string path = rawSpiceModel->GetParam( libParamIndex ).value->ToString(); wxString path = rawSpiceModel->GetParam( libParamIndex ).value->ToString();
if( path != "" ) if( !path.IsEmpty() )
m_rawIncludes.insert( path ); m_rawIncludes.insert( path );
} }
else if( auto kibisModel = dynamic_cast<const SIM_MODEL_KIBIS*>( aItem.model ) ) else if( auto kibisModel = dynamic_cast<const SIM_MODEL_KIBIS*>( aItem.model ) )
{ {
wxFileName cacheDir; wxFileName cacheFn;
cacheDir.AssignDir( PATHS::GetUserCachePath() ); cacheFn.AssignDir( PATHS::GetUserCachePath() );
cacheDir.AppendDir( wxT( "ibis" ) ); cacheFn.AppendDir( wxT( "ibis" ) );
cacheFn.SetFullName( aSymbol.GetRef( &aSheet ) + wxT( ".cache" ) );
std::string libraryPath = fmt::format( "{}/{}.cache", wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
std::string( cacheDir.GetPath() ),
std::string( aSymbol.GetRef( &aSheet ) ) );
wxFile cacheFile( libraryPath, wxFile::write );
if( !cacheFile.IsOpened() ) if( !cacheFile.IsOpened() )
{ {
DisplayErrorMessage( nullptr, DisplayErrorMessage( nullptr, wxString::Format( _( "Could not open file '%s' to write "
wxString::Format( _( "Could not open file '%s' to write IBIS model" ), "IBIS model" ),
libraryPath ) ); cacheFn.GetFullPath() ) );
} }
auto spiceGenerator = static_cast<const SPICE_GENERATOR_KIBIS&>( kibisModel->SpiceGenerator() ); auto spiceGenerator = static_cast<const SPICE_GENERATOR_KIBIS&>( kibisModel->SpiceGenerator() );
std::string modelData = spiceGenerator.IbisDevice( std::string modelData = spiceGenerator.IbisDevice( aItem, m_schematic->Prj(),
aItem, std::string( m_schematic->Prj().GetProjectPath().c_str() ), cacheFn.GetPath( wxPATH_GET_SEPARATOR ) );
std::string( cacheDir.GetPath( wxPATH_GET_SEPARATOR ) ).c_str() );
cacheFile.Write( wxString( modelData ) ); cacheFile.Write( wxString( modelData ) );
m_rawIncludes.insert( libraryPath ); m_rawIncludes.insert( cacheFn.GetFullPath() );
} }
} }
@ -464,7 +461,7 @@ void NETLIST_EXPORTER_SPICE::readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& a
void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions, void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
const std::string& aPath ) const wxString& aPath )
{ {
// First, expand env vars, if any. // First, expand env vars, if any.
wxString expandedPath = ExpandEnvVarSubstitutions( aPath, &m_schematic->Prj() ); wxString expandedPath = ExpandEnvVarSubstitutions( aPath, &m_schematic->Prj() );
@ -495,13 +492,13 @@ void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned
void NETLIST_EXPORTER_SPICE::writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions ) void NETLIST_EXPORTER_SPICE::writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
{ {
for( auto&& [path, library] : m_libMgr.GetLibraries() ) for( auto& [path, library] : m_libMgr.GetLibraries() )
{ {
if( dynamic_cast<const SIM_LIBRARY_SPICE*>( &library.get() ) ) if( dynamic_cast<const SIM_LIBRARY_SPICE*>( &library.get() ) )
writeInclude( aFormatter, aNetlistOptions, path ); writeInclude( aFormatter, aNetlistOptions, path );
} }
for( const std::string& path : m_rawIncludes ) for( const wxString& path : m_rawIncludes )
writeInclude( aFormatter, aNetlistOptions, path ); writeInclude( aFormatter, aNetlistOptions, path );
} }

View File

@ -144,7 +144,7 @@ private:
void readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& aItem, int& aNcCounter ); void readPinNetNames( SCH_SYMBOL& aSymbol, SPICE_ITEM& aItem, int& aNcCounter );
void writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions, void writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
const std::string& aPath ); const wxString& aPath );
void writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions ); void writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions );
void writeModels( OUTPUTFORMATTER& aFormatter ); void writeModels( OUTPUTFORMATTER& aFormatter );
@ -156,7 +156,7 @@ private:
std::string m_title; ///< Spice simulation title found in the schematic sheet std::string m_title; ///< Spice simulation title found in the schematic sheet
std::vector<std::string> m_directives; ///< Spice directives found in the schematic sheet std::vector<std::string> m_directives; ///< Spice directives found in the schematic sheet
//std::map<std::string, std::unique_ptr<SIM_LIBRARY>> m_libraries; ///< Spice libraries //std::map<std::string, std::unique_ptr<SIM_LIBRARY>> m_libraries; ///< Spice libraries
std::set<std::string> m_rawIncludes; ///< include directives found in symbols std::set<wxString> m_rawIncludes; ///< include directives found in symbols
std::set<std::string> m_nets; std::set<std::string> m_nets;
std::list<SPICE_ITEM> m_items; ///< Items representing schematic symbols in Spice world std::list<SPICE_ITEM> m_items; ///< Items representing schematic symbols in Spice world
}; };

View File

@ -24,6 +24,7 @@
#include <pgm_base.h> #include <pgm_base.h>
#include <string> #include <string>
#include <common.h>
#include <sch_symbol.h> #include <sch_symbol.h>
// Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers // Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
@ -33,7 +34,6 @@
#include <sim/sim_model.h> #include <sim/sim_model.h>
#include <sim/sim_model_ideal.h> #include <sim/sim_model_ideal.h>
SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT& aPrj ) : m_project( aPrj ) SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT& aPrj ) : m_project( aPrj )
{ {
} }
@ -46,26 +46,18 @@ void SIM_LIB_MGR::Clear()
} }
SIM_LIBRARY& SIM_LIB_MGR::CreateLibrary( const std::string& aLibraryPath, REPORTER* aReporter ) SIM_LIBRARY& SIM_LIB_MGR::CreateLibrary( const wxString& aLibraryPath, REPORTER* aReporter )
{ {
std::string absolutePath = std::string( m_project.AbsolutePath( aLibraryPath ).ToUTF8() ); wxString path = ExpandEnvVarSubstitutions( aLibraryPath, &m_project );
wxString absolutePath = m_project.AbsolutePath( path );
auto it =
m_libraries.try_emplace( aLibraryPath, SIM_LIBRARY::Create( absolutePath, aReporter ) )
.first;
return *it->second;
}
SIM_LIBRARY& SIM_LIB_MGR::SetLibrary( const std::string& aLibraryPath, REPORTER* aReporter )
{
std::string absolutePath = std::string( m_project.AbsolutePath( aLibraryPath ).ToUTF8() );
// May throw an exception. // May throw an exception.
std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( absolutePath, aReporter ); std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( m_project.AbsolutePath( path ),
aReporter );
Clear(); Clear();
m_libraries[aLibraryPath] = std::move( library ); m_libraries[path] = std::move( library );
return *m_libraries.at( aLibraryPath ); return *m_libraries.at( path );
} }
@ -153,18 +145,18 @@ template SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<LIB_FIEL
template <typename T> template <typename T>
SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::string& aLibraryPath, SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
const std::string& aBaseModelName, const std::string& aBaseModelName,
const std::vector<T>& aFields, const std::vector<T>& aFields,
int aSymbolPinCount ) int aSymbolPinCount )
{ {
std::string absolutePath = std::string( m_project.AbsolutePath( aLibraryPath ).ToUTF8() ); wxString path = ExpandEnvVarSubstitutions( aLibraryPath, &m_project );
wxString absolutePath = m_project.AbsolutePath( path );
SIM_LIBRARY* library = nullptr; SIM_LIBRARY* library = nullptr;
try try
{ {
auto it = m_libraries.try_emplace( aLibraryPath, auto it = m_libraries.try_emplace( path, SIM_LIBRARY::Create( absolutePath ) ).first;
SIM_LIBRARY::Create( absolutePath ) ).first;
library = &*it->second; library = &*it->second;
} }
catch( const IO_ERROR& e ) catch( const IO_ERROR& e )
@ -203,9 +195,9 @@ void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
} }
std::map<std::string, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
{ {
std::map<std::string, std::reference_wrapper<const SIM_LIBRARY>> libraries; std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
for( auto& [path, library] : m_libraries ) for( auto& [path, library] : m_libraries )
libraries.try_emplace( path, *library ); libraries.try_emplace( path, *library );

View File

@ -45,8 +45,7 @@ public:
void Clear(); void Clear();
SIM_LIBRARY& CreateLibrary( const std::string& aLibraryPath, REPORTER* aReporter = nullptr ); SIM_LIBRARY& CreateLibrary( const wxString& aLibraryPath, REPORTER* aReporter );
SIM_LIBRARY& SetLibrary( const std::string& aLibraryPath, REPORTER* aReporter = nullptr );
SIM_MODEL& CreateModel( SIM_MODEL::TYPE aType, int aSymbolPinCount ); SIM_MODEL& CreateModel( SIM_MODEL::TYPE aType, int aSymbolPinCount );
@ -63,20 +62,18 @@ public:
SIM_LIBRARY::MODEL CreateModel( const std::vector<T>& aFields, int aSymbolPinCount ); SIM_LIBRARY::MODEL CreateModel( const std::vector<T>& aFields, int aSymbolPinCount );
template <typename T> template <typename T>
SIM_LIBRARY::MODEL CreateModel( const std::string& aLibraryPath, SIM_LIBRARY::MODEL CreateModel( const wxString& aLibraryPath, const std::string& aBaseModelName,
const std::string& aBaseModelName, const std::vector<T>& aFields, int aSymbolPinCount );
const std::vector<T>& aFields,
int aSymbolPinCount );
void SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel ); void SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel );
std::map<std::string, std::reference_wrapper<const SIM_LIBRARY>> GetLibraries() const; std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> GetLibraries() const;
std::vector<std::reference_wrapper<SIM_MODEL>> GetModels() const; std::vector<std::reference_wrapper<SIM_MODEL>> GetModels() const;
private: private:
const PROJECT& m_project; const PROJECT& m_project;
std::map<std::string, std::unique_ptr<SIM_LIBRARY>> m_libraries; std::map<wxString, std::unique_ptr<SIM_LIBRARY>> m_libraries;
std::vector<std::unique_ptr<SIM_MODEL>> m_models; std::vector<std::unique_ptr<SIM_MODEL>> m_models;
}; };

View File

@ -27,18 +27,17 @@
#include <sim/sim_library_spice.h> #include <sim/sim_library_spice.h>
std::unique_ptr<SIM_LIBRARY> SIM_LIBRARY::Create( std::string aFilePath, REPORTER* aReporter ) std::unique_ptr<SIM_LIBRARY> SIM_LIBRARY::Create( const wxString& aFilePath, REPORTER* aReporter )
{ {
std::unique_ptr<SIM_LIBRARY> library; std::unique_ptr<SIM_LIBRARY> library;
wxString wxaFilePath( aFilePath );
if( wxaFilePath.EndsWith( ".ibs" ) ) if( aFilePath.EndsWith( ".ibs" ) )
library = std::make_unique<SIM_LIBRARY_KIBIS>(); library = std::make_unique<SIM_LIBRARY_KIBIS>();
else else
library = std::make_unique<SIM_LIBRARY_SPICE>(); library = std::make_unique<SIM_LIBRARY_SPICE>();
library->m_reporter = aReporter; library->m_reporter = aReporter;
library->ReadFile( aFilePath ); library->ReadFile( std::string( aFilePath.c_str() ) );
return library; return library;
} }

View File

@ -52,8 +52,8 @@ public:
* @param aReporter The reporter the library reports to * @param aReporter The reporter the library reports to
* @return The library loaded in a newly constructed object. * @return The library loaded in a newly constructed object.
*/ */
static std::unique_ptr<SIM_LIBRARY> Create( std::string aFilePath, static std::unique_ptr<SIM_LIBRARY> Create( const wxString& aFilePath,
REPORTER* aReporter = nullptr ); REPORTER* aReporter = nullptr );
/** /**
* Read library from a source file. Must be in the format appropriate to the subclass, e.g. * Read library from a source file. Must be in the format appropriate to the subclass, e.g.

View File

@ -24,7 +24,7 @@
#include <sim/kibis/kibis.h> #include <sim/kibis/kibis.h>
#include <sim/sim_model_kibis.h> #include <sim/sim_model_kibis.h>
#include <sim/sim_library_kibis.h> #include <sim/sim_library_kibis.h>
#include <paths.h> #include <common.h>
#include <fmt/core.h> #include <fmt/core.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <kiway.h> #include <kiway.h>
@ -58,22 +58,20 @@ std::vector<std::string> SPICE_GENERATOR_KIBIS::CurrentNames( const SPICE_ITEM&
} }
std::string SPICE_GENERATOR_KIBIS::IbisDevice( const SPICE_ITEM& aItem, const std::string aCwd, std::string SPICE_GENERATOR_KIBIS::IbisDevice( const SPICE_ITEM& aItem, const PROJECT& aProject,
const std::string aCacheDir ) const const wxString& aCacheDir ) const
{ {
std::string ibisLibFilename = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY::LIBRARY_FIELD ); std::string ibisLibFilename = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY::LIBRARY_FIELD );
std::string ibisCompName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY::NAME_FIELD ); std::string ibisCompName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY::NAME_FIELD );
std::string ibisPinName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::PIN_FIELD ); std::string ibisPinName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::PIN_FIELD );
std::string ibisModelName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::MODEL_FIELD ); std::string ibisModelName = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::MODEL_FIELD );
bool diffMode = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::DIFF_FIELD ) == "1"; bool diffMode = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_LIBRARY_KIBIS::DIFF_FIELD ) == "1";
wxFileName libPath = wxFileName( wxString( ibisLibFilename ) ); wxString path = ExpandEnvVarSubstitutions( ibisLibFilename, &aProject );
wxString absolutePath = aProject.AbsolutePath( path );
if( !libPath.IsAbsolute() ) KIBIS kibis( std::string( absolutePath.c_str() ) );
libPath.MakeAbsolute( aCwd ); kibis.m_cacheDir = std::string( aCacheDir.c_str() );
KIBIS kibis( std::string( libPath.GetFullPath().c_str() ) );
kibis.m_cacheDir = aCacheDir;
if( !kibis.m_valid ) if( !kibis.m_valid )
{ {

View File

@ -41,8 +41,8 @@ public:
std::string ModelLine( const SPICE_ITEM& aItem ) const override; std::string ModelLine( const SPICE_ITEM& aItem ) const override;
std::vector<std::string> CurrentNames( const SPICE_ITEM& aItem ) const override; std::vector<std::string> CurrentNames( const SPICE_ITEM& aItem ) const override;
std::string IbisDevice( const SPICE_ITEM& aItem, const std::string aCwd, std::string IbisDevice( const SPICE_ITEM& aItem, const PROJECT& aProject,
const std::string aCacheDir ) const; const wxString& aCacheDir ) const;
protected: protected:
std::vector<std::reference_wrapper<const SIM_MODEL::PARAM>> GetInstanceParams() const override; std::vector<std::reference_wrapper<const SIM_MODEL::PARAM>> GetInstanceParams() const override;

View File

@ -76,7 +76,7 @@ bool EnsureFileDirectoryExists( wxFileName* aTargetFullFileName,
* @param aString a string containing (perhaps) references to env var * @param aString a string containing (perhaps) references to env var
* @return the expanded environment variable. * @return the expanded environment variable.
*/ */
const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aProject ); const wxString ExpandEnvVarSubstitutions( const wxString& aString, const PROJECT* aProject );
/** /**
* Expand '${var-name}' templates in text. The LocalResolver is given first crack at it, * Expand '${var-name}' templates in text. The LocalResolver is given first crack at it,