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