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