24#include <magic_enum.hpp>
25#include <unordered_set>
59 std::vector<LIBRARY_TABLE_TYPE> aTablesToLoad )
73 wxCHECK_MSG(
false,
m_tables,
"Invalid scope passed to loadTables" );
77 std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>& aTarget = getTarget();
79 if( aTablesToLoad.size() == 0 )
84 aTarget.erase( type );
89 wxFileName fn( aTablePath, fileName );
91 if( fn.IsFileReadable() )
93 auto table = std::make_unique<LIBRARY_TABLE>( fn, aScope );
94 wxCHECK2(
table->Type() == type,
continue );
95 aTarget[type] = std::move(
table );
100 wxLogTrace(
traceLibraries,
"No library table found at %s", fn.GetFullPath() );
108 std::unordered_set<wxString> seenTables;
113 seenTables.insert( aTable.Path() );
122 wxFileName file( row.URI() );
125 file.MakeAbsolute( wxFileName( aTable.Path() ).GetPath() );
128 wxString src = file.GetFullPath();
130 if( seenTables.contains( src ) )
132 wxLogTrace(
traceLibraries,
"Library table %s has already been loaded!",
135 row.SetErrorDescription(
136 _(
"A reference to this library table already exists" ) );
140 auto child = std::make_unique<LIBRARY_TABLE>( file, aRootTable.
Scope() );
142 processOneTable( *child );
147 row.SetErrorDescription( child->ErrorDescription() );
155 processOneTable( aRootTable );
168 wxCHECK(
false, wxEmptyString );
178 wxCHECK( !
m_tables.contains( aType ), );
181 m_tables[aType] = std::make_unique<LIBRARY_TABLE>( fn.GetFullPath(),
190 m_projectTables[aType] = std::make_unique<LIBRARY_TABLE>( fn.GetFullPath(),
200 const wxString& aPrefix ) :
206 wxFileName f( aBasePath,
"" );
210 .value_or(
nullptr );
212 .value_or(
nullptr );
215 .value_or(
nullptr );
219 wxDirTraverseResult
OnFile(
const wxString& aFilePath )
override
221 wxFileName file = wxFileName::FileName( aFilePath );
225 if( file.GetExt() == wxT(
"kicad_sym" ) && file.GetDirCount() >=
m_prefix_dir_count + 2
231 return wxDIR_CONTINUE;
235 wxDirTraverseResult
OnDir(
const wxString& dirPath )
override
237 static wxString designBlockExt = wxString::Format( wxS(
".%s" ),
239 wxFileName dir = wxFileName::DirName( dirPath );
243 if( dirPath.EndsWith( wxS(
".pretty" ) ) && dir.GetDirCount() >=
m_prefix_dir_count + 3
248 else if( dirPath.EndsWith( designBlockExt )
255 return wxDIR_CONTINUE;
263 if( aTable->
HasRow( aNickname ) )
269 aNickname = wxString::Format(
"%s%s_%d",
m_lib_prefix, aBaseName, increment );
271 }
while( aTable->
HasRow( aNickname ) );
282 int aExtensionLength )
284 wxString versionedPath = wxString::Format( wxS(
"${%s}" ),
287 wxArrayString parts = aSource.GetDirs();
289 parts.Insert( versionedPath, 0 );
292 parts.Add( aSource.GetFullName() );
294 wxString libPath = wxJoin( parts,
'/' );
298 wxString
name = parts.Last().substr( 0, parts.Last().length() - aExtensionLength );
303 wxLogTrace(
traceLibraries,
"Manager: Adding PCM lib '%s' as '%s'", libPath, nickname );
315 wxLogTrace(
traceLibraries,
"Manager: Not adding existing PCM lib '%s'", libPath );
340 return fn.GetFullPath();
346 if( wxFileName fn( aPath ); fn.IsFileReadable() )
364 std::vector<LIBRARY_TABLE_TYPE> invalidTables;
370 invalidTables.emplace_back( type );
373 return invalidTables;
383 table.SetType( aType );
384 table.Rows().clear();
388 if( !defaultLib.IsFileReadable() )
390 wxLogTrace(
traceLibraries,
"Warning: couldn't read default library table for %s at '%s'",
391 magic_enum::enum_name( aType ), defaultLib.GetFullPath() );
394 if( aPopulateDefaultLibraries )
400 chained.
SetURI( defaultLib.GetFullPath() );
406 table.Format( &formatter );
421 for(
const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter :
m_adapters | std::views::values )
422 adapter->GlobalTablesChanged( aTablesToLoad );
429 wxCHECK( settings, );
437 wxFileName d( *packagesPath,
"" );
442 wxDir dir( d.GetPath() );
444 dir.Traverse( traverser );
451 wxLogTrace(
traceLibraries,
"Warning: save failed after PCM auto-add: %s",
458 auto cleanupRemovedPCMLibraries =
464 auto toErase = std::ranges::remove_if(
table->Rows(),
467 wxString path = GetFullURI( &aRow, true );
468 return path.StartsWith( *packagesPath ) && !wxFile::Exists( path );
471 table->Rows().erase( toErase.begin(), toErase.end() );
473 if( !toErase.empty() )
479 "Warning: save failed after PCM auto-remove: %s",
498 for(
const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter :
m_adapters | std::views::values )
499 adapter->ProjectChanged();
504 std::unique_ptr<LIBRARY_MANAGER_ADAPTER>&& aAdapter )
506 wxCHECK_MSG( !
m_adapters.contains( aType ), ,
"You should only register an adapter once!" );
528 wxCHECK_MSG(
false, std::nullopt,
"Table() requires a single scope" );
535 magic_enum::enum_name( aType ) );
563 bool aIncludeInvalid )
const
565 std::map<wxString, LIBRARY_TABLE_ROW*> rows;
566 std::vector<wxString> rowOrder;
568 std::list<std::ranges::ref_view<
569 const std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>
575 tables = { std::views::all(
m_tables ) };
590 std::function<void(
const std::unique_ptr<LIBRARY_TABLE>&)> processTable =
591 [&](
const std::unique_ptr<LIBRARY_TABLE>& aTable )
593 if( aTable->Type() != aType )
596 if( aTable->IsOk() || aIncludeInvalid )
600 if( row.IsOk() || aIncludeInvalid )
611 if( !rows.contains( row.Nickname() ) )
612 rowOrder.emplace_back( row.Nickname() );
614 rows[ row.Nickname() ] = &row;
621 for(
const std::unique_ptr<LIBRARY_TABLE>&
table :
622 std::views::join( tables ) | std::views::values )
624 processTable(
table );
627 std::vector<LIBRARY_TABLE_ROW*> ret;
629 for(
const wxString& row : rowOrder )
630 ret.emplace_back( rows[row] );
637 const wxString& aNickname,
642 if( row->Nickname() == aNickname )
651 const wxString& aUri,
666 if( wxFileName::IsDirReadable( aProjectPath ) )
674 "New project path %s is not readable, not loading project tables",
682 wxCHECK( aTable, tl::unexpected(
LIBRARY_ERROR(
"Internal error" ) ) );
685 for(
const std::unique_ptr<LIBRARY_TABLE>& t :
m_tables | std::views::values )
687 if( t.get() == aTable )
690 wxFileName fn( aTable->
Path() );
697 aTable->
Format( &formatter );
716 const wxString& aNickname,
717 bool aSubstituted )
const
719 if( std::optional<const LIBRARY_TABLE_ROW*>
result =
GetRow( aType, aNickname ) )
740 return path.GetFullPath();
747 if( aURI1.Find(
"://" ) != wxNOT_FOUND )
750 return aURI1 == aURI2;
754 const wxFileName fn1( aURI1 );
755 const wxFileName fn2( aURI2 );
802 bool me = aChangedTables.size() == 0;
844 return row->Nickname();
853 std::vector<wxString> ret;
858 ret.emplace_back( row->Nickname() );
866 bool aCheckEnabled )
const
868 if( std::optional<const LIB_DATA*> r =
fetchIfLoaded( aNickname ); r.has_value() )
869 return !aCheckEnabled || !( *r )->row->Disabled();
898 if( std::optional<const LIB_DATA*> optRow =
fetchIfLoaded( aNickname ); optRow )
899 return ( *optRow )->row->Description();
920 const wxString& aUri,
956 for(
const std::future<void>& future :
m_futures )
983 const wxString& aNickname )
const
1002 const wxString& aNickname )
1013 return std::nullopt;
1019 auto tryLoadFromScope =
1023 bool present =
false;
1026 std::lock_guard lock( aMutex );
1027 present = aTarget.contains( aNickname ) && aTarget.at( aNickname ).plugin;
1035 wxLogTrace(
traceLibraries,
"Library %s (%s) not yet loaded, will attempt...",
1036 aNickname, magic_enum::enum_name( aScope ) );
1040 std::lock_guard lock( aMutex );
1043 aTarget[ row->
Nickname() ].row = row;
1044 aTarget[ row->
Nickname() ].plugin.reset( *plugin );
1046 return &aTarget.at( aNickname );
1050 return tl::unexpected( plugin.error() );
1057 return &aTarget.at( aNickname );
1071 wxString msg = wxString::Format(
_(
"Library %s not found" ), aNickname );
virtual bool DeleteLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Delete an existing library and returns true, or if library does not exist returns false,...
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
virtual ~LIBRARY_MANAGER_ADAPTER()
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
LIBRARY_TABLE * GlobalTable() const
Retrieves the global library table for this adapter type.
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
bool IsLibraryLoaded(const wxString &aNickname)
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Like LIBRARY_MANAGER::Rows but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< LIBRARY_TABLE_ROW * > FindRowByURI(const wxString &aUri, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::FindRowByURI but filtered to the LIBRARY_TABLE_TYPE of this adapter.
virtual std::map< wxString, LIB_DATA > & globalLibs()=0
virtual std::mutex & globalLibsMutex()=0
virtual LIBRARY_TABLE_TYPE Type() const =0
The type of library table this adapter works with.
bool DeleteLibrary(const wxString &aNickname)
Deletes the given library from disk if it exists; returns true if deleted.
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
std::optional< wxString > FindLibraryByURI(const wxString &aURI) const
LIBRARY_MANAGER & Manager() const
void abortLoad()
Aborts any async load in progress; blocks until fully done aborting.
std::optional< wxString > GetLibraryDescription(const wxString &aNickname) const
virtual LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row)=0
Creates a concrete plugin for the given row.
void GlobalTablesChanged(std::initializer_list< LIBRARY_TABLE_TYPE > aChangedTables={})
Notify the adapter that the global library tables have changed.
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::map< wxString, LIB_DATA > m_libraries
std::vector< wxString > GetLibraryNames() const
Returns a list of library nicknames that are available (skips any that failed to load)
virtual void ProjectChanged()
Notify the adapter that the active project has changed.
std::vector< std::future< void > > m_futures
static wxString getUri(const LIBRARY_TABLE_ROW *aRow)
std::mutex m_libraries_mutex
std::atomic< size_t > m_loadCount
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
static wxString ExpandURI(const wxString &aShortURI, const PROJECT &aProject)
std::optional< LIBRARY_MANAGER_ADAPTER * > Adapter(LIBRARY_TABLE_TYPE aType) const
std::map< wxString, std::unique_ptr< LIBRARY_TABLE > > m_childTables
Map of full URI to table object for tables that are referenced by global or project tables.
void loadNestedTables(LIBRARY_TABLE &aTable)
LIBRARY_RESULT< void > Save(LIBRARY_TABLE *aTable) const
static const std::map< LIBRARY_TABLE_TYPE, const std::string & > m_typeToFilenameMap
static bool UrisAreEquivalent(const wxString &aURI1, const wxString &aURI2)
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
void RegisterAdapter(LIBRARY_TABLE_TYPE aType, std::unique_ptr< LIBRARY_MANAGER_ADAPTER > &&aAdapter)
static wxString DefaultGlobalTablePath(LIBRARY_TABLE_TYPE aType)
static std::vector< LIBRARY_TABLE_TYPE > InvalidGlobalTables()
void createEmptyTable(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
static bool GlobalTablesValid()
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_TABLE > > m_projectTables
void LoadProjectTables(const wxString &aProjectPath)
std::optional< LIBRARY_TABLE_ROW * > FindRowByURI(LIBRARY_TABLE_TYPE aType, const wxString &aUri, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
static bool CreateGlobalTable(LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries)
void loadTables(const wxString &aTablePath, LIBRARY_TABLE_SCOPE aScope, std::vector< LIBRARY_TABLE_TYPE > aTablesToLoad={})
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
static wxString tableFileName(LIBRARY_TABLE_TYPE aType)
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Returns a flattened list of libraries of the given type.
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_TABLE > > m_tables
void ProjectChanged()
Notify all adapters that the project has changed.
static bool IsTableValid(const wxString &aPath)
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_MANAGER_ADAPTER > > m_adapters
std::optional< LIBRARY_TABLE_ROW * > GetRow(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
std::map< std::string, UTF8 > GetOptionsMap() const
void SetDescription(const wxString &aDescription)
static const wxString TABLE_TYPE_NAME
void SetURI(const wxString &aUri)
const wxString & URI() const
const wxString & Nickname() const
void Format(OUTPUTFORMATTER *aOutput) const
LIBRARY_TABLE_ROW & InsertRow()
Builds a new row and inserts it at the end of the table; returning a reference to the row.
const wxString & Path() const
LIBRARY_TABLE_SCOPE Scope() const
bool HasRow(const wxString &aNickname) const
bool HasRowWithURI(const wxString &aUri, const PROJECT &aProject, bool aSubstituted=false) const
Returns true if the given (fully-expanded) URI exists as a library in this table.
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
LIBRARY_TABLE * m_designBlockTable
std::set< LIBRARY_TABLE * > m_modified
LIBRARY_MANAGER & m_manager
std::set< LIBRARY_TABLE * > Modified() const
const PROJECT & m_project
PCM_LIB_TRAVERSER(const wxString &aBasePath, LIBRARY_MANAGER &aManager, const wxString &aPrefix)
void ensureUnique(LIBRARY_TABLE *aTable, const wxString &aBaseName, wxString &aNickname) const
LIBRARY_TABLE * m_symbolTable
wxDirTraverseResult OnDir(const wxString &dirPath) override
Handles footprint library and design block library directories, minimum nest level 3.
void addRowIfNecessary(LIBRARY_TABLE *aTable, const wxFileName &aSource, ADD_MODE aMode, int aExtensionLength)
LIBRARY_TABLE * m_fpTable
wxDirTraverseResult OnFile(const wxString &aFilePath) override
Handles symbol library files, minimum nest level 2.
size_t m_prefix_dir_count
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
virtual LIBRARY_MANAGER & GetLibraryManager() const
Container for project specific data.
T * GetAppSettings(const char *aFilename)
Return a handle to the a given settings by type.
static void ResolvePossibleSymlinks(wxFileName &aFilename)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Functions related to environment variables, including help functions.
static const std::string KiCadDesignBlockLibPathExtension
static const std::string SymbolLibraryTableFileName
static const std::string DesignBlockLibraryTableFileName
static const std::string FootprintLibraryTableFileName
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
KICOMMON_API std::optional< wxString > GetVersionedEnvVarValue(const std::map< wxString, ENV_VAR_ITEM > &aMap, const wxString &aBaseName)
Attempt to retrieve the value of a versioned environment variable, such as KICAD8_TEMPLATE_DIR.
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global program "get" accessor.
std::vector< LIBRARY_TABLE > tables
Storage for an actual loaded library (including library content owned by the plugin)
std::unique_ptr< IO_BASE > plugin
const LIBRARY_TABLE_ROW * row
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().