KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint_library_adapter.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
22
23#include <chrono>
24#include <env_vars.h>
25#include <footprint_info_impl.h>
26#include <thread_pool.h>
27#include <trace_helpers.h>
28#include <footprint.h>
29
30#include <magic_enum.hpp>
31#include <wx/hash.h>
32#include <wx/log.h>
33
34using namespace std::chrono_literals;
35
36
38
40
42
44
45
50
51
53{
54 return ENV_VAR::GetVersionedEnvVarName( wxS( "FOOTPRINT_DIR" ) );
55}
56
57
58void FOOTPRINT_LIBRARY_ADAPTER::enumerateLibrary( LIB_DATA* aLib, const wxString& aUri )
59{
60 wxArrayString namesAS;
61 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
62 PCB_IO* plugin = pcbplugin( aLib );
63 wxString nickname = aLib->row->Nickname();
64
65 // FootprintEnumerate populates the plugin's internal FP_CACHE with parsed footprints
66 plugin->FootprintEnumerate( namesAS, aUri, false, &options );
67
68 std::vector<std::unique_ptr<FOOTPRINT>> footprints;
69 footprints.reserve( namesAS.size() );
70
71 // For plugins with internal caches (like kicad_sexpr), GetEnumeratedFootprint returns
72 // a borrowed pointer and ClearCachedFootprints handles cleanup. For other plugins,
73 // GetEnumeratedFootprint allocates new memory that we must delete after cloning.
74 const bool pluginCaches = plugin->CachesEnumeratedFootprints();
75
76 for( const wxString& footprintName : namesAS )
77 {
78 try
79 {
80 const FOOTPRINT* cached = plugin->GetEnumeratedFootprint( aUri, footprintName, &options );
81
82 if( !cached )
83 continue;
84
85 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( cached->Duplicate( IGNORE_PARENT_GROUP ) );
86 footprint->SetParent( nullptr );
87
88 // For non-caching plugins, delete the allocated footprint now that we've cloned it
89 if( !pluginCaches )
90 delete cached;
91
92 LIB_ID id = footprint->GetFPID();
93 id.SetLibNickname( nickname );
94 footprint->SetFPID( id );
95 footprints.emplace_back( footprint );
96 }
97 catch( IO_ERROR& e )
98 {
99 wxLogTrace( traceLibraries, "FP: %s:%s enumeration error: %s",
100 nickname, footprintName, e.What() );
101 }
102 }
103
104 // GetLibraryTimestamp() reads the filesystem, so do it before taking the lock.
105 long long timestamp = plugin->GetLibraryTimestamp( aUri );
106
107 {
108 std::unique_lock lock( PreloadedFootprintsMutex );
109 PreloadedFootprints.Get()[nickname] = std::move( footprints );
110 m_preloadedTimestamps[nickname] = timestamp;
111 }
112
113 // Clear the plugin's FP_CACHE now that we've copied footprints to PreloadedFootprints.
114 // This eliminates the double-caching that was consuming ~1.2GB of extra RAM.
115 plugin->ClearCachedFootprints( aUri );
116}
117
118
119std::optional<LIB_STATUS> FOOTPRINT_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
120{
122
123 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
124
125 try
126 {
127 wxArrayString dummyList;
128 pcbplugin( aLib )->FootprintEnumerate( dummyList, getUri( aLib->row ), false, &options );
130 }
131 catch( IO_ERROR& e )
132 {
134 aLib->status.error = LIBRARY_ERROR( { e.What() } );
135 wxLogTrace( traceLibraries, "FP: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
136 }
137
138 return aLib->status;
139}
140
141
142std::optional<LIB_STATUS> FOOTPRINT_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
143{
145
146 if( result.has_value() )
147 return LoadOne( *result );
148
149 return LIB_STATUS{
150 .load_status = LOAD_STATUS::LOAD_ERROR,
151 .error = LIBRARY_ERROR( { result.error() } )
152 };
153}
154
155
156std::vector<FOOTPRINT*> FOOTPRINT_LIBRARY_ADAPTER::GetFootprints( const wxString& aNickname, bool aBestEfforts )
157{
158 std::vector<FOOTPRINT*> footprints;
159
160 std::shared_lock lock( PreloadedFootprintsMutex );
161 auto it = PreloadedFootprints.Get().find( aNickname );
162
163 if( it == PreloadedFootprints.Get().end() )
164 return footprints;
165
166 footprints.reserve( it->second.size() );
167
168 for( const auto& fp : it->second )
169 footprints.push_back( fp.get() );
170
171 return footprints;
172}
173
174
175
176std::vector<wxString> FOOTPRINT_LIBRARY_ADAPTER::GetFootprintNames( const wxString& aNickname, bool aBestEfforts )
177{
178 // TODO(JE) can we kill wxArrayString in internal API?
179 wxArrayString namesAS;
180 std::vector<wxString> names;
181
182 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
183 {
184 const LIB_DATA* lib = *maybeLib;
185 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
186
187 try
188 {
189 pcbplugin( lib )->FootprintEnumerate( namesAS, getUri( lib->row ), true, &options );
190 }
191 catch( IO_ERROR& e )
192 {
193 wxLogTrace( traceLibraries, "FP: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
194 }
195 }
196
197 for( const wxString& name : namesAS )
198 names.emplace_back( name );
199
200 return names;
201}
202
203
204long long FOOTPRINT_LIBRARY_ADAPTER::GenerateTimestamp( const wxString* aNickname )
205{
206 long long hash = 0;
207
208 if( aNickname )
209 {
210 wxCHECK( HasLibrary( *aNickname, true ), hash );
211
212 if( std::optional<const LIB_DATA*> r = fetchIfLoaded( *aNickname ); r.has_value() )
213 {
214 PCB_IO* plugin = dynamic_cast<PCB_IO*>( ( *r )->plugin.get() );
215 wxCHECK( plugin, hash );
216 return plugin->GetLibraryTimestamp( LIBRARY_MANAGER::GetFullURI( ( *r )->row, true ) )
217 + wxHashTable::MakeKey( *aNickname );
218 }
219 }
220
221 for( const wxString& nickname : GetLibraryNames() )
222 {
223 if( std::optional<const LIB_DATA*> r = fetchIfLoaded( nickname ); r.has_value() )
224 {
225 wxCHECK2( ( *r )->plugin->IsPCB_IO(), continue );
226 PCB_IO* plugin = static_cast<PCB_IO*>( ( *r )->plugin.get() );
227 hash += plugin->GetLibraryTimestamp( LIBRARY_MANAGER::GetFullURI( ( *r )->row, true ) )
228 + wxHashTable::MakeKey( nickname );
229 }
230 }
231
232 return hash;
233}
234
235
237{
238 std::optional<LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
239
240 if( !maybeLib )
241 return;
242
243 LIB_DATA* lib = *maybeLib;
244 PCB_IO* plugin = dynamic_cast<PCB_IO*>( lib->plugin.get() );
245
246 if( !plugin )
247 return;
248
249 wxString uri = getUri( lib->row );
250 long long currentTimestamp = plugin->GetLibraryTimestamp( uri );
251
252 {
253 std::shared_lock lock( PreloadedFootprintsMutex );
254 auto tsIt = m_preloadedTimestamps.find( aNickname );
255
256 if( tsIt != m_preloadedTimestamps.end() && tsIt->second == currentTimestamp )
257 return;
258
259 wxLogTrace( traceLibraries, "FP: %s changed on disk, re-enumerating", aNickname );
260 }
261
262 enumerateLibrary( lib, uri );
263}
264
265
266bool FOOTPRINT_LIBRARY_ADAPTER::FootprintExists( const wxString& aNickname, const wxString& aName )
267{
268 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
269 {
270 const LIB_DATA* lib = *maybeLib;
271 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
272 return pcbplugin( lib )->FootprintExists( getUri( lib->row ), aName, &options );
273 }
274
275 return false;
276}
277
278
279FOOTPRINT* FOOTPRINT_LIBRARY_ADAPTER::LoadFootprint( const wxString& aNickname, const wxString& aName, bool aKeepUUID )
280{
281 // First check if the footprint is in PreloadedFootprints and clone from there.
282 // This avoids re-parsing the file and keeps FP_CACHE from being repopulated.
283 {
284 std::shared_lock lock( PreloadedFootprintsMutex );
285 auto libIt = PreloadedFootprints.Get().find( aNickname );
286
287 if( libIt != PreloadedFootprints.Get().end() )
288 {
289 for( const auto& fp : libIt->second )
290 {
291 if( fp->GetFPID().GetLibItemName() == UTF8( aName ) )
292 {
294
295 if( aKeepUUID )
296 copy = static_cast<FOOTPRINT*>( fp->Clone() );
297 else
298 copy = static_cast<FOOTPRINT*>( fp->Duplicate( IGNORE_PARENT_GROUP ) );
299
300 copy->SetParent( nullptr );
301 return copy;
302 }
303 }
304 }
305 }
306
307 // Footprint not found in PreloadedFootprints, fall back to plugin.
308 // This re-parses the file but is needed for footprints not yet enumerated.
309 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
310 {
311 try
312 {
313 if( FOOTPRINT* footprint = pcbplugin( *lib )->FootprintLoad( getUri( ( *lib )->row ), aName, aKeepUUID ) )
314 {
315 LIB_ID id = footprint->GetFPID();
316 id.SetLibNickname( ( *lib )->row->Nickname() );
317 footprint->SetFPID( id );
318 return footprint;
319 }
320 }
321 catch( const IO_ERROR& ioe )
322 {
323 wxLogTrace( traceLibraries, "LoadFootprint: error loading %s:%s: %s", aNickname, aName, ioe.What() );
324 }
325 }
326 else
327 {
328 wxLogTrace( traceLibraries, "LoadFootprint: requested library %s not loaded", aNickname );
329 }
330
331 return nullptr;
332}
333
334
336{
337 wxString nickname = aFootprintId.GetLibNickname();
338 wxString footprintName = aFootprintId.GetLibItemName();
339
340 if( nickname.size() )
341 return LoadFootprint( nickname, footprintName, aKeepUUID );
342
343 // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
344 for( const wxString& library : GetLibraryNames() )
345 {
346 // FootprintLoad() returns NULL on not found, does not throw exception
347 // unless there's an IO_ERROR.
348 if( FOOTPRINT* ret = LoadFootprint( library, footprintName, aKeepUUID ) )
349 return ret;
350 }
351
352 return nullptr;
353}
354
355
357 const FOOTPRINT* aFootprint,
358 bool aOverwrite )
359{
360 wxCHECK( aFootprint, SAVE_SKIPPED );
361
362 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
363 {
364 if( !aOverwrite )
365 {
366 wxString fpname = aFootprint->GetFPID().GetLibItemName();
367
368 try
369 {
370 FOOTPRINT* existing = pcbplugin( *lib )->FootprintLoad( getUri( ( *lib )->row ), fpname, false );
371
372 if( existing )
373 {
374 delete existing;
375 return SAVE_SKIPPED;
376 }
377 }
378 catch( IO_ERROR& e )
379 {
380 wxLogTrace( traceLibraries, "SaveFootprint: error checking for existing footprint %s: %s",
381 aFootprint->GetFPIDAsString(), e.What() );
382 return SAVE_SKIPPED;
383 }
384 }
385
386 try
387 {
388 pcbplugin( *lib )->FootprintSave( getUri( ( *lib )->row ), aFootprint );
389 }
390 catch( IO_ERROR& e )
391 {
392 wxLogTrace( traceLibraries, "SaveFootprint: error saving %s: %s",
393 aFootprint->GetFPIDAsString(), e.What() );
394 return SAVE_SKIPPED;
395 }
396
397 {
398 std::unique_lock lock( PreloadedFootprintsMutex );
399 auto it = PreloadedFootprints.Get().find( aNickname );
400
401 if( it != PreloadedFootprints.Get().end() )
402 {
403 wxString fpName = aFootprint->GetFPID().GetLibItemName();
404
405 if( aOverwrite )
406 {
407 auto& footprints = it->second;
408 footprints.erase( std::remove_if( footprints.begin(), footprints.end(),
409 [&fpName]( const std::unique_ptr<FOOTPRINT>& fp )
410 {
411 return fp->GetFPID().GetLibItemName().wx_str() == fpName;
412 } ),
413 footprints.end() );
414 }
415
416 FOOTPRINT* clone = static_cast<FOOTPRINT*>( aFootprint->Duplicate( IGNORE_PARENT_GROUP ) );
417 clone->SetParent( nullptr );
418
419 LIB_ID id = clone->GetFPID();
420 id.SetLibNickname( aNickname );
421 clone->SetFPID( id );
422
423 it->second.emplace_back( clone );
424 }
425 }
426
427 return SAVE_OK;
428 }
429 else
430 {
431 wxLogTrace( traceLibraries, "SaveFootprint: requested library %s not loaded", aNickname );
432 return SAVE_SKIPPED;
433 }
434}
435
436
437void FOOTPRINT_LIBRARY_ADAPTER::DeleteFootprint( const wxString& aNickname, const wxString& aFootprintName )
438{
439 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
440 {
441 try
442 {
443 pcbplugin( *lib )->FootprintDelete( getUri( ( *lib )->row ), aFootprintName );
444 }
445 catch( IO_ERROR& e )
446 {
447 wxLogTrace( traceLibraries, "DeleteFootprint: error deleting %s:%s: %s", aNickname,
448 aFootprintName, e.What() );
449 return;
450 }
451
452 {
453 std::unique_lock lock( PreloadedFootprintsMutex );
454 auto it = PreloadedFootprints.Get().find( aNickname );
455
456 if( it != PreloadedFootprints.Get().end() )
457 {
458 auto& footprints = it->second;
459 footprints.erase( std::remove_if( footprints.begin(), footprints.end(),
460 [&aFootprintName]( const std::unique_ptr<FOOTPRINT>& fp )
461 {
462 return fp->GetFPID().GetLibItemName().wx_str() == aFootprintName;
463 } ),
464 footprints.end() );
465 }
466 }
467 }
468 else
469 {
470 wxLogTrace( traceLibraries, "DeleteFootprint: requested library %s not loaded", aNickname );
471 }
472}
473
474
476{
477 {
478 std::shared_lock lock( m_librariesMutex );
479
480 if( auto it = m_libraries.find( aLib ); it != m_libraries.end() )
481 return it->second.plugin->IsLibraryWritable( getUri( it->second.row ) );
482 }
483
484 {
485 std::shared_lock lock( GlobalLibraryMutex );
486
487 if( auto it = GlobalLibraries.Get().find( aLib ); it != GlobalLibraries.Get().end() )
488 return it->second.plugin->IsLibraryWritable( getUri( it->second.row ) );
489 }
490
491 return false;
492}
493
494
496{
498
499 if( type == PCB_IO_MGR::NESTED_TABLE )
500 {
501 wxString msg;
502 wxFileName fileName( row->URI() );
503
504 if( fileName.FileExists() )
505 return tl::unexpected( LIBRARY_TABLE_OK() );
506 else
507 msg = wxString::Format( _( "Nested table '%s' not found." ), row->URI() );
508
509 return tl::unexpected( LIBRARY_ERROR( msg ) );
510 }
511 else if( type == PCB_IO_MGR::PCB_FILE_UNKNOWN )
512 {
513 wxLogTrace( traceLibraries, "FP: Plugin type %s is unknown!", row->Type() );
514 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
515 return tl::unexpected( LIBRARY_ERROR( msg ) );
516 }
517
519 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
520
521 wxLogTrace( traceLibraries, "FP: Library %s (%s) plugin created", row->Nickname(),
522 magic_enum::enum_name( row->Scope() ) );
523
524 return plugin;
525}
526
527
529{
530 // Note: can't use dynamic_cast across compile units on Mac
531 wxCHECK( aRow->plugin->IsPCB_IO(), nullptr );
532 PCB_IO* ret = static_cast<PCB_IO*>( aRow->plugin.get() );
533 return ret;
534}
const char * name
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
bool IsFootprintLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
static std::shared_mutex GlobalLibraryMutex
void DeleteFootprint(const wxString &aNickname, const wxString &aFootprintName)
Deletes the aFootprintName from the library given by aNickname.
std::vector< FOOTPRINT * > GetFootprints(const wxString &aNickname, bool aBestEfforts=false)
Retrieves a list of footprints contained in a given loaded library.
void RefreshLibraryIfChanged(const wxString &aNickname)
Checks whether the library on disk has changed since it was last enumerated into PreloadedFootprints ...
void enumerateLibrary(LIB_DATA *aLib, const wxString &aUri) override
Override in derived class to perform library-specific enumeration.
IO_BASE * plugin(const LIB_DATA *aRow) override
SAVE_T SaveFootprint(const wxString &aNickname, const FOOTPRINT *aFootprint, bool aOverwrite=true)
Write aFootprint to an existing library given by aNickname.
std::vector< wxString > GetFootprintNames(const wxString &aNickname, bool aBestEfforts=false)
Retrieves a list of footprint names contained in a given loaded library.
FOOTPRINT * LoadFootprint(const wxString &aNickname, const wxString &aName, bool aKeepUUID)
Load a FOOTPRINT having aName from the library given by aNickname.
static LEAK_AT_EXIT< std::map< wxString, LIB_DATA > > GlobalLibraries
static PCB_IO * pcbplugin(const LIB_DATA *aRow)
FOOTPRINT * LoadFootprintWithOptionalNickname(const LIB_ID &aFootprintId, bool aKeepUUID)
Load a footprint having aFootprintId with possibly an empty nickname.
std::map< wxString, long long > m_preloadedTimestamps
Per-library filesystem timestamps recorded when PreloadedFootprints was last populated.
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
FOOTPRINT_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
long long GenerateTimestamp(const wxString *aNickname)
Generates a filesystem timestamp / hash value for library(ies)
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
SAVE_T
The set of return values from SaveSymbol() below.
static LEAK_AT_EXIT< std::map< wxString, std::vector< std::unique_ptr< FOOTPRINT > > > > PreloadedFootprints
Storage for preloaded footprints, indexed by library nickname.
bool FootprintExists(const wxString &aNickname, const wxString &aName)
static std::shared_mutex PreloadedFootprintsMutex
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:352
wxString GetFPIDAsString() const
Definition footprint.h:357
const LIB_ID & GetFPID() const
Definition footprint.h:351
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
virtual bool IsPCB_IO() const
Work-around for lack of dynamic_cast across compile units on Mac.
Definition io_base.h:84
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()
A wrapper for static data that should not be destroyed at program exit.
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
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::shared_mutex m_librariesMutex
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
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)
static wxString getUri(const LIBRARY_TABLE_ROW *aRow)
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
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...
LIBRARY_TABLE_SCOPE Scope() const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Type() const
const wxString & URI() const
const wxString & Nickname() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:100
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
static PCB_FILE_T EnumFromStr(const wxString &aFileType)
Return the PCB_FILE_T from the corresponding plugin type name: "kicad", "legacy", etc.
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:56
@ PCB_FILE_UNKNOWN
0 is not a legal menu id on Mac
Definition pcb_io_mgr.h:57
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
A base class that BOARD loading and saving plugins should derive from.
Definition pcb_io.h:70
virtual void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr)
Return a list of footprint names contained within the library at aLibraryPath.
Definition pcb_io.cpp:95
virtual bool FootprintExists(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Check for the existence of a footprint.
Definition pcb_io.cpp:136
virtual void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Delete aFootprintName from the library at aLibraryPath.
Definition pcb_io.cpp:160
virtual FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
Definition pcb_io.cpp:144
virtual void FootprintSave(const wxString &aLibraryPath, const FOOTPRINT *aFootprint, const std::map< std::string, UTF8 > *aProperties=nullptr)
Write aFootprint to an existing library located at aLibraryPath.
Definition pcb_io.cpp:152
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:71
#define _(s)
#define IGNORE_PARENT_GROUP
Definition eda_item.h:56
Functions related to environment variables, including help functions.
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
Storage for an actual loaded library (including library content owned by the plugin)
LIB_STATUS status
std::unique_ptr< IO_BASE > plugin
const LIBRARY_TABLE_ROW * row
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.