KiCad PCB EDA Suite
Loading...
Searching...
No Matches
library_manager.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <chrono>
22#include <common.h>
23#include <env_vars.h>
24#include <list>
25#include <magic_enum.hpp>
26#include <thread_pool.h>
27#include <ranges>
28#include <unordered_set>
29
30#include <paths.h>
31#include <pgm_base.h>
32#include <richio.h>
33#include <trace_helpers.h>
35
37
38using namespace std::chrono_literals;
41#include <wx/dir.h>
42#include <wx/log.h>
43
45{
46 std::vector<LIBRARY_TABLE> tables;
47};
48
49
53
54
56
57
58void LIBRARY_MANAGER::loadTables( const wxString& aTablePath, LIBRARY_TABLE_SCOPE aScope,
59 std::vector<LIBRARY_TABLE_TYPE> aTablesToLoad )
60{
61 auto getTarget =
62 [&]() -> std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>&
63 {
64 switch( aScope )
65 {
67 return m_tables;
68
70 return m_projectTables;
71
72 default:
73 wxCHECK_MSG( false, m_tables, "Invalid scope passed to loadTables" );
74 }
75 };
76
77 std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>& aTarget = getTarget();
78
79 if( aTablesToLoad.size() == 0 )
81
82 for( LIBRARY_TABLE_TYPE type : aTablesToLoad )
83 {
84 aTarget.erase( type );
85
86 wxFileName fn( aTablePath, tableFileName( type ) );
87
88 if( fn.IsFileReadable() )
89 {
90 std::unique_ptr<LIBRARY_TABLE> table = std::make_unique<LIBRARY_TABLE>( fn, aScope );
91
92 if( table->Type() != type )
93 {
94 auto actualName = magic_enum::enum_name( table->Type() );
95 auto expectedName = magic_enum::enum_name( type );
96 wxLogWarning( wxS( "Library table '%s' has type %s but expected %s; skipping" ),
97 fn.GetFullPath(),
98 wxString( actualName.data(), actualName.size() ),
99 wxString( expectedName.data(), expectedName.size() ) );
100 continue;
101 }
102
103 aTarget[type] = std::move( table );
104 loadNestedTables( *aTarget[type] );
105 }
106 else
107 {
108 wxLogTrace( traceLibraries, "No library table found at %s", fn.GetFullPath() );
109 }
110 }
111}
112
113
115{
116 std::unordered_set<wxString> seenTables;
117
118 std::function<void( LIBRARY_TABLE& )> processOneTable =
119 [&]( LIBRARY_TABLE& aTable )
120 {
121 seenTables.insert( aTable.Path() );
122
123 if( !aTable.IsOk() )
124 return;
125
126 for( LIBRARY_TABLE_ROW& row : aTable.Rows() )
127 {
128 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
129 {
130 wxFileName file( ExpandURI( row.URI(), Pgm().GetSettingsManager().Prj() ) );
131
132 // URI may be relative to parent
133 file.MakeAbsolute( wxFileName( aTable.Path() ).GetPath() );
134
136 wxString src = file.GetFullPath();
137
138 if( seenTables.contains( src ) )
139 {
140 wxLogTrace( traceLibraries, "Library table %s has already been loaded!", src );
141 row.SetOk( false );
142 row.SetErrorDescription( _( "A reference to this library table already exists" ) );
143 continue;
144 }
145
146 auto child = std::make_unique<LIBRARY_TABLE>( file, aRootTable.Scope() );
147
148 processOneTable( *child );
149
150 if( !child->IsOk() )
151 {
152 row.SetOk( false );
153 row.SetErrorDescription( child->ErrorDescription() );
154 }
155
156 m_childTables.insert( { row.URI(), std::move( child ) } );
157 }
158 }
159 };
160
161 processOneTable( aRootTable );
162}
163
164
166{
167 switch( aType )
168 {
172 default: wxCHECK( false, wxEmptyString );
173 }
174}
175
176
178{
179 if( aScope == LIBRARY_TABLE_SCOPE::GLOBAL )
180 {
181 wxCHECK( !m_tables.contains( aType ), /* void */ );
182 wxFileName fn( PATHS::GetUserSettingsPath(), tableFileName( aType ) );
183
184 m_tables[aType] = std::make_unique<LIBRARY_TABLE>( fn, LIBRARY_TABLE_SCOPE::GLOBAL );
185 m_tables[aType]->SetType( aType );
186 }
187 else if( aScope == LIBRARY_TABLE_SCOPE::PROJECT )
188 {
189 wxCHECK( !m_projectTables.contains( aType ), /* void */ );
190 wxFileName fn( Pgm().GetSettingsManager().Prj().GetProjectDirectory(), tableFileName( aType ) );
191
192 m_projectTables[aType] = std::make_unique<LIBRARY_TABLE>( fn, LIBRARY_TABLE_SCOPE::PROJECT );
193 m_projectTables[aType]->SetType( aType );
194 }
195}
196
197
198class PCM_LIB_TRAVERSER final : public wxDirTraverser
199{
200public:
201 explicit PCM_LIB_TRAVERSER( const wxString& aBasePath, LIBRARY_MANAGER& aManager,
202 const wxString& aPrefix ) :
203 m_manager( aManager ),
205 m_path_prefix( aBasePath ),
206 m_lib_prefix( aPrefix )
207 {
208 wxFileName f( aBasePath, "" );
209 m_prefix_dir_count = f.GetDirCount();
210
214 .value_or( nullptr );
215 }
216
218 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
219 {
220 wxFileName file = wxFileName::FileName( aFilePath );
221
222 // consider a file to be a lib if it's name ends with .kicad_sym and
223 // it is under $KICADn_3RD_PARTY/symbols/<pkgid>/ i.e. has nested level of at least +2
224 if( file.GetExt() == wxT( "kicad_sym" )
225 && file.GetDirCount() >= m_prefix_dir_count + 2
226 && file.GetDirs()[m_prefix_dir_count] == wxT( "symbols" ) )
227 {
229 }
230
231 return wxDIR_CONTINUE;
232 }
233
235 wxDirTraverseResult OnDir( const wxString& dirPath ) override
236 {
237 static wxString designBlockExt = wxString::Format( wxS( ".%s" ), FILEEXT::KiCadDesignBlockLibPathExtension );
238 wxFileName dir = wxFileName::DirName( dirPath );
239
240 // consider a directory to be a lib if it's name ends with .pretty and
241 // it is under $KICADn_3RD_PARTY/footprints/<pkgid>/ i.e. has nested level of at least +3
242 if( dirPath.EndsWith( wxS( ".pretty" ) )
243 && dir.GetDirCount() >= m_prefix_dir_count + 3
244 && dir.GetDirs()[m_prefix_dir_count] == wxT( "footprints" ) )
245 {
247 }
248 else if( dirPath.EndsWith( designBlockExt )
249 && dir.GetDirCount() >= m_prefix_dir_count + 3
250 && dir.GetDirs()[m_prefix_dir_count] == wxT( "design_blocks" ) )
251 {
252 addRowIfNecessary( m_designBlockTable, dir, ADD_MODE::AM_DIRECTORY, designBlockExt.Len() );
253 }
254
255 return wxDIR_CONTINUE;
256 }
257
258 std::set<LIBRARY_TABLE*> Modified() const { return m_modified; }
259
260private:
261 void ensureUnique( LIBRARY_TABLE* aTable, const wxString& aBaseName, wxString& aNickname ) const
262 {
263 if( aTable->HasRow( aNickname ) )
264 {
265 int increment = 1;
266
267 do
268 {
269 aNickname = wxString::Format( "%s%s_%d", m_lib_prefix, aBaseName, increment );
270 increment++;
271 } while( aTable->HasRow( aNickname ) );
272 }
273 }
274
275 enum class ADD_MODE
276 {
279 };
280
281 void addRowIfNecessary( LIBRARY_TABLE* aTable, const wxFileName& aSource, ADD_MODE aMode,
282 int aExtensionLength )
283 {
284 wxString versionedPath = wxString::Format( wxS( "${%s}" ),
285 ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
286
287 wxArrayString parts = aSource.GetDirs();
288 parts.RemoveAt( 0, m_prefix_dir_count );
289 parts.Insert( versionedPath, 0 );
290
291 if( aMode == ADD_MODE::AM_FILE )
292 parts.Add( aSource.GetFullName() );
293
294 wxString libPath = wxJoin( parts, '/' );
295
296 if( !aTable->HasRowWithURI( libPath, m_project ) )
297 {
298 wxString name = parts.Last().substr( 0, parts.Last().length() - aExtensionLength );
299 wxString nickname = wxString::Format( "%s%s", m_lib_prefix, name );
300
301 ensureUnique( aTable, name, nickname );
302
303 wxLogTrace( traceLibraries, "Manager: Adding PCM lib '%s' as '%s'", libPath, nickname );
304
305 LIBRARY_TABLE_ROW& row = aTable->InsertRow();
306
307 row.SetNickname( nickname );
308 row.SetURI( libPath );
309 row.SetType( wxT( "KiCad" ) );
310 row.SetDescription( _( "Added by Plugin and Content Manager" ) );
311 m_modified.insert( aTable );
312 }
313 else
314 {
315 wxLogTrace( traceLibraries, "Manager: Not adding existing PCM lib '%s'", libPath );
316 }
317 }
318
319private:
323 wxString m_lib_prefix;
325 std::set<LIBRARY_TABLE*> m_modified;
326
330};
331
332
334{
335 wxString basePath = PATHS::GetUserSettingsPath();
336
337 wxFileName fn( basePath, tableFileName( aType ) );
338 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
339
340 return fn.GetFullPath();
341}
342
343
344bool LIBRARY_MANAGER::IsTableValid( const wxString& aPath )
345{
346 if( wxFileName fn( aPath ); fn.IsFileReadable() )
347 {
349
350 if( temp.IsOk() )
351 return true;
352 }
353
354 return false;
355}
356
357
359{
360 return InvalidGlobalTables().empty();
361}
362
363
364std::vector<LIBRARY_TABLE_TYPE> LIBRARY_MANAGER::InvalidGlobalTables()
365{
366 std::vector<LIBRARY_TABLE_TYPE> invalidTables;
367 wxString basePath = PATHS::GetUserSettingsPath();
368
372 {
373 wxFileName fn( basePath, tableFileName( tableType ) );
374
375 if( !IsTableValid( fn.GetFullPath() ) )
376 invalidTables.emplace_back( tableType );
377 }
378
379 return invalidTables;
380}
381
382
383bool LIBRARY_MANAGER::CreateGlobalTable( LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries )
384{
385 wxFileName fn( DefaultGlobalTablePath( aType ) );
386
388 table.SetType( aType );
389 table.Rows().clear();
390
391 wxFileName defaultLib( PATHS::GetStockTemplatesPath(), tableFileName( aType ) );
392
393 if( !defaultLib.IsFileReadable() )
394 {
395 wxLogTrace( traceLibraries, "Warning: couldn't read default library table for %s at '%s'",
396 magic_enum::enum_name( aType ), defaultLib.GetFullPath() );
397 }
398
399 if( aPopulateDefaultLibraries )
400 {
401 LIBRARY_TABLE_ROW& chained = table.InsertRow();
403 chained.SetNickname( wxT( "KiCad" ) );
404 chained.SetDescription( _( "KiCad Default Libraries" ) );
405 chained.SetURI( defaultLib.GetFullPath() );
406 }
407
408 try
409 {
410 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fn.GetFullPath(), KICAD_FORMAT::FORMAT_MODE::LIBRARY_TABLE );
411 table.Format( &formatter );
412 }
413 catch( IO_ERROR& e )
414 {
415 wxLogTrace( traceLibraries, "Exception while saving: %s", e.What() );
416 return false;
417 }
418
419 return true;
420}
421
422
423void LIBRARY_MANAGER::LoadGlobalTables( std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad )
424{
425 // Cancel any in-progress load
426 {
427 std::scoped_lock lock( m_adaptersMutex );
428
429 for( const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter : m_adapters | std::views::values )
430 adapter->GlobalTablesChanged( aTablesToLoad );
431 }
432
434
436 KICAD_SETTINGS* settings = mgr.GetAppSettings<KICAD_SETTINGS>( "kicad" );
437
438 wxCHECK( settings, /* void */ );
439
440 const ENV_VAR_MAP& vars = Pgm().GetLocalEnvVariables();
441 std::optional<wxString> packagesPath = ENV_VAR::GetVersionedEnvVarValue( vars, wxT( "3RD_PARTY" ) );
442
443 if( packagesPath && settings->m_PcmLibAutoAdd )
444 {
445 // Scan for libraries in PCM packages directory
446 wxFileName d( *packagesPath, "" );
447
448 if( d.DirExists() )
449 {
450 PCM_LIB_TRAVERSER traverser( *packagesPath, *this, settings->m_PcmLibPrefix );
451 wxDir dir( d.GetPath() );
452
453 dir.Traverse( traverser );
454
455 for( LIBRARY_TABLE* table : traverser.Modified() )
456 {
457 table->Save().map_error(
458 []( const LIBRARY_ERROR& aError )
459 {
460 wxLogTrace( traceLibraries, wxT( "Warning: save failed after PCM auto-add: %s" ),
461 aError.message );
462 } );
463 }
464 }
465 }
466
467 auto cleanupRemovedPCMLibraries =
468 [&]( LIBRARY_TABLE_TYPE aType )
469 {
470 LIBRARY_TABLE* table = Table( aType, LIBRARY_TABLE_SCOPE::GLOBAL ).value_or( nullptr );
471 wxCHECK( table, /* void */ );
472
473 auto toErase = std::ranges::remove_if( table->Rows(),
474 [&]( const LIBRARY_TABLE_ROW& aRow )
475 {
476 wxString path = GetFullURI( &aRow, true );
477 return path.StartsWith( *packagesPath ) && !wxFile::Exists( path );
478 } );
479
480 table->Rows().erase( toErase.begin(), toErase.end() );
481
482 if( !toErase.empty() )
483 {
484 table->Save().map_error(
485 []( const LIBRARY_ERROR& aError )
486 {
487 wxLogTrace( traceLibraries, wxT( "Warning: save failed after PCM auto-remove: %s" ),
488 aError.message );
489 } );
490 }
491 };
492
493 if( packagesPath && settings->m_PcmLibAutoRemove )
494 {
495 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::SYMBOL );
496 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::FOOTPRINT );
497 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::DESIGN_BLOCK );
498 }
499}
500
501
502void LIBRARY_MANAGER::LoadProjectTables( std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad )
503{
504 LoadProjectTables( Pgm().GetSettingsManager().Prj().GetProjectDirectory(), aTablesToLoad );
505}
506
507
509{
510 LoadProjectTables( Pgm().GetSettingsManager().Prj().GetProjectDirectory() );
511
512 std::scoped_lock lock( m_adaptersMutex );
513
514 for( const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter : m_adapters | std::views::values )
515 adapter->ProjectChanged();
516}
517
518
520{
521 std::scoped_lock lock( m_adaptersMutex );
522
523 for( const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter : m_adapters | std::views::values )
524 adapter->AbortAsyncLoad();
525}
526
527
529 std::unique_ptr<LIBRARY_MANAGER_ADAPTER>&& aAdapter )
530{
531 std::scoped_lock lock( m_adaptersMutex );
532
533 wxCHECK_MSG( !m_adapters.contains( aType ), /**/, "You should only register an adapter once!" );
534
535 m_adapters[aType] = std::move( aAdapter );
536}
537
538
540{
541 std::scoped_lock lock( m_adaptersMutex );
542 if( !m_adapters.contains( aType ) )
543 return false;
544
545 if( m_adapters[aType].get() != aAdapter )
546 return false;
547
548 m_adapters.erase( aType );
549 return true;
550}
551
552
553std::optional<LIBRARY_MANAGER_ADAPTER*> LIBRARY_MANAGER::Adapter( LIBRARY_TABLE_TYPE aType ) const
554{
555 std::scoped_lock lock( m_adaptersMutex );
556
557 if( m_adapters.contains( aType ) )
558 return m_adapters.at( aType ).get();
559
560 return std::nullopt;
561}
562
563
564std::optional<LIBRARY_TABLE*> LIBRARY_MANAGER::Table( LIBRARY_TABLE_TYPE aType,
565 LIBRARY_TABLE_SCOPE aScope )
566{
567 switch( aScope )
568 {
571 wxCHECK_MSG( false, std::nullopt, "Table() requires a single scope" );
572
574 {
575 if( !m_tables.contains( aType ) )
576 {
577 wxLogTrace( traceLibraries, "WARNING: missing global table (%s)",
578 magic_enum::enum_name( aType ) );
579 return std::nullopt;
580 }
581
582 return m_tables.at( aType ).get();
583 }
584
586 {
587 // TODO: handle multiple projects
588 if( !m_projectTables.contains( aType ) )
589 {
590 if( !Pgm().GetSettingsManager().Prj().IsNullProject() )
592 else
593 return std::nullopt;
594 }
595
596 return m_projectTables.at( aType ).get();
597 }
598 }
599
600 return std::nullopt;
601}
602
603
604std::vector<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::Rows( LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope,
605 bool aIncludeInvalid ) const
606{
607 std::map<wxString, LIBRARY_TABLE_ROW*> rows;
608 std::vector<wxString> rowOrder;
609
610 std::list<std::ranges::ref_view<const std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>>> tables;
611
612 switch( aScope )
613 {
615 tables = { std::views::all( m_tables ) };
616 break;
617
619 tables = { std::views::all( m_projectTables ) };
620 break;
621
623 tables = { std::views::all( m_tables ), std::views::all( m_projectTables ) };
624 break;
625
627 wxFAIL;
628 }
629
630 std::function<void(const std::unique_ptr<LIBRARY_TABLE>&)> processTable =
631 [&]( const std::unique_ptr<LIBRARY_TABLE>& aTable )
632 {
633 if( aTable->Type() != aType )
634 return;
635
636 if( aTable->IsOk() || aIncludeInvalid )
637 {
638 for( LIBRARY_TABLE_ROW& row : aTable->Rows() )
639 {
640 if( row.IsOk() || aIncludeInvalid )
641 {
642 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
643 {
644 if( !m_childTables.contains( row.URI() ) )
645 continue;
646
647 // Don't include libraries from disabled or hidden nested tables
648 if( row.Disabled() || row.Hidden() )
649 continue;
650
651 processTable( m_childTables.at( row.URI() ) );
652 }
653 else
654 {
655 if( !rows.contains( row.Nickname() ) )
656 rowOrder.emplace_back( row.Nickname() );
657
658 rows[ row.Nickname() ] = &row;
659 }
660 }
661 }
662 }
663 };
664
665 for( const std::unique_ptr<LIBRARY_TABLE>& table :
666 std::views::join( tables ) | std::views::values )
667 {
668 processTable( table );
669 }
670
671 std::vector<LIBRARY_TABLE_ROW*> ret;
672
673 for( const wxString& row : rowOrder )
674 ret.emplace_back( rows[row] );
675
676 return ret;
677}
678
679
680std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::GetRow( LIBRARY_TABLE_TYPE aType, const wxString& aNickname,
681 LIBRARY_TABLE_SCOPE aScope ) const
682{
683 for( LIBRARY_TABLE_ROW* row : Rows( aType, aScope, true ) )
684 {
685 if( row->Nickname() == aNickname )
686 return row;
687 }
688
689 return std::nullopt;
690}
691
692
693std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::FindRowByURI( LIBRARY_TABLE_TYPE aType,
694 const wxString& aUri,
695 LIBRARY_TABLE_SCOPE aScope ) const
696{
697 for( LIBRARY_TABLE_ROW* row : Rows( aType, aScope, true ) )
698 {
699 if( UrisAreEquivalent( GetFullURI( row, true ), aUri ) )
700 return row;
701 }
702
703 return std::nullopt;
704}
705
706
707void LIBRARY_MANAGER::ReloadLibraryEntry( LIBRARY_TABLE_TYPE aType, const wxString& aNickname,
708 LIBRARY_TABLE_SCOPE aScope )
709{
710 if( std::optional<LIBRARY_MANAGER_ADAPTER*> adapter = Adapter( aType ); adapter )
711 ( *adapter )->ReloadLibraryEntry( aNickname, aScope );
712}
713
714
715void LIBRARY_MANAGER::LoadProjectTables( const wxString& aProjectPath,
716 std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad )
717{
718 if( wxFileName::IsDirReadable( aProjectPath ) )
719 {
720 loadTables( aProjectPath, LIBRARY_TABLE_SCOPE::PROJECT, aTablesToLoad );
721 }
722 else
723 {
724 m_projectTables.clear();
725 wxLogTrace( traceLibraries, "New project path %s is not readable, not loading project tables", aProjectPath );
726 }
727}
728
729
731 std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad )
732{
733 if( aScope == LIBRARY_TABLE_SCOPE::PROJECT )
734 LoadProjectTables( aTablesToLoad );
735 else
736 LoadGlobalTables( aTablesToLoad );
737}
738
739
740std::optional<wxString> LIBRARY_MANAGER::GetFullURI( LIBRARY_TABLE_TYPE aType, const wxString& aNickname,
741 bool aSubstituted ) const
742{
743 if( std::optional<const LIBRARY_TABLE_ROW*> result = GetRow( aType, aNickname ) )
744 return GetFullURI( *result, aSubstituted );
745
746 return std::nullopt;
747}
748
749
750wxString LIBRARY_MANAGER::GetFullURI( const LIBRARY_TABLE_ROW* aRow, bool aSubstituted )
751{
752 if( aSubstituted )
753 return ExpandEnvVarSubstitutions( aRow->URI(), &Pgm().GetSettingsManager().Prj() );
754
755 return aRow->URI();
756}
757
758
759wxString LIBRARY_MANAGER::ExpandURI( const wxString& aShortURI, const PROJECT& aProject )
760{
761 wxLogNull doNotLog; // We do our own error reporting; we don't want to hear about missing envvars
762
763 wxFileName path( ExpandEnvVarSubstitutions( aShortURI, &aProject ) );
764 path.MakeAbsolute();
765 return path.GetFullPath();
766}
767
768
769bool LIBRARY_MANAGER::UrisAreEquivalent( const wxString& aURI1, const wxString& aURI2 )
770{
771 // Avoid comparing filenames as wxURIs
772 if( aURI1.Find( "://" ) != wxNOT_FOUND )
773 {
774 // found as full path
775 return aURI1 == aURI2;
776 }
777 else
778 {
779 const wxFileName fn1( aURI1 );
780 const wxFileName fn2( aURI2 );
781
782 // This will also test if the file is a symlink so if we are comparing
783 // a symlink to the same real file, the comparison will be true. See
784 // wxFileName::SameAs() in the wxWidgets source.
785
786 // found as full path and file name
787 return fn1 == fn2;
788 }
789}
790
791
795
796
801
802
806
807
812
813
815{
816 abortLoad();
817
818 {
819 std::unique_lock lock( m_librariesMutex );
820 m_libraries.clear();
821 }
822}
823
824
829
830
831void LIBRARY_MANAGER_ADAPTER::GlobalTablesChanged( std::initializer_list<LIBRARY_TABLE_TYPE> aChangedTables )
832{
833 bool me = aChangedTables.size() == 0;
834
835 for( LIBRARY_TABLE_TYPE type : aChangedTables )
836 {
837 if( type == Type() )
838 {
839 me = true;
840 break;
841 }
842 }
843
844 if( !me )
845 return;
846
847 abortLoad();
848
849 {
850 std::unique_lock lock( globalLibsMutex() );
851 globalLibs().clear();
852 }
853}
854
855
857{
858 abortLoad();
859
861
862 if( plugin.has_value() )
863 {
864 LIB_DATA lib;
865 lib.row = &aRow;
866 lib.plugin.reset( *plugin );
867
868 std::optional<LIB_STATUS> status = LoadOne( &lib );
869
870 if( status.has_value() )
871 {
872 aRow.SetOk( status.value().load_status == LOAD_STATUS::LOADED );
873
874 if( status.value().error.has_value() )
875 aRow.SetErrorDescription( status.value().error.value().message );
876 }
877 }
878 else if( plugin.error().message == LIBRARY_TABLE_OK().message )
879 {
880 aRow.SetOk( true );
881 aRow.SetErrorDescription( wxEmptyString );
882 }
883 else
884 {
885 aRow.SetOk( false );
886 aRow.SetErrorDescription( plugin.error().message );
887 }
888}
889
890
891
893{
894 wxCHECK( m_manager.Table( Type(), LIBRARY_TABLE_SCOPE::GLOBAL ), nullptr );
895 return *m_manager.Table( Type(), LIBRARY_TABLE_SCOPE::GLOBAL );
896}
897
898
899std::optional<LIBRARY_TABLE*> LIBRARY_MANAGER_ADAPTER::ProjectTable() const
900{
902}
903
904
905std::optional<wxString> LIBRARY_MANAGER_ADAPTER::FindLibraryByURI( const wxString& aURI ) const
906{
907 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
908 {
909 if( LIBRARY_MANAGER::UrisAreEquivalent( row->URI(), aURI ) )
910 return row->Nickname();
911 }
912
913 return std::nullopt;
914}
915
916
917std::vector<wxString> LIBRARY_MANAGER_ADAPTER::GetLibraryNames() const
918{
919 std::vector<wxString> ret;
920 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( Type() );
921
922 wxLogTrace( traceLibraries, "GetLibraryNames: checking %zu rows from table", rows.size() );
923
924 for( const LIBRARY_TABLE_ROW* row : rows )
925 {
926 wxString nickname = row->Nickname();
927 std::optional<const LIB_DATA*> loaded = fetchIfLoaded( nickname );
928
929 if( loaded )
930 ret.emplace_back( nickname );
931 }
932
933 wxLogTrace( traceLibraries, "GetLibraryNames: returning %zu of %zu libraries", ret.size(), rows.size() );
934 return ret;
935}
936
937
938bool LIBRARY_MANAGER_ADAPTER::HasLibrary( const wxString& aNickname, bool aCheckEnabled ) const
939{
940 std::optional<const LIB_DATA*> r = fetchIfLoaded( aNickname );
941
942 if( r.has_value() )
943 return !aCheckEnabled || !r.value()->row->Disabled();
944
945 return false;
946}
947
948
949bool LIBRARY_MANAGER_ADAPTER::DeleteLibrary( const wxString& aNickname )
950{
951 if( LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( aNickname ); result.has_value() )
952 {
953 LIB_DATA* data = *result;
954 std::map<std::string, UTF8> options = data->row->GetOptionsMap();
955
956 try
957 {
958 return data->plugin->DeleteLibrary( getUri( data->row ), &options );
959 }
960 catch( ... )
961 {
962 return false;
963 }
964 }
965
966 return false;
967}
968
969
970std::optional<wxString> LIBRARY_MANAGER_ADAPTER::GetLibraryDescription( const wxString& aNickname ) const
971{
972 if( std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname ); optRow )
973 return ( *optRow )->row->Description();
974
975 return std::nullopt;
976}
977
978
979std::vector<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER_ADAPTER::Rows( LIBRARY_TABLE_SCOPE aScope,
980 bool aIncludeInvalid ) const
981{
982 return m_manager.Rows( Type(), aScope, aIncludeInvalid );
983}
984
985
986std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER_ADAPTER::GetRow( const wxString &aNickname,
987 LIBRARY_TABLE_SCOPE aScope ) const
988{
989 return m_manager.GetRow( Type(), aNickname, aScope );
990}
991
992
993std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER_ADAPTER::FindRowByURI(
994 const wxString& aUri,
995 LIBRARY_TABLE_SCOPE aScope ) const
996{
997 return m_manager.FindRowByURI( Type(), aUri, aScope );
998}
999
1000
1002{
1003 {
1004 std::lock_guard lock( m_loadMutex );
1005
1006 if( m_futures.empty() )
1007 return;
1008
1009 wxLogTrace( traceLibraries, "Aborting library load..." );
1010 m_abort.store( true );
1011 }
1012
1014 wxLogTrace( traceLibraries, "Aborted" );
1015
1016 {
1017 std::lock_guard lock( m_loadMutex );
1018 m_abort.store( false );
1019 m_futures.clear();
1020 m_loadTotal.store( 0 );
1021 m_loadCount.store( 0 );
1022 }
1023}
1024
1025
1027{
1028 size_t total = m_loadTotal.load();
1029
1030 if( total == 0 )
1031 return std::nullopt;
1032
1033 size_t loaded = m_loadCount.load();
1034 return loaded / static_cast<float>( total );
1035}
1036
1037
1039{
1040 wxLogTrace( traceLibraries, "BlockUntilLoaded: entry, acquiring m_loadMutex" );
1041 std::unique_lock<std::mutex> asyncLock( m_loadMutex );
1042
1043 wxLogTrace( traceLibraries, "BlockUntilLoaded: waiting on %zu futures", m_futures.size() );
1044
1045 for( const std::future<void>& future : m_futures )
1046 future.wait();
1047
1048 wxLogTrace( traceLibraries, "BlockUntilLoaded: all futures complete, loadCount=%zu, loadTotal=%zu",
1049 m_loadCount.load(), m_loadTotal.load() );
1050}
1051
1052
1053bool LIBRARY_MANAGER_ADAPTER::IsLibraryLoaded( const wxString& aNickname )
1054{
1055 {
1056 std::shared_lock lock( m_librariesMutex );
1057
1058 if( auto it = m_libraries.find( aNickname ); it != m_libraries.end() )
1059 return it->second.status.load_status == LOAD_STATUS::LOADED;
1060 }
1061
1062 {
1063 std::shared_lock lock( globalLibsMutex() );
1064
1065 if( auto it = globalLibs().find( aNickname ); it != globalLibs().end() )
1066 return it->second.status.load_status == LOAD_STATUS::LOADED;
1067 }
1068
1069 return false;
1070}
1071
1072
1073std::optional<LIBRARY_ERROR> LIBRARY_MANAGER_ADAPTER::LibraryError( const wxString& aNickname ) const
1074{
1075 {
1076 std::shared_lock lock( m_librariesMutex );
1077
1078 if( auto it = m_libraries.find( aNickname ); it != m_libraries.end() )
1079 return it->second.status.error;
1080 }
1081
1082 {
1083 std::shared_lock lock( globalLibsMutex() );
1084
1085 if( auto it = globalLibs().find( aNickname ); it != globalLibs().end() )
1086 return it->second.status.error;
1087 }
1088
1089 return std::nullopt;
1090}
1091
1092
1093std::vector<std::pair<wxString, LIB_STATUS>> LIBRARY_MANAGER_ADAPTER::GetLibraryStatuses() const
1094{
1095 std::vector<std::pair<wxString, LIB_STATUS>> ret;
1096
1097 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
1098 {
1099 if( row->Disabled() )
1100 continue;
1101
1102 if( std::optional<LIB_STATUS> result = GetLibraryStatus( row->Nickname() ) )
1103 {
1104 ret.emplace_back( std::make_pair( row->Nickname(), *result ) );
1105 }
1106 else
1107 {
1108 // This should probably never happen, but until that can be proved...
1109 ret.emplace_back( std::make_pair( row->Nickname(),
1110 LIB_STATUS( {
1111 .load_status = LOAD_STATUS::LOAD_ERROR,
1112 .error = LIBRARY_ERROR( _( "Library not found in library table" ) )
1113 } ) ) );
1114 }
1115 }
1116
1117 return ret;
1118}
1119
1120
1122{
1123 wxString errors;
1124
1125 for( const auto& [nickname, status] : GetLibraryStatuses() )
1126 {
1127 if( status.load_status == LOAD_STATUS::LOAD_ERROR && status.error )
1128 {
1129 if( !errors.IsEmpty() )
1130 errors += wxS( "\n" );
1131
1132 errors += wxString::Format( _( "Library '%s': %s" ),
1133 nickname, status.error->message );
1134 }
1135 }
1136
1137 return errors;
1138}
1139
1140
1142{
1143 auto reloadScope =
1144 [&]( LIBRARY_TABLE_SCOPE aScopeToReload, std::map<wxString, LIB_DATA>& aTarget,
1145 std::shared_mutex& aMutex )
1146 {
1147 bool wasLoaded = false;
1148
1149 {
1150 std::unique_lock lock( aMutex );
1151 auto it = aTarget.find( aNickname );
1152
1153 if( it != aTarget.end() && it->second.plugin )
1154 {
1155 wasLoaded = true;
1156 aTarget.erase( it );
1157 }
1158 }
1159
1160 if( wasLoaded )
1161 {
1162 LIBRARY_RESULT<LIB_DATA*> result = loadFromScope( aNickname, aScopeToReload, aTarget, aMutex );
1163
1164 if( !result.has_value() )
1165 {
1166 wxLogTrace( traceLibraries, "ReloadLibraryEntry: failed to reload %s (%s): %s",
1167 aNickname, magic_enum::enum_name( aScopeToReload ),
1168 result.error().message );
1169 }
1170 }
1171 };
1172
1173 switch( aScope )
1174 {
1177 break;
1178
1181 break;
1182
1187 break;
1188 }
1189}
1190
1191
1192bool LIBRARY_MANAGER_ADAPTER::IsWritable( const wxString& aNickname ) const
1193{
1194 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
1195 {
1196 const LIB_DATA* rowData = *result;
1197 return rowData->plugin->IsLibraryWritable( getUri( rowData->row ) );
1198 }
1199
1200 return false;
1201}
1202
1203
1204bool LIBRARY_MANAGER_ADAPTER::CreateLibrary( const wxString& aNickname )
1205{
1206 if( LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( aNickname ); result.has_value() )
1207 {
1208 LIB_DATA* data = *result;
1209 std::map<std::string, UTF8> options = data->row->GetOptionsMap();
1210
1211 try
1212 {
1213 data->plugin->CreateLibrary( getUri( data->row ), &options );
1214 return true;
1215 }
1216 catch( ... )
1217 {
1218 return false;
1219 }
1220 }
1221
1222 return false;
1223}
1224
1225
1227{
1228 return LIBRARY_MANAGER::ExpandURI( aRow->URI(), Pgm().GetSettingsManager().Prj() );
1229}
1230
1231
1232std::optional<const LIB_DATA*> LIBRARY_MANAGER_ADAPTER::fetchIfLoaded( const wxString& aNickname ) const
1233{
1234 {
1235 std::shared_lock lock( m_librariesMutex );
1236
1237 if( auto it = m_libraries.find( aNickname ); it != m_libraries.end() )
1238 {
1239 if( it->second.status.load_status == LOAD_STATUS::LOADED )
1240 return &it->second;
1241
1242 return std::nullopt;
1243 }
1244 }
1245
1246 {
1247 std::shared_lock lock( globalLibsMutex() );
1248
1249 if( auto it = globalLibs().find( aNickname ); it != globalLibs().end() )
1250 {
1251 if( it->second.status.load_status == LOAD_STATUS::LOADED )
1252 return &it->second;
1253
1254 return std::nullopt;
1255 }
1256 }
1257
1258 return std::nullopt;
1259}
1260
1261
1262std::optional<LIB_DATA*> LIBRARY_MANAGER_ADAPTER::fetchIfLoaded( const wxString& aNickname )
1263{
1264 {
1265 std::shared_lock lock( m_librariesMutex );
1266
1267 if( auto it = m_libraries.find( aNickname ); it != m_libraries.end() )
1268 {
1269 if( it->second.status.load_status == LOAD_STATUS::LOADED )
1270 return &it->second;
1271
1272 return std::nullopt;
1273 }
1274 }
1275
1276 {
1277 std::shared_lock lock( globalLibsMutex() );
1278
1279 if( auto it = globalLibs().find( aNickname ); it != globalLibs().end() )
1280 {
1281 if( it->second.status.load_status == LOAD_STATUS::LOADED )
1282 return &it->second;
1283
1284 return std::nullopt;
1285 }
1286 }
1287
1288 return std::nullopt;
1289}
1290
1291
1293 LIBRARY_TABLE_SCOPE aScope,
1294 std::map<wxString, LIB_DATA>& aTarget,
1295 std::shared_mutex& aMutex )
1296{
1297 bool present = false;
1298
1299 {
1300 std::shared_lock lock( aMutex );
1301 present = aTarget.contains( aNickname ) && aTarget.at( aNickname ).plugin;
1302 }
1303
1304 if( !present )
1305 {
1306 if( auto result = m_manager.GetRow( Type(), aNickname, aScope ) )
1307 {
1308 const LIBRARY_TABLE_ROW* row = *result;
1309 wxLogTrace( traceLibraries, "Library %s (%s) not yet loaded, will attempt...",
1310 aNickname, magic_enum::enum_name( aScope ) );
1311
1312 if( LIBRARY_RESULT<IO_BASE*> plugin = createPlugin( row ); plugin.has_value() )
1313 {
1314 std::unique_lock lock( aMutex );
1315
1316 aTarget[ row->Nickname() ].status.load_status = LOAD_STATUS::LOADING;
1317 aTarget[ row->Nickname() ].row = row;
1318 aTarget[ row->Nickname() ].plugin.reset( *plugin );
1319
1320 return &aTarget.at( aNickname );
1321 }
1322 else
1323 {
1324 return tl::unexpected( plugin.error() );
1325 }
1326 }
1327
1328 return nullptr;
1329 }
1330
1331 std::shared_lock lock( aMutex );
1332 return &aTarget.at( aNickname );
1333}
1334
1335
1337{
1340
1341 if( !result.has_value() || *result )
1342 return result;
1343
1345
1346 if( !result.has_value() || *result )
1347 return result;
1348
1349 wxString msg = wxString::Format( _( "Library %s not found" ), aNickname );
1350 return tl::unexpected( LIBRARY_ERROR( msg ) );
1351}
1352
1353
1354std::optional<LIB_STATUS> LIBRARY_MANAGER_ADAPTER::GetLibraryStatus( const wxString& aNickname ) const
1355{
1356 {
1357 std::shared_lock lock( m_librariesMutex );
1358
1359 if( auto it = m_libraries.find( aNickname ); it != m_libraries.end() )
1360 return it->second.status;
1361 }
1362
1363 {
1364 std::shared_lock lock( globalLibsMutex() );
1365
1366 if( auto it = globalLibs().find( aNickname ); it != globalLibs().end() )
1367 return it->second.status;
1368 }
1369
1370 return std::nullopt;
1371}
1372
1373
1375{
1376 std::unique_lock<std::mutex> asyncLock( m_loadMutex, std::try_to_lock );
1377
1378 if( !asyncLock )
1379 return;
1380
1381 std::erase_if( m_futures,
1382 []( std::future<void>& aFuture )
1383 {
1384 return aFuture.valid()
1385 && aFuture.wait_for( 0s ) == std::future_status::ready;
1386 } );
1387
1388 if( !m_futures.empty() )
1389 {
1390 wxLogTrace( traceLibraries, "Cannot AsyncLoad, futures from a previous call remain!" );
1391 return;
1392 }
1393
1394 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( Type() );
1395
1396 m_loadTotal.store( rows.size() );
1397 m_loadCount.store( 0 );
1398
1399 if( rows.empty() )
1400 {
1401 wxLogTrace( traceLibraries, "AsyncLoad: no libraries left to load; exiting" );
1402 return;
1403 }
1404
1406
1407 auto check =
1408 [&]( const wxString& aLib, std::map<wxString, LIB_DATA>& aMap, std::shared_mutex& aMutex )
1409 {
1410 std::shared_lock lock( aMutex );
1411
1412 if( auto it = aMap.find( aLib ); it != aMap.end() )
1413 {
1414 LOAD_STATUS status = it->second.status.load_status;
1415
1416 if( status == LOAD_STATUS::LOADED || status == LOAD_STATUS::LOADING )
1417 return true;
1418 }
1419
1420 return false;
1421 };
1422
1423 for( const LIBRARY_TABLE_ROW* row : rows )
1424 {
1425 wxString nickname = row->Nickname();
1426 LIBRARY_TABLE_SCOPE scope = row->Scope();
1427
1428 if( check( nickname, m_libraries, m_librariesMutex ) )
1429 {
1430 m_loadTotal.fetch_sub( 1 );
1431 continue;
1432 }
1433
1434 if( check( nickname, globalLibs(), globalLibsMutex() ) )
1435 {
1436 m_loadTotal.fetch_sub( 1 );
1437 continue;
1438 }
1439
1440 // Resolve the URI on the main thread. URI expansion accesses PROJECT data
1441 // (text variables, env vars) that is not thread-safe.
1442 wxString uri = getUri( row );
1443
1444 m_futures.emplace_back( tp.submit_task(
1445 [this, nickname, scope, uri]()
1446 {
1447 if( m_abort.load() )
1448 return;
1449
1450 LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
1451
1452 if( result.has_value() )
1453 {
1454 LIB_DATA* lib = *result;
1455
1456 try
1457 {
1458 {
1459 std::unique_lock lock( scope == LIBRARY_TABLE_SCOPE::GLOBAL ? globalLibsMutex()
1460 : m_librariesMutex );
1461 lib->status.load_status = LOAD_STATUS::LOADING;
1462 }
1463
1464 enumerateLibrary( lib, uri );
1465
1466 {
1467 std::unique_lock lock( scope == LIBRARY_TABLE_SCOPE::GLOBAL ? globalLibsMutex()
1468 : m_librariesMutex );
1469 lib->status.load_status = LOAD_STATUS::LOADED;
1470 }
1471 }
1472 catch( IO_ERROR& e )
1473 {
1474 std::unique_lock lock( scope == LIBRARY_TABLE_SCOPE::GLOBAL ? globalLibsMutex()
1475 : m_librariesMutex );
1476 lib->status.load_status = LOAD_STATUS::LOAD_ERROR;
1477 lib->status.error = LIBRARY_ERROR( { e.What() } );
1478 wxLogTrace( traceLibraries, "%s: plugin threw exception: %s", nickname, e.What() );
1479 }
1480 }
1481 else
1482 {
1483 std::unique_lock lock( scope == LIBRARY_TABLE_SCOPE::GLOBAL ? globalLibsMutex()
1484 : m_librariesMutex );
1485
1486 std::map<wxString, LIB_DATA>& target = ( scope == LIBRARY_TABLE_SCOPE::GLOBAL ) ? globalLibs()
1487 : m_libraries;
1488
1489 target[nickname].status = LIB_STATUS( {
1490 .load_status = LOAD_STATUS::LOAD_ERROR,
1491 .error = result.error()
1492 } );
1493 }
1494
1495 ++m_loadCount;
1496 }, BS::pr::lowest ) );
1497 }
1498
1499 size_t total = m_loadTotal.load();
1500
1501 if( total )
1502 wxLogTrace( traceLibraries, "Started async load of %zu libraries", total );
1503}
const char * name
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,...
Definition io_base.cpp:53
virtual bool IsLibraryWritable(const wxString &aLibraryPath)
Return true if the library at aLibraryPath is writable.
Definition io_base.cpp:60
virtual void CreateLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Create a new empty library at aLibraryPath empty.
Definition io_base.cpp:46
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()
wxString m_PcmLibPrefix
The interface used by the classes that actually can load IO plugins for the different parts of KiCad ...
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
virtual std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib)=0
virtual std::optional< LIBRARY_ERROR > LibraryError(const wxString &aNickname) const
void ReloadLibraryEntry(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const
Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void AbortAsyncLoad()
Aborts any async load in progress; blocks until fully done aborting.
LIBRARY_TABLE * GlobalTable() const
Retrieves the global library table for this adapter type.
virtual std::shared_mutex & globalLibsMutex()=0
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
void CheckTableRow(LIBRARY_TABLE_ROW &aRow)
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 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.
wxString GetLibraryLoadErrors() const
Returns all library load errors as newline-separated strings for display.
std::optional< wxString > FindLibraryByURI(const wxString &aURI) const
std::shared_mutex m_librariesMutex
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
virtual IO_BASE * plugin(const LIB_DATA *aRow)=0
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.
LIBRARY_RESULT< LIB_DATA * > loadFromScope(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope, std::map< wxString, LIB_DATA > &aTarget, std::shared_mutex &aMutex)
std::vector< std::future< void > > m_futures
static wxString getUri(const LIBRARY_TABLE_ROW *aRow)
bool CreateLibrary(const wxString &aNickname)
Creates the library (i.e. saves to disk) for the given row if it exists.
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
virtual bool IsWritable(const wxString &aNickname) const
Return true if the given nickname exists and is not a read-only library.
std::vector< std::pair< wxString, LIB_STATUS > > GetLibraryStatuses() const
Returns a list of all library nicknames and their status (even if they failed to load)
std::atomic< size_t > m_loadTotal
std::atomic< size_t > m_loadCount
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
void ReloadTables(LIBRARY_TABLE_SCOPE aScope, std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
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)
bool RemoveAdapter(LIBRARY_TABLE_TYPE aType, LIBRARY_MANAGER_ADAPTER *aAdapter)
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
static bool UrisAreEquivalent(const wxString &aURI1, const wxString &aURI2)
std::mutex m_adaptersMutex
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)
void AbortAsyncLoads()
Abort any async library loading operations in progress.
static bool GlobalTablesValid()
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_TABLE > > m_projectTables
std::optional< LIBRARY_TABLE_ROW * > FindRowByURI(LIBRARY_TABLE_TYPE aType, const wxString &aUri, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
void LoadProjectTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the project library tables in the given list, or all tables if no list is given
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 SetOk(bool aOk=true)
void SetType(const wxString &aType)
void SetErrorDescription(const wxString &aDescription)
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
LIBRARY_TABLE_ROW & InsertRow()
Builds a new row and inserts it at the end of the table; returning a reference to the row.
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.
bool IsOk() const
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
Definition paths.cpp:347
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:624
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.
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:787
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:131
Container for project specific data.
Definition project.h:65
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.
Definition common.cpp:558
The common library.
#define _(s)
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
PROJECT & Prj()
Definition kicad.cpp:642
LOAD_STATUS
Status of a library load managed by a library adapter.
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
LIBRARY_TABLE_TYPE
LIBRARY_TABLE_SCOPE
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.
Definition env_vars.cpp:86
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
wxString message
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
The overall status of a loaded or loading library.
std::string path
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:39