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 <common.h>
22#include <env_vars.h>
23#include <list>
24#include <magic_enum.hpp>
25#include <unordered_set>
26
27#include <paths.h>
28#include <pgm_base.h>
29#include <richio.h>
30#include <trace_helpers.h>
32
36#include <wx/dir.h>
37#include <wx/log.h>
38
40{
41 std::vector<LIBRARY_TABLE> tables;
42};
43
44
48
49
51
52const std::map<LIBRARY_TABLE_TYPE, const std::string&> LIBRARY_MANAGER::m_typeToFilenameMap =
56
57
58void LIBRARY_MANAGER::loadTables( const wxString& aTablePath, LIBRARY_TABLE_SCOPE aScope,
59 std::vector<LIBRARY_TABLE_TYPE> aTablesToLoad )
60{
61 auto getTarget =
62 [&]() -> std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>&
63 {
64 switch( aScope )
65 {
67 return m_tables;
68
70 return m_projectTables;
71
72 default:
73 wxCHECK_MSG( false, m_tables, "Invalid scope passed to loadTables" );
74 }
75 };
76
77 std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>& aTarget = getTarget();
78
79 if( aTablesToLoad.size() == 0 )
81
82 for( LIBRARY_TABLE_TYPE type : aTablesToLoad )
83 {
84 aTarget.erase( type );
85
86 wxCHECK2( m_typeToFilenameMap.contains( type ), continue );
87 const std::string& fileName = m_typeToFilenameMap.at( type );
88
89 wxFileName fn( aTablePath, fileName );
90
91 if( fn.IsFileReadable() )
92 {
93 auto table = std::make_unique<LIBRARY_TABLE>( fn, aScope );
94 wxCHECK2( table->Type() == type, continue );
95 aTarget[type] = std::move( table );
96 loadNestedTables( *aTarget[type] );
97 }
98 else
99 {
100 wxLogTrace( traceLibraries, "No library table found at %s", fn.GetFullPath() );
101 }
102 }
103}
104
105
107{
108 std::unordered_set<wxString> seenTables;
109
110 std::function<void(LIBRARY_TABLE&)> processOneTable =
111 [&]( LIBRARY_TABLE& aTable )
112 {
113 seenTables.insert( aTable.Path() );
114
115 if( !aTable.IsOk() )
116 return;
117
118 for( LIBRARY_TABLE_ROW& row : aTable.Rows() )
119 {
120 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
121 {
122 wxFileName file( row.URI() );
123
124 // URI may be relative to parent
125 file.MakeAbsolute( wxFileName( aTable.Path() ).GetPath() );
126
128 wxString src = file.GetFullPath();
129
130 if( seenTables.contains( src ) )
131 {
132 wxLogTrace( traceLibraries, "Library table %s has already been loaded!",
133 src );
134 row.SetOk( false );
135 row.SetErrorDescription(
136 _( "A reference to this library table already exists" ) );
137 continue;
138 }
139
140 auto child = std::make_unique<LIBRARY_TABLE>( file, aRootTable.Scope() );
141
142 processOneTable( *child );
143
144 if( !child->IsOk() )
145 {
146 row.SetOk( false );
147 row.SetErrorDescription( child->ErrorDescription() );
148 }
149
150 m_childTables.insert( { row.URI(), std::move( child ) } );
151 }
152 }
153 };
154
155 processOneTable( aRootTable );
156}
157
158
170
171
173{
174
175
176 if( aScope == LIBRARY_TABLE_SCOPE::GLOBAL )
177 {
178 wxCHECK( !m_tables.contains( aType ), /* void */ );
179 wxFileName fn( PATHS::GetUserSettingsPath(), tableFileName( aType ) );
180
181 m_tables[aType] = std::make_unique<LIBRARY_TABLE>( fn.GetFullPath(),
183 }
184 else if( aScope == LIBRARY_TABLE_SCOPE::PROJECT )
185 {
186 wxCHECK( !m_projectTables.contains( aType ), /* void */ );
187 wxFileName fn( Pgm().GetSettingsManager().Prj().GetProjectDirectory(),
188 tableFileName( aType ) );
189
190 m_projectTables[aType] = std::make_unique<LIBRARY_TABLE>( fn.GetFullPath(),
192 }
193}
194
195
196class PCM_LIB_TRAVERSER final : public wxDirTraverser
197{
198public:
199 explicit PCM_LIB_TRAVERSER( const wxString& aBasePath, LIBRARY_MANAGER& aManager,
200 const wxString& aPrefix ) :
201 m_manager( aManager ),
203 m_path_prefix( aBasePath ),
204 m_lib_prefix( aPrefix )
205 {
206 wxFileName f( aBasePath, "" );
207 m_prefix_dir_count = f.GetDirCount();
208
210 .value_or( nullptr );
212 .value_or( nullptr );
215 .value_or( nullptr );
216 }
217
219 wxDirTraverseResult OnFile( const wxString& aFilePath ) override
220 {
221 wxFileName file = wxFileName::FileName( aFilePath );
222
223 // consider a file to be a lib if it's name ends with .kicad_sym and
224 // it is under $KICADn_3RD_PARTY/symbols/<pkgid>/ i.e. has nested level of at least +2
225 if( file.GetExt() == wxT( "kicad_sym" ) && file.GetDirCount() >= m_prefix_dir_count + 2
226 && file.GetDirs()[m_prefix_dir_count] == wxT( "symbols" ) )
227 {
229 }
230
231 return wxDIR_CONTINUE;
232 }
233
235 wxDirTraverseResult OnDir( const wxString& dirPath ) override
236 {
237 static wxString designBlockExt = wxString::Format( wxS( ".%s" ),
239 wxFileName dir = wxFileName::DirName( dirPath );
240
241 // consider a directory to be a lib if it's name ends with .pretty and
242 // it is under $KICADn_3RD_PARTY/footprints/<pkgid>/ i.e. has nested level of at least +3
243 if( dirPath.EndsWith( wxS( ".pretty" ) ) && dir.GetDirCount() >= m_prefix_dir_count + 3
244 && dir.GetDirs()[m_prefix_dir_count] == wxT( "footprints" ) )
245 {
247 }
248 else if( dirPath.EndsWith( designBlockExt )
249 && dir.GetDirCount() >= m_prefix_dir_count + 3
250 && dir.GetDirs()[m_prefix_dir_count] == wxT( "design_blocks" ) )
251 {
252 addRowIfNecessary( m_designBlockTable, dir, ADD_MODE::AM_DIRECTORY, designBlockExt.Len() );
253 }
254
255 return wxDIR_CONTINUE;
256 }
257
258 std::set<LIBRARY_TABLE*> Modified() const { return m_modified; }
259
260private:
261 void ensureUnique( LIBRARY_TABLE* aTable, const wxString& aBaseName, wxString& aNickname ) const
262 {
263 if( aTable->HasRow( aNickname ) )
264 {
265 int increment = 1;
266
267 do
268 {
269 aNickname = wxString::Format( "%s%s_%d", m_lib_prefix, aBaseName, increment );
270 increment++;
271 } while( aTable->HasRow( aNickname ) );
272 }
273 }
274
275 enum class ADD_MODE
276 {
279 };
280
281 void addRowIfNecessary( LIBRARY_TABLE* aTable, const wxFileName& aSource, ADD_MODE aMode,
282 int aExtensionLength )
283 {
284 wxString versionedPath = wxString::Format( wxS( "${%s}" ),
285 ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
286
287 wxArrayString parts = aSource.GetDirs();
288 parts.RemoveAt( 0, m_prefix_dir_count );
289 parts.Insert( versionedPath, 0 );
290
291 if( aMode == ADD_MODE::AM_FILE )
292 parts.Add( aSource.GetFullName() );
293
294 wxString libPath = wxJoin( parts, '/' );
295
296 if( !aTable->HasRowWithURI( libPath, m_project ) )
297 {
298 wxString name = parts.Last().substr( 0, parts.Last().length() - aExtensionLength );
299 wxString nickname = wxString::Format( "%s%s", m_lib_prefix, name );
300
301 ensureUnique( aTable, name, nickname );
302
303 wxLogTrace( traceLibraries, "Manager: Adding PCM lib '%s' as '%s'", libPath, nickname );
304
305 LIBRARY_TABLE_ROW& row = aTable->InsertRow();
306
307 row.SetNickname( nickname );
308 row.SetURI( libPath );
309 row.SetType( wxT( "KiCad" ) );
310 row.SetDescription( _( "Added by Plugin and Content Manager" ) );
311 m_modified.insert( aTable );
312 }
313 else
314 {
315 wxLogTrace( traceLibraries, "Manager: Not adding existing PCM lib '%s'", libPath );
316 }
317 }
318
322 wxString m_lib_prefix;
324 std::set<LIBRARY_TABLE*> m_modified;
325
329};
330
331
333{
334 wxCHECK( m_typeToFilenameMap.contains( aType ), wxEmptyString );
335 wxString basePath = PATHS::GetUserSettingsPath();
336
337 wxFileName fn( basePath, m_typeToFilenameMap.at( aType ) );
338 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
339
340 return fn.GetFullPath();
341}
342
343
344bool LIBRARY_MANAGER::IsTableValid( const wxString& aPath )
345{
346 if( wxFileName fn( aPath ); fn.IsFileReadable() )
347 {
348 if( LIBRARY_TABLE temp( fn, LIBRARY_TABLE_SCOPE::GLOBAL ); temp.IsOk() )
349 return true;
350 }
351
352 return false;
353}
354
355
357{
358 return InvalidGlobalTables().empty();
359}
360
361
362std::vector<LIBRARY_TABLE_TYPE> LIBRARY_MANAGER::InvalidGlobalTables()
363{
364 std::vector<LIBRARY_TABLE_TYPE> invalidTables;
365 wxString basePath = PATHS::GetUserSettingsPath();
366
367 for( auto [type, name] : m_typeToFilenameMap )
368 {
369 if( wxFileName fn( basePath, name ); !IsTableValid( fn.GetFullPath() ) )
370 invalidTables.emplace_back( type );
371 }
372
373 return invalidTables;
374}
375
376
377bool LIBRARY_MANAGER::CreateGlobalTable( LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries )
378{
379 wxCHECK( m_typeToFilenameMap.contains( aType ), false );
380 wxFileName fn( DefaultGlobalTablePath( aType ) );
381
383 table.SetType( aType );
384 table.Rows().clear();
385
386 wxFileName defaultLib( PATHS::GetStockTemplatesPath(), m_typeToFilenameMap.at( aType ) );
387
388 if( !defaultLib.IsFileReadable() )
389 {
390 wxLogTrace( traceLibraries, "Warning: couldn't read default library table for %s at '%s'",
391 magic_enum::enum_name( aType ), defaultLib.GetFullPath() );
392 }
393
394 if( aPopulateDefaultLibraries )
395 {
396 LIBRARY_TABLE_ROW& chained = table.InsertRow();
398 chained.SetNickname( wxT( "KiCad" ) );
399 chained.SetDescription( _( "KiCad Default Libraries" ) );
400 chained.SetURI( defaultLib.GetFullPath() );
401 }
402
403 try
404 {
405 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fn.GetFullPath(), KICAD_FORMAT::FORMAT_MODE::LIBRARY_TABLE );
406 table.Format( &formatter );
407 }
408 catch( IO_ERROR& e )
409 {
410 wxLogTrace( traceLibraries, "Exception while saving: %s", e.What() );
411 return false;
412 }
413
414 return true;
415}
416
417
418void LIBRARY_MANAGER::LoadGlobalTables( std::initializer_list<LIBRARY_TABLE_TYPE> aTablesToLoad )
419{
420 // Cancel any in-progress load
421 for( const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter : m_adapters | std::views::values )
422 adapter->GlobalTablesChanged( aTablesToLoad );
423
425
427 KICAD_SETTINGS* settings = mgr.GetAppSettings<KICAD_SETTINGS>( "kicad" );
428
429 wxCHECK( settings, /* void */ );
430
431 const ENV_VAR_MAP& vars = Pgm().GetLocalEnvVariables();
432 std::optional<wxString> packagesPath = ENV_VAR::GetVersionedEnvVarValue( vars, wxT( "3RD_PARTY" ) );
433
434 if( packagesPath && settings->m_PcmLibAutoAdd )
435 {
436 // Scan for libraries in PCM packages directory
437 wxFileName d( *packagesPath, "" );
438
439 if( d.DirExists() )
440 {
441 PCM_LIB_TRAVERSER traverser( *packagesPath, *this, settings->m_PcmLibPrefix );
442 wxDir dir( d.GetPath() );
443
444 dir.Traverse( traverser );
445
446 for( LIBRARY_TABLE* table : traverser.Modified() )
447 {
448 Pgm().GetLibraryManager().Save( table ).map_error(
449 []( const LIBRARY_ERROR& aError )
450 {
451 wxLogTrace( traceLibraries, "Warning: save failed after PCM auto-add: %s",
452 aError.message );
453 } );
454 }
455 }
456 }
457
458 auto cleanupRemovedPCMLibraries =
459 [&]( LIBRARY_TABLE_TYPE aType )
460 {
461 LIBRARY_TABLE* table = Table( aType, LIBRARY_TABLE_SCOPE::GLOBAL ).value_or( nullptr );
462 wxCHECK( table, /* void */ );
463
464 auto toErase = std::ranges::remove_if( table->Rows(),
465 [&]( const LIBRARY_TABLE_ROW& aRow )
466 {
467 wxString path = GetFullURI( &aRow, true );
468 return path.StartsWith( *packagesPath ) && !wxFile::Exists( path );
469 } );
470
471 table->Rows().erase( toErase.begin(), toErase.end() );
472
473 if( !toErase.empty() )
474 {
475 Pgm().GetLibraryManager().Save( table ).map_error(
476 []( const LIBRARY_ERROR& aError )
477 {
478 wxLogTrace( traceLibraries,
479 "Warning: save failed after PCM auto-remove: %s",
480 aError.message );
481 } );
482 }
483 };
484
485 if( packagesPath && settings->m_PcmLibAutoRemove )
486 {
487 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::SYMBOL );
488 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::FOOTPRINT );
489 cleanupRemovedPCMLibraries( LIBRARY_TABLE_TYPE::DESIGN_BLOCK );
490 }
491}
492
493
495{
496 LoadProjectTables( Pgm().GetSettingsManager().Prj().GetProjectDirectory() );
497
498 for( const std::unique_ptr<LIBRARY_MANAGER_ADAPTER>& adapter : m_adapters | std::views::values )
499 adapter->ProjectChanged();
500}
501
502
504 std::unique_ptr<LIBRARY_MANAGER_ADAPTER>&& aAdapter )
505{
506 wxCHECK_MSG( !m_adapters.contains( aType ), /**/, "You should only register an adapter once!" );
507
508 m_adapters[aType] = std::move( aAdapter );
509}
510
511
512std::optional<LIBRARY_MANAGER_ADAPTER*> LIBRARY_MANAGER::Adapter( LIBRARY_TABLE_TYPE aType ) const
513{
514 if( m_adapters.contains( aType ) )
515 return m_adapters.at( aType ).get();
516
517 return std::nullopt;
518}
519
520
521std::optional<LIBRARY_TABLE*> LIBRARY_MANAGER::Table( LIBRARY_TABLE_TYPE aType,
522 LIBRARY_TABLE_SCOPE aScope )
523{
524 switch( aScope )
525 {
528 wxCHECK_MSG( false, std::nullopt, "Table() requires a single scope" );
529
531 {
532 if( !m_tables.contains( aType ) )
533 {
534 wxLogTrace( traceLibraries, "WARNING: missing global table (%s)",
535 magic_enum::enum_name( aType ) );
536 return std::nullopt;
537 }
538
539 return m_tables.at( aType ).get();
540 }
541
543 {
544 // TODO: handle multiple projects
545 if( !m_projectTables.contains( aType ) )
546 {
547 if( !Pgm().GetSettingsManager().Prj().IsNullProject() )
549 else
550 return std::nullopt;
551 }
552
553 return m_projectTables.at( aType ).get();
554 }
555 }
556
557 return std::nullopt;
558}
559
560
561std::vector<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::Rows( LIBRARY_TABLE_TYPE aType,
562 LIBRARY_TABLE_SCOPE aScope,
563 bool aIncludeInvalid ) const
564{
565 std::map<wxString, LIBRARY_TABLE_ROW*> rows;
566 std::vector<wxString> rowOrder;
567
568 std::list<std::ranges::ref_view<
569 const std::map<LIBRARY_TABLE_TYPE, std::unique_ptr<LIBRARY_TABLE>>
570 >> tables;
571
572 switch( aScope )
573 {
575 tables = { std::views::all( m_tables ) };
576 break;
577
579 tables = { std::views::all( m_projectTables ) };
580 break;
581
583 tables = { std::views::all( m_tables ), std::views::all( m_projectTables ) };
584 break;
585
587 wxFAIL;
588 }
589
590 std::function<void(const std::unique_ptr<LIBRARY_TABLE>&)> processTable =
591 [&]( const std::unique_ptr<LIBRARY_TABLE>& aTable )
592 {
593 if( aTable->Type() != aType )
594 return;
595
596 if( aTable->IsOk() || aIncludeInvalid )
597 {
598 for( LIBRARY_TABLE_ROW& row : aTable->Rows() )
599 {
600 if( row.IsOk() || aIncludeInvalid )
601 {
602 if( row.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
603 {
604 if( !m_childTables.contains( row.URI() ) )
605 continue;
606
607 processTable( m_childTables.at( row.URI() ) );
608 }
609 else
610 {
611 if( !rows.contains( row.Nickname() ) )
612 rowOrder.emplace_back( row.Nickname() );
613
614 rows[ row.Nickname() ] = &row;
615 }
616 }
617 }
618 }
619 };
620
621 for( const std::unique_ptr<LIBRARY_TABLE>& table :
622 std::views::join( tables ) | std::views::values )
623 {
624 processTable( table );
625 }
626
627 std::vector<LIBRARY_TABLE_ROW*> ret;
628
629 for( const wxString& row : rowOrder )
630 ret.emplace_back( rows[row] );
631
632 return ret;
633}
634
635
636std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::GetRow( LIBRARY_TABLE_TYPE aType,
637 const wxString& aNickname,
638 LIBRARY_TABLE_SCOPE aScope ) const
639{
640 for( LIBRARY_TABLE_ROW* row : Rows( aType, aScope, true ) )
641 {
642 if( row->Nickname() == aNickname )
643 return row;
644 }
645
646 return std::nullopt;
647}
648
649
650std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER::FindRowByURI( LIBRARY_TABLE_TYPE aType,
651 const wxString& aUri,
652 LIBRARY_TABLE_SCOPE aScope ) const
653{
654 for( LIBRARY_TABLE_ROW* row : Rows( aType, aScope, true ) )
655 {
656 if( UrisAreEquivalent( GetFullURI( row, true ), aUri ) )
657 return row;
658 }
659
660 return std::nullopt;
661}
662
663
664void LIBRARY_MANAGER::LoadProjectTables( const wxString& aProjectPath )
665{
666 if( wxFileName::IsDirReadable( aProjectPath ) )
667 {
669 }
670 else
671 {
672 m_projectTables.clear();
673 wxLogTrace( traceLibraries,
674 "New project path %s is not readable, not loading project tables",
675 aProjectPath );
676 }
677}
678
679
681{
682 wxCHECK( aTable, tl::unexpected( LIBRARY_ERROR( "Internal error" ) ) );
683
684 // TODO(JE) clean this up; shouldn't need to iterate
685 for( const std::unique_ptr<LIBRARY_TABLE>& t : m_tables | std::views::values )
686 {
687 if( t.get() == aTable )
688 {
689 wxLogTrace( traceLibraries, "Saving %s", aTable->Path() );
690 wxFileName fn( aTable->Path() );
691 // This should already be normalized, but just in case...
692 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
693
694 try
695 {
696 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fn.GetFullPath(), KICAD_FORMAT::FORMAT_MODE::LIBRARY_TABLE );
697 aTable->Format( &formatter );
698 }
699 catch( IO_ERROR& e )
700 {
701 wxLogTrace( traceLibraries, "Exception while saving: %s", e.What() );
702 return tl::unexpected( LIBRARY_ERROR( e.What() ) );
703 }
704
705 return LIBRARY_RESULT<void>();
706 }
707 }
708
709 // Unmanaged table? TODO(JE) should this happen?
710 wxLogTrace( traceLibraries, "Can't save %s; unmanaged library", aTable->Path() );
711 return tl::unexpected( LIBRARY_ERROR( "Internal error" ) );
712}
713
714
715std::optional<wxString> LIBRARY_MANAGER::GetFullURI( LIBRARY_TABLE_TYPE aType,
716 const wxString& aNickname,
717 bool aSubstituted ) const
718{
719 if( std::optional<const LIBRARY_TABLE_ROW*> result = GetRow( aType, aNickname ) )
720 return GetFullURI( *result, aSubstituted );
721
722 return std::nullopt;
723}
724
725
727 bool aSubstituted )
728{
729 if( aSubstituted )
730 return ExpandEnvVarSubstitutions( aRow->URI(), nullptr );
731
732 return aRow->URI();
733}
734
735
736wxString LIBRARY_MANAGER::ExpandURI( const wxString& aShortURI, const PROJECT& aProject )
737{
738 wxFileName path( ExpandEnvVarSubstitutions( aShortURI, &aProject ) );
739 path.MakeAbsolute();
740 return path.GetFullPath();
741}
742
743
744bool LIBRARY_MANAGER::UrisAreEquivalent( const wxString& aURI1, const wxString& aURI2 )
745{
746 // Avoid comparing filenames as wxURIs
747 if( aURI1.Find( "://" ) != wxNOT_FOUND )
748 {
749 // found as full path
750 return aURI1 == aURI2;
751 }
752 else
753 {
754 const wxFileName fn1( aURI1 );
755 const wxFileName fn2( aURI2 );
756
757 // This will also test if the file is a symlink so if we are comparing
758 // a symlink to the same real file, the comparison will be true. See
759 // wxFileName::SameAs() in the wxWidgets source.
760
761 // found as full path and file name
762 return fn1 == fn2;
763 }
764}
765
766
770
771
776
777
781
782
787
788
790{
791 abortLoad();
792
793 {
794 std::lock_guard lock( m_libraries_mutex );
795 m_libraries.clear();
796 }
797}
798
799
800void LIBRARY_MANAGER_ADAPTER::GlobalTablesChanged( std::initializer_list<LIBRARY_TABLE_TYPE> aChangedTables )
801{
802 bool me = aChangedTables.size() == 0;
803
804 for( LIBRARY_TABLE_TYPE type : aChangedTables )
805 {
806 if( type == Type() )
807 {
808 me = true;
809 break;
810 }
811 }
812
813 if( !me )
814 return;
815
816 abortLoad();
817
818 {
819 std::lock_guard lock( globalLibsMutex() );
820 globalLibs().clear();
821 }
822}
823
824
825
827{
828 wxCHECK( m_manager.Table( Type(), LIBRARY_TABLE_SCOPE::GLOBAL ), nullptr );
829 return *m_manager.Table( Type(), LIBRARY_TABLE_SCOPE::GLOBAL );
830}
831
832
833std::optional<LIBRARY_TABLE*> LIBRARY_MANAGER_ADAPTER::ProjectTable() const
834{
836}
837
838
839std::optional<wxString> LIBRARY_MANAGER_ADAPTER::FindLibraryByURI( const wxString& aURI ) const
840{
841 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
842 {
843 if( LIBRARY_MANAGER::UrisAreEquivalent( row->URI(), aURI ) )
844 return row->Nickname();
845 }
846
847 return std::nullopt;
848}
849
850
851std::vector<wxString> LIBRARY_MANAGER_ADAPTER::GetLibraryNames() const
852{
853 std::vector<wxString> ret;
854
855 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
856 {
857 if( fetchIfLoaded( row->Nickname() ) )
858 ret.emplace_back( row->Nickname() );
859 }
860
861 return ret;
862}
863
864
865bool LIBRARY_MANAGER_ADAPTER::HasLibrary( const wxString& aNickname,
866 bool aCheckEnabled ) const
867{
868 if( std::optional<const LIB_DATA*> r = fetchIfLoaded( aNickname ); r.has_value() )
869 return !aCheckEnabled || !( *r )->row->Disabled();
870
871 return false;
872}
873
874
875bool LIBRARY_MANAGER_ADAPTER::DeleteLibrary( const wxString& aNickname )
876{
877 if( LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( aNickname ); result.has_value() )
878 {
879 LIB_DATA* data = *result;
880 std::map<std::string, UTF8> options = data->row->GetOptionsMap();
881
882 try
883 {
884 return data->plugin->DeleteLibrary( getUri( data->row ), &options );
885 }
886 catch( ... )
887 {
888 return false;
889 }
890 }
891
892 return false;
893}
894
895
896std::optional<wxString> LIBRARY_MANAGER_ADAPTER::GetLibraryDescription( const wxString& aNickname ) const
897{
898 if( std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname ); optRow )
899 return ( *optRow )->row->Description();
900
901 return std::nullopt;
902}
903
904
905std::vector<LIBRARY_TABLE_ROW *> LIBRARY_MANAGER_ADAPTER::Rows(
906 LIBRARY_TABLE_SCOPE aScope, bool aIncludeInvalid ) const
907{
908 return m_manager.Rows( Type(), aScope, aIncludeInvalid );
909}
910
911
912std::optional<LIBRARY_TABLE_ROW *> LIBRARY_MANAGER_ADAPTER::GetRow(
913 const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope ) const
914{
915 return m_manager.GetRow( Type(), aNickname, aScope );
916}
917
918
919std::optional<LIBRARY_TABLE_ROW*> LIBRARY_MANAGER_ADAPTER::FindRowByURI(
920 const wxString& aUri,
921 LIBRARY_TABLE_SCOPE aScope ) const
922{
923 return m_manager.FindRowByURI( Type(), aUri, aScope );
924}
925
926
928{
929 if( m_futures.empty() )
930 return;
931
932 wxLogTrace( traceLibraries, "Aborting library load..." );
933 m_abort.store( true );
934
936 wxLogTrace( traceLibraries, "Aborted" );
937
938 m_abort.store( false );
939 m_futures.clear();
940 m_loadTotal = 0;
941}
942
943
945{
946 if( m_loadTotal == 0 )
947 return std::nullopt;
948
949 size_t loaded = m_loadCount.load();
950 return loaded / static_cast<float>( m_loadTotal );
951}
952
953
955{
956 for( const std::future<void>& future : m_futures )
957 future.wait();
958}
959
960
961bool LIBRARY_MANAGER_ADAPTER::IsLibraryLoaded( const wxString& aNickname )
962{
963 // TODO: row->IsOK() doesn't actually tell you if a library is loaded
964 // Once we are preloading libraries we can cache the status of plugin load instead
965
966 if( m_libraries.contains( aNickname ) )
967 return m_libraries[aNickname].row->IsOk();
968
969 if( globalLibs().contains( aNickname ) )
970 return globalLibs()[aNickname].row->IsOk();
971
972 return false;
973}
974
975
977{
979}
980
981
982std::optional<const LIB_DATA*> LIBRARY_MANAGER_ADAPTER::fetchIfLoaded(
983 const wxString& aNickname ) const
984{
985 if( m_libraries.contains( aNickname )
986 && m_libraries.at( aNickname ).status.load_status == LOAD_STATUS::LOADED )
987 {
988 return &m_libraries.at( aNickname );
989 }
990
991 if( globalLibs().contains( aNickname )
992 && globalLibs().at( aNickname ).status.load_status == LOAD_STATUS::LOADED )
993 {
994 return &globalLibs().at( aNickname );
995 }
996
997 return std::nullopt;
998}
999
1000
1002 const wxString& aNickname )
1003{
1004 if( m_libraries.contains( aNickname ) && m_libraries.at( aNickname ).status.load_status == LOAD_STATUS::LOADED )
1005 return &m_libraries.at( aNickname );
1006
1007 if( globalLibs().contains( aNickname )
1008 && globalLibs().at( aNickname ).status.load_status == LOAD_STATUS::LOADED )
1009 {
1010 return &globalLibs().at( aNickname );
1011 }
1012
1013 return std::nullopt;
1014}
1015
1016
1018{
1019 auto tryLoadFromScope =
1020 [&]( LIBRARY_TABLE_SCOPE aScope, std::map<wxString, LIB_DATA>& aTarget,
1021 std::mutex& aMutex ) -> LIBRARY_RESULT<LIB_DATA*>
1022 {
1023 bool present = false;
1024
1025 {
1026 std::lock_guard lock( aMutex );
1027 present = aTarget.contains( aNickname ) && aTarget.at( aNickname ).plugin;
1028 }
1029
1030 if( !present )
1031 {
1032 if( auto result = m_manager.GetRow( Type(), aNickname, aScope ) )
1033 {
1034 const LIBRARY_TABLE_ROW* row = *result;
1035 wxLogTrace( traceLibraries, "Library %s (%s) not yet loaded, will attempt...",
1036 aNickname, magic_enum::enum_name( aScope ) );
1037
1038 if( LIBRARY_RESULT<IO_BASE*> plugin = createPlugin( row ); plugin.has_value() )
1039 {
1040 std::lock_guard lock( aMutex );
1041
1042 aTarget[ row->Nickname() ].status.load_status = LOAD_STATUS::LOADING;
1043 aTarget[ row->Nickname() ].row = row;
1044 aTarget[ row->Nickname() ].plugin.reset( *plugin );
1045
1046 return &aTarget.at( aNickname );
1047 }
1048 else
1049 {
1050 return tl::unexpected( plugin.error() );
1051 }
1052 }
1053
1054 return nullptr;
1055 }
1056
1057 return &aTarget.at( aNickname );
1058 };
1059
1062
1063 if( !result.has_value() || *result )
1064 return result;
1065
1067
1068 if( !result.has_value() || *result )
1069 return result;
1070
1071 wxString msg = wxString::Format( _( "Library %s not found" ), aNickname );
1072 return tl::unexpected( LIBRARY_ERROR( msg ) );
1073}
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
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
std::optional< float > AsyncLoadProgress() const
Returns async load progress between 0.0 and 1.0, or nullopt if load is not in progress.
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
LIBRARY_TABLE * GlobalTable() const
Retrieves the global library table for this adapter type.
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
bool IsLibraryLoaded(const wxString &aNickname)
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Like LIBRARY_MANAGER::Rows but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< LIBRARY_TABLE_ROW * > FindRowByURI(const wxString &aUri, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::FindRowByURI but filtered to the LIBRARY_TABLE_TYPE of this adapter.
virtual std::map< wxString, LIB_DATA > & globalLibs()=0
virtual std::mutex & globalLibsMutex()=0
virtual LIBRARY_TABLE_TYPE Type() const =0
The type of library table this adapter works with.
bool DeleteLibrary(const wxString &aNickname)
Deletes the given library from disk if it exists; returns true if deleted.
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
std::optional< wxString > FindLibraryByURI(const wxString &aURI) const
LIBRARY_MANAGER & Manager() const
void abortLoad()
Aborts any async load in progress; blocks until fully done aborting.
std::optional< wxString > GetLibraryDescription(const wxString &aNickname) const
virtual LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row)=0
Creates a concrete plugin for the given row.
void GlobalTablesChanged(std::initializer_list< LIBRARY_TABLE_TYPE > aChangedTables={})
Notify the adapter that the global library tables have changed.
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::map< wxString, LIB_DATA > m_libraries
std::vector< wxString > GetLibraryNames() const
Returns a list of library nicknames that are available (skips any that failed to load)
virtual void ProjectChanged()
Notify the adapter that the active project has changed.
std::vector< std::future< void > > m_futures
static wxString getUri(const LIBRARY_TABLE_ROW *aRow)
std::atomic< size_t > m_loadCount
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
static wxString ExpandURI(const wxString &aShortURI, const PROJECT &aProject)
std::optional< LIBRARY_MANAGER_ADAPTER * > Adapter(LIBRARY_TABLE_TYPE aType) const
std::map< wxString, std::unique_ptr< LIBRARY_TABLE > > m_childTables
Map of full URI to table object for tables that are referenced by global or project tables.
void loadNestedTables(LIBRARY_TABLE &aTable)
LIBRARY_RESULT< void > Save(LIBRARY_TABLE *aTable) const
static const std::map< LIBRARY_TABLE_TYPE, const std::string & > m_typeToFilenameMap
static bool UrisAreEquivalent(const wxString &aURI1, const wxString &aURI2)
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
void RegisterAdapter(LIBRARY_TABLE_TYPE aType, std::unique_ptr< LIBRARY_MANAGER_ADAPTER > &&aAdapter)
static wxString DefaultGlobalTablePath(LIBRARY_TABLE_TYPE aType)
static std::vector< LIBRARY_TABLE_TYPE > InvalidGlobalTables()
void createEmptyTable(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
static bool GlobalTablesValid()
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_TABLE > > m_projectTables
void LoadProjectTables(const wxString &aProjectPath)
std::optional< LIBRARY_TABLE_ROW * > FindRowByURI(LIBRARY_TABLE_TYPE aType, const wxString &aUri, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
static bool CreateGlobalTable(LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries)
void loadTables(const wxString &aTablePath, LIBRARY_TABLE_SCOPE aScope, std::vector< LIBRARY_TABLE_TYPE > aTablesToLoad={})
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
static wxString tableFileName(LIBRARY_TABLE_TYPE aType)
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Returns a flattened list of libraries of the given type.
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_TABLE > > m_tables
void ProjectChanged()
Notify all adapters that the project has changed.
static bool IsTableValid(const wxString &aPath)
std::map< LIBRARY_TABLE_TYPE, std::unique_ptr< LIBRARY_MANAGER_ADAPTER > > m_adapters
std::optional< LIBRARY_TABLE_ROW * > GetRow(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
std::map< std::string, UTF8 > GetOptionsMap() const
void SetDescription(const wxString &aDescription)
static const wxString TABLE_TYPE_NAME
void SetURI(const wxString &aUri)
const wxString & URI() const
const wxString & Nickname() const
void Format(OUTPUTFORMATTER *aOutput) const
LIBRARY_TABLE_ROW & InsertRow()
Builds a new row and inserts it at the end of the table; returning a reference to the row.
const wxString & Path() const
LIBRARY_TABLE_SCOPE Scope() const
bool HasRow(const wxString &aNickname) const
bool HasRowWithURI(const wxString &aUri, const PROJECT &aProject, bool aSubstituted=false) const
Returns true if the given (fully-expanded) URI exists as a library in this table.
bool IsOk() const
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
Definition paths.cpp:307
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
Definition paths.cpp:592
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:783
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:128
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:130
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:355
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:623
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.
Definition pgm_base.cpp:946
see class PGM_BASE
std::vector< LIBRARY_TABLE > tables
Storage for an actual loaded library (including library content owned by the plugin)
std::unique_ptr< IO_BASE > plugin
const LIBRARY_TABLE_ROW * row
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:39