96 std::vector<std::unique_ptr<FOOTPRINT>> aDefinitions,
97 const wxString& aCacheNickname,
98 const std::vector<wxString>& aSourceLibNicknames )
106 using SOURCE_KEY = std::pair<wxString, wxString>;
108 std::map<SOURCE_KEY, FOOTPRINT*> defByKey;
109 std::map<wxString, std::vector<FOOTPRINT*>> defsByName;
111 for(
const std::unique_ptr<FOOTPRINT>& def : aDefinitions )
113 wxString
name = def->GetFPID().GetUniStringLibItemName();
118 defByKey.emplace( SOURCE_KEY( def->GetFPID().GetUniStringLibNickname(),
name ), def.get() );
119 defsByName[
name].push_back( def.get() );
124 auto findDef = [&](
const wxString& aNick,
const wxString& aName ) ->
FOOTPRINT*
126 if(
auto it = defByKey.find( SOURCE_KEY( aNick, aName ) ); it != defByKey.end() )
129 auto byName = defsByName.find( aName );
131 if( byName == defsByName.end() || byName->second.size() != 1 )
134 return byName->second.front();
138 for(
const wxString& nick : aSourceLibNicknames )
144 std::set<wxString> provenance( aSourceLibNicknames.begin(), aSourceLibNicknames.end() );
147 auto resolveSource = [&](
const FOOTPRINT* aFp,
const wxString& aName ) -> wxString
149 std::vector<wxString> candidates;
152 if( !ownNick.IsEmpty() )
153 candidates.push_back( ownNick );
155 for(
const wxString& nick : aSourceLibNicknames )
157 if( nick != ownNick )
158 candidates.push_back( nick );
161 std::vector<wxString> matches;
163 for(
const wxString& nick : candidates )
171 if( provenance.count( nick ) )
173 if( !
m_adapter.FootprintExists( nick, aName ) )
178 FOOTPRINT* def = findDef( ownNick, aName );
184 std::unique_ptr<FOOTPRINT> candidate(
m_adapter.LoadFootprint( nick, aName,
true ) );
186 if( !candidate || !sameInterface( *candidate, *def ) )
190 matches.push_back( nick );
193 return matches.size() == 1 ? matches.front() : wxString( wxEmptyString );
197 std::map<SOURCE_KEY, wxString> targetByKey;
198 std::set<SOURCE_KEY> cacheKeys;
199 std::map<SOURCE_KEY, std::vector<FOOTPRINT*>> instancesByKey;
203 wxString
name = fp->GetFPID().GetUniStringLibItemName();
208 SOURCE_KEY key( fp->GetFPID().GetUniStringLibNickname(),
name );
210 instancesByKey[key].push_back( fp );
212 if( targetByKey.count( key ) )
215 wxString sourceNick = resolveSource( fp,
name );
216 targetByKey[key] = sourceNick;
218 if( sourceNick.IsEmpty() )
219 cacheKeys.insert( key );
224 std::set<wxString> takenFiles;
226 auto uniqueName = [&takenFiles](
const wxString& aName )
228 for(
int suffix = 0; ; ++suffix )
230 wxString candidate = suffix ? wxString::Format( wxS(
"%s_%d" ), aName, suffix ) : aName;
231 wxString fileName = candidate;
234 fileName.MakeLower();
236 if( takenFiles.insert( fileName ).second )
242 std::map<wxString, FOOTPRINT*> cacheDefs;
243 std::map<SOURCE_KEY, wxString> cacheNameByKey;
244 std::map<const FOOTPRINT*, wxString> cacheNameByDef;
245 std::vector<std::unique_ptr<FOOTPRINT>> placedDefs;
246 std::vector<wxString> renameReports;
248 for(
const SOURCE_KEY& key : cacheKeys )
250 const wxString&
name = key.second;
255 const std::vector<FOOTPRINT*>& instances = instancesByKey[key];
257 if( instances.empty() )
260 wxString firstSig = placedSignature( instances.front() );
262 for(
auto it = instances.begin() + 1; it != instances.end(); ++it )
264 if( placedSignature( *it ) != firstSig )
266 m_reporter.Report( wxString::Format(
_(
"Imported footprint '%s' has "
267 "conflicting placed definitions; "
268 "keeping the first." ),
name ),
274 placedDefs.emplace_back(
static_cast<FOOTPRINT*
>( instances.front()->Clone() ) );
275 def = placedDefs.back().get();
279 if(
auto it = cacheNameByDef.find( def ); it != cacheNameByDef.end() )
281 cacheNameByKey[key] = it->second;
285 wxString cacheName = uniqueName(
name );
287 cacheNameByDef[def] = cacheName;
288 cacheNameByKey[key] = cacheName;
289 cacheDefs[cacheName] = def;
291 if( cacheName !=
name )
293 renameReports.push_back(
294 wxString::Format(
_(
"Imported footprint '%s' from '%s' was renamed to '%s' "
295 "because another library supplies a different footprint "
296 "of that name." ),
name, key.first, cacheName ) );
301 if( !cacheDefs.empty() )
305 if( !
result.m_cacheNickname.IsEmpty() )
307 for(
const wxString& report : renameReports )
314 LIB_ID fpid = fp->GetFPID();
321 auto it = targetByKey.find( key );
323 if( it == targetByKey.end() )
330 if( it->second.IsEmpty() )
332 auto cacheName = cacheNameByKey.find( key );
334 if(
result.m_cacheNickname.IsEmpty() || cacheName == cacheNameByKey.end() )
349 result.m_linkedToSource++;
358 const wxString& aCacheNickname,
const std::map<wxString, FOOTPRINT*>& aCacheDefs,
362 wxString finalPath = finalFn.GetFullPath();
363 wxString tempPath = finalPath + wxS(
".tmp" );
368 if( existingRow && !isManagedCache( existingRow ) )
370 m_reporter.Report( wxString::Format(
_(
"A footprint library named '%s' is already "
371 "registered; leaving imported footprints "
372 "unresolved." ), aCacheNickname ),
381 m_reporter.Report(
_(
"Cannot reconcile imported footprints: no KiCad footprint "
387 auto safeDelete = [&pi](
const wxString& aPath )
391 pi->DeleteLibrary( aPath );
402 if( wxDir::Exists( tempPath ) )
403 pi->DeleteLibrary( tempPath );
405 pi->CreateLibrary( tempPath );
408 std::map<std::string, UTF8> properties { {
"skip_cache_validation",
"" } };
411 for(
const auto& [
name, def] : aCacheDefs )
413 LIB_ID id = def->GetFPID();
416 id.SetLibItemName(
name );
418 def->SetReference( wxS(
"REF**" ) );
419 pi->FootprintSave( tempPath, def, &properties );
426 m_reporter.Report( wxString::Format(
_(
"Error writing imported footprint cache "
427 "'%s': %s" ), aCacheNickname, ioe.
What() ),
433 if( wxDir::Exists( tempPath ) )
434 safeDelete( tempPath );
440 if( wxDir::Exists( finalPath ) )
442 if( isManagedCache( existingRow ) )
444 safeDelete( finalPath );
448 m_reporter.Report( wxString::Format(
_(
"A library already exists at '%s'; leaving "
449 "imported footprints unresolved." ),
452 safeDelete( tempPath );
457 if( !wxRenameFile( tempPath, finalPath,
false ) )
459 m_reporter.Report( wxString::Format(
_(
"Could not publish imported footprint cache to "
460 "'%s'." ), finalPath ),
462 safeDelete( tempPath );
wxString result
Test unit parsing edge cases and error handling.