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