182 std::vector<std::unique_ptr<FOOTPRINT>> aDefinitions,
183 const wxString& aCacheNickname,
184 const std::vector<wxString>& aSourceLibNicknames )
194 if( fp->GetFPID().GetLibItemName().empty() && !fp->GetFPID().GetLibNickname().empty() )
199 using SOURCE_KEY = std::pair<wxString, wxString>;
201 std::map<SOURCE_KEY, FOOTPRINT*> defByKey;
202 std::map<wxString, std::vector<FOOTPRINT*>> defsByName;
204 for(
const std::unique_ptr<FOOTPRINT>& def : aDefinitions )
206 wxString
name = def->GetFPID().GetUniStringLibItemName();
211 defByKey.emplace( SOURCE_KEY( def->GetFPID().GetUniStringLibNickname(),
name ), def.get() );
212 defsByName[
name].push_back( def.get() );
217 auto findDef = [&](
const wxString& aNick,
const wxString& aName ) ->
FOOTPRINT*
219 if(
auto it = defByKey.find( SOURCE_KEY( aNick, aName ) ); it != defByKey.end() )
222 auto byName = defsByName.find( aName );
224 if( byName == defsByName.end() || byName->second.size() != 1 )
227 return byName->second.front();
231 for(
const wxString& nick : aSourceLibNicknames )
237 std::set<wxString> provenance( aSourceLibNicknames.begin(), aSourceLibNicknames.end() );
240 auto resolveSource = [&](
const FOOTPRINT* aFp,
const wxString& aName ) -> wxString
245 if( !ownNick.IsEmpty() )
248 for(
const wxString& nick : aSourceLibNicknames )
250 if( nick != ownNick )
254 std::vector<wxString> matches;
264 if( provenance.count( nick ) )
271 exists =
m_adapter.FootprintExists( nick, aName );
282 FOOTPRINT* def = findDef( ownNick, aName );
288 std::unique_ptr<FOOTPRINT> candidate(
m_adapter.LoadFootprint( nick, aName,
true ) );
290 if( !candidate || !sameInterface( *candidate, *def ) )
294 matches.push_back( nick );
297 return matches.size() == 1 ? matches.front() : wxString( wxEmptyString );
301 std::map<SOURCE_KEY, wxString> targetByKey;
302 std::set<SOURCE_KEY> cacheKeys;
303 std::map<SOURCE_KEY, std::vector<FOOTPRINT*>> instancesByKey;
307 wxString
name = fp->GetFPID().GetUniStringLibItemName();
312 SOURCE_KEY key( fp->GetFPID().GetUniStringLibNickname(),
name );
314 instancesByKey[key].push_back( fp );
316 if( targetByKey.count( key ) )
319 wxString sourceNick = resolveSource( fp,
name );
320 targetByKey[key] = sourceNick;
322 if( sourceNick.IsEmpty() )
323 cacheKeys.insert( key );
328 std::set<wxString> takenFiles;
330 auto uniqueName = [&takenFiles](
const wxString& aName )
332 for(
int suffix = 0; ; ++suffix )
334 wxString candidate = suffix ? wxString::Format( wxS(
"%s_%d" ), aName, suffix ) : aName;
339 wxString fileName = candidate;
340 fileName.MakeLower();
342 if( takenFiles.insert( fileName ).second )
348 std::map<wxString, FOOTPRINT*> cacheDefs;
349 std::map<SOURCE_KEY, wxString> cacheNameByKey;
350 std::map<const FOOTPRINT*, wxString> cacheNameByDef;
351 std::vector<std::unique_ptr<FOOTPRINT>> placedDefs;
352 std::vector<wxString> renameReports;
354 std::map<const FOOTPRINT*, wxString> cacheNameByInstance;
356 for(
const SOURCE_KEY& key : cacheKeys )
358 const wxString&
name = key.second;
365 POSE_SIGNATURE m_Signature;
366 wxString m_CacheName;
369 std::vector<ITEM> items;
372 items.push_back( { def, poseSignature( *def ), wxString() } );
374 for(
FOOTPRINT* instance : instancesByKey[key] )
376 POSE_SIGNATURE signature = poseSignature( *instance );
378 auto match = std::find_if( items.begin(), items.end(),
379 [&](
const ITEM& aItem )
381 return aItem.m_Signature == signature
382 && !instance->FootprintNeedsUpdate(
383 aItem.m_Def, BOARD_ITEM::COMPARE_FLAGS::DRC )
384 && samePadOverrides( *instance, *aItem.m_Def );
387 if( match == items.end() )
389 placedDefs.emplace_back(
static_cast<FOOTPRINT*
>( instance->Clone() ) );
390 items.push_back( { placedDefs.back().get(), signature, wxString() } );
391 match = items.end() - 1;
395 if( match->m_CacheName.IsEmpty() )
398 auto known = cacheNameByDef.find( match->m_Def );
400 if( known != cacheNameByDef.end() )
402 match->m_CacheName = known->second;
406 match->m_CacheName = uniqueName(
name );
408 cacheNameByDef[match->m_Def] = match->m_CacheName;
409 cacheDefs[match->m_CacheName] = match->m_Def;
413 cacheNameByInstance[instance] = match->m_CacheName;
416 if( items.size() > 1 && !instancesByKey[key].empty() )
418 m_reporter.Report( wxString::Format(
_(
"Imported footprint '%s' has %zu differing placed "
419 "versions; each is kept as its own library item." ),
420 name, items.size() ),
425 if( def && cacheNameByDef.find( def ) == cacheNameByDef.end() )
427 wxString cacheName = uniqueName(
name );
429 cacheNameByDef[def] = cacheName;
430 cacheDefs[cacheName] = def;
433 if( !instancesByKey[key].
empty() )
434 cacheNameByKey[key] = cacheNameByInstance[instancesByKey[key].front()];
436 cacheNameByKey[key] = cacheNameByDef[def];
438 if( cacheNameByKey.count( key ) && cacheNameByKey[key] !=
name )
440 renameReports.push_back(
441 wxString::Format(
_(
"Imported footprint '%s' from '%s' was renamed to '%s' "
442 "in the import cache." ),
name, key.first, cacheNameByKey[key] ) );
447 if( !cacheDefs.empty() )
451 if( !
result.m_cacheNickname.IsEmpty() )
453 for(
const wxString& report : renameReports )
460 LIB_ID fpid = fp->GetFPID();
467 auto it = targetByKey.find( key );
469 if( it == targetByKey.end() )
476 if( it->second.IsEmpty() )
478 auto cacheName = cacheNameByInstance.find( fp );
480 if( cacheName == cacheNameByInstance.end() )
482 auto byKey = cacheNameByKey.find( key );
484 if( byKey != cacheNameByKey.end() )
485 cacheName = cacheNameByInstance.emplace( fp, byKey->second ).first;
488 if(
result.m_cacheNickname.IsEmpty() || cacheName == cacheNameByInstance.end() )
503 result.m_linkedToSource++;
512 const wxString& aCacheNickname,
const std::map<wxString, FOOTPRINT*>& aCacheDefs,
516 wxString finalPath = finalFn.GetFullPath();
517 wxString tempPath = finalPath + wxS(
".tmp" );
522 if( existingRow && !isManagedCache( existingRow ) )
524 m_reporter.Report( wxString::Format(
_(
"A footprint library named '%s' is already "
525 "registered; leaving imported footprints "
526 "unresolved." ), aCacheNickname ),
535 m_reporter.Report(
_(
"Cannot reconcile imported footprints: no KiCad footprint "
541 auto safeDelete = [&pi](
const wxString& aPath )
545 pi->DeleteLibrary( aPath );
556 if( wxDir::Exists( tempPath ) )
557 pi->DeleteLibrary( tempPath );
559 pi->CreateLibrary( tempPath );
562 std::map<std::string, UTF8> properties { {
"skip_cache_validation",
"" } };
565 for(
const auto& [
name, def] : aCacheDefs )
567 LIB_ID id = def->GetFPID();
570 id.SetLibItemName(
name );
572 def->SetReference( wxS(
"REF**" ) );
573 pi->FootprintSave( tempPath, def, &properties );
580 m_reporter.Report( wxString::Format(
_(
"Error writing imported footprint cache "
581 "'%s': %s" ), aCacheNickname, ioe.
What() ),
587 if( wxDir::Exists( tempPath ) )
588 safeDelete( tempPath );
594 if( wxDir::Exists( finalPath ) )
596 if( isManagedCache( existingRow ) )
598 safeDelete( finalPath );
602 m_reporter.Report( wxString::Format(
_(
"A library already exists at '%s'; leaving "
603 "imported footprints unresolved." ),
606 safeDelete( tempPath );
611 if( !wxRenameFile( tempPath, finalPath,
false ) )
613 m_reporter.Report( wxString::Format(
_(
"Could not publish imported footprint cache to "
614 "'%s'." ), finalPath ),
616 safeDelete( tempPath );