94 std::vector<std::unique_ptr<FOOTPRINT>> aDefinitions,
95 const wxString& aCacheNickname,
96 const std::vector<wxString>& aSourceLibNicknames )
103 std::map<wxString, FOOTPRINT*> defByName;
105 for(
const std::unique_ptr<FOOTPRINT>& def : aDefinitions )
107 wxString
name = def->GetFPID().GetUniStringLibItemName();
109 if( !
name.IsEmpty() )
110 defByName.emplace(
name, def.get() );
114 for(
const wxString& nick : aSourceLibNicknames )
120 std::set<wxString> provenance( aSourceLibNicknames.begin(), aSourceLibNicknames.end() );
123 auto resolveSource = [&](
const FOOTPRINT* aFp,
const wxString& aName ) -> wxString
125 std::vector<wxString> candidates;
128 if( !ownNick.IsEmpty() )
129 candidates.push_back( ownNick );
131 for(
const wxString& nick : aSourceLibNicknames )
133 if( nick != ownNick )
134 candidates.push_back( nick );
137 std::vector<wxString> matches;
139 for(
const wxString& nick : candidates )
147 if( provenance.count( nick ) )
149 if( !
m_adapter.FootprintExists( nick, aName ) )
154 auto def = defByName.find( aName );
156 if( def == defByName.end() )
160 std::unique_ptr<FOOTPRINT> candidate(
m_adapter.LoadFootprint( nick, aName,
true ) );
162 if( !candidate || !sameInterface( *candidate, *def->second ) )
166 matches.push_back( nick );
169 return matches.size() == 1 ? matches.front() : wxString( wxEmptyString );
174 std::map<wxString, wxString> targetByKey;
175 std::set<wxString> cacheNames;
176 std::map<wxString, std::vector<FOOTPRINT*>> instancesByName;
178 auto keyOf = [](
const wxString& aNick,
const wxString& aName )
180 return aNick + wxS(
"\x1f" ) + aName;
185 wxString
name = fp->GetFPID().GetUniStringLibItemName();
190 instancesByName[
name].push_back( fp );
192 wxString key = keyOf( fp->GetFPID().GetUniStringLibNickname(),
name );
194 if( targetByKey.count( key ) )
197 wxString sourceNick = resolveSource( fp,
name );
198 targetByKey[key] = sourceNick;
200 if( sourceNick.IsEmpty() )
201 cacheNames.insert(
name );
205 std::map<wxString, FOOTPRINT*> cacheDefs;
206 std::vector<std::unique_ptr<FOOTPRINT>> placedDefs;
208 for(
const wxString&
name : cacheNames )
210 if(
auto it = defByName.find(
name ); it != defByName.end() )
212 cacheDefs[
name] = it->second;
216 const std::vector<FOOTPRINT*>& instances = instancesByName[
name];
218 if( instances.empty() )
221 wxString firstSig = placedSignature( instances.front() );
223 for(
auto it = instances.begin() + 1; it != instances.end(); ++it )
225 if( placedSignature( *it ) != firstSig )
227 m_reporter.Report( wxString::Format(
_(
"Imported footprint '%s' has conflicting "
228 "placed definitions; keeping the first." ),
235 placedDefs.emplace_back(
static_cast<FOOTPRINT*
>( instances.front()->Clone() ) );
236 cacheDefs[
name] = placedDefs.back().get();
240 if( !cacheDefs.empty() )
246 LIB_ID fpid = fp->GetFPID();
254 if( it == targetByKey.end() )
261 if( it->second.IsEmpty() )
263 if(
result.m_cacheNickname.IsEmpty() )
277 result.m_linkedToSource++;
286 const wxString& aCacheNickname,
const std::map<wxString, FOOTPRINT*>& aCacheDefs,
290 wxString finalPath = finalFn.GetFullPath();
291 wxString tempPath = finalPath + wxS(
".tmp" );
296 if( existingRow && !isManagedCache( existingRow ) )
298 m_reporter.Report( wxString::Format(
_(
"A footprint library named '%s' is already "
299 "registered; leaving imported footprints "
300 "unresolved." ), aCacheNickname ),
309 m_reporter.Report(
_(
"Cannot reconcile imported footprints: no KiCad footprint "
315 auto safeDelete = [&pi](
const wxString& aPath )
319 pi->DeleteLibrary( aPath );
330 if( wxDir::Exists( tempPath ) )
331 pi->DeleteLibrary( tempPath );
333 pi->CreateLibrary( tempPath );
336 std::map<std::string, UTF8> properties { {
"skip_cache_validation",
"" } };
339 for(
const auto& [
name, def] : aCacheDefs )
341 LIB_ID id = def->GetFPID();
345 def->SetReference( wxS(
"REF**" ) );
346 pi->FootprintSave( tempPath, def, &properties );
353 m_reporter.Report( wxString::Format(
_(
"Error writing imported footprint cache "
354 "'%s': %s" ), aCacheNickname, ioe.
What() ),
360 if( wxDir::Exists( tempPath ) )
361 safeDelete( tempPath );
367 if( wxDir::Exists( finalPath ) )
369 if( isManagedCache( existingRow ) )
371 safeDelete( finalPath );
375 m_reporter.Report( wxString::Format(
_(
"A library already exists at '%s'; leaving "
376 "imported footprints unresolved." ),
379 safeDelete( tempPath );
384 if( !wxRenameFile( tempPath, finalPath,
false ) )
386 m_reporter.Report( wxString::Format(
_(
"Could not publish imported footprint cache to "
387 "'%s'." ), finalPath ),
389 safeDelete( tempPath );
446 PROJECT& aProject,
const wxString& aBoardPath,
447 const std::map<std::string, UTF8>* aProperties,
REPORTER& aReporter )
459 std::vector<wxString> sourceLibs;
462 if( cacheNick.IsEmpty() )
470 result = reconciler.
Reconcile( &aBoard, std::move( aDefinitions ), cacheNick, sourceLibs );
474 aReporter.
Report( wxString::Format(
_(
"Could not reconcile imported footprint libraries: "
wxString result
Test unit parsing edge cases and error handling.