Files
kicad-source-mirror/common/design_block_library_adapter.cpp
T
Seth Hillbrand ca7259ab95 Add LEAK_AT_EXIT wrapper to skip destructor calls at program exit
GlobalLibraries and PreloadedFootprints each have deep object
hierarchies that take noticeable time to destroy during shutdown.
Since the OS reclaims all process memory anyway, running these
destructors is wasted work. The new LEAK_AT_EXIT template wrapper
heap-allocates the wrapped object on first access and never frees it,
speeding up program exit.

When built with address sanitizer, it uses __lsan_ignore_object to
prevent false positive leak reports.  Also added a valgrind
suppression file for the same purpose.
2026-01-23 17:26:37 -08:00

325 lines
10 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* @author Jon Evans <jon@craftyjon.com>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "design_block.h"
#include <design_block_library_adapter.h>
#include <design_block_io.h>
#include <env_vars.h>
#include <ki_exception.h>
#include <trace_helpers.h>
#include <magic_enum.hpp>
#include <wx/log.h>
LEAK_AT_EXIT<std::map<wxString, LIB_DATA>> DESIGN_BLOCK_LIBRARY_ADAPTER::GlobalLibraries;
std::shared_mutex DESIGN_BLOCK_LIBRARY_ADAPTER::GlobalLibraryMutex;
DESIGN_BLOCK_LIBRARY_ADAPTER::DESIGN_BLOCK_LIBRARY_ADAPTER( LIBRARY_MANAGER& aManager ) :
LIBRARY_MANAGER_ADAPTER( aManager )
{
}
wxString DESIGN_BLOCK_LIBRARY_ADAPTER::GlobalPathEnvVariableName()
{
return ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) );
}
DESIGN_BLOCK_IO* DESIGN_BLOCK_LIBRARY_ADAPTER::dbplugin( const LIB_DATA* aRow )
{
DESIGN_BLOCK_IO* ret = dynamic_cast<DESIGN_BLOCK_IO*>( aRow->plugin.get() );
wxCHECK( aRow->plugin && ret, nullptr );
return ret;
}
LIBRARY_RESULT<IO_BASE*> DESIGN_BLOCK_LIBRARY_ADAPTER::createPlugin( const LIBRARY_TABLE_ROW* row )
{
// TODO(JE) do we maintain a precondition that URIs are absolute paths after expansion?
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T type = DESIGN_BLOCK_IO_MGR::EnumFromStr( row->Type() );
if( type == DESIGN_BLOCK_IO_MGR::NESTED_TABLE )
{
wxString msg;
wxFileName fileName( row->URI() );
if( fileName.FileExists() )
return tl::unexpected( LIBRARY_TABLE_OK() );
else
msg = wxString::Format( _( "Nested table '%s' not found." ), row->URI() );
return tl::unexpected( LIBRARY_ERROR( msg ) );
}
else if( type == DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_UNKNOWN )
{
wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
return tl::unexpected( LIBRARY_ERROR( msg ) );
}
DESIGN_BLOCK_IO* plugin = DESIGN_BLOCK_IO_MGR::FindPlugin( type );
wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
return plugin;
}
IO_BASE* DESIGN_BLOCK_LIBRARY_ADAPTER::plugin( const LIB_DATA* aRow )
{
return dbplugin( aRow );
}
void DESIGN_BLOCK_LIBRARY_ADAPTER::enumerateLibrary( LIB_DATA* aLib )
{
wxArrayString dummyList;
std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
dbplugin( aLib )->DesignBlockEnumerate( dummyList, getUri( aLib->row ), false, &options );
}
std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
{
aLib->status.load_status = LOAD_STATUS::LOADING;
std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
try
{
wxArrayString dummyList;
dbplugin( aLib )->DesignBlockEnumerate( dummyList, getUri( aLib->row ), false, &options );
wxLogTrace( traceLibraries, "DB: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
aLib->status.load_status = LOAD_STATUS::LOADED;
}
catch( IO_ERROR& e )
{
aLib->status.load_status = LOAD_STATUS::LOAD_ERROR;
aLib->status.error = LIBRARY_ERROR( { e.What() } );
wxLogTrace( traceLibraries, "DB: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
}
return aLib->status;
}
std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
{
LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
if( result.has_value() )
return LoadOne( *result );
return LIB_STATUS{
.load_status = LOAD_STATUS::LOAD_ERROR,
.error = LIBRARY_ERROR( { result.error() } )
};
}
std::vector<DESIGN_BLOCK*> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlocks( const wxString& aNickname )
{
std::vector<DESIGN_BLOCK*> blocks;
std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
if( !maybeLib )
return blocks;
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
wxArrayString blockNames;
try
{
dbplugin( lib )->DesignBlockEnumerate( blockNames, getUri( lib->row ), false, &options );
}
catch( IO_ERROR& e )
{
wxLogTrace( traceLibraries, "DB: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
}
for( const wxString& blockName : blockNames )
{
try
{
blocks.emplace_back( dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), blockName, false, &options ) );
}
catch( IO_ERROR& e )
{
wxLogTrace( traceLibraries, "DB: Exception enumerating design block %s: %s", blockName, e.What() );
}
}
return blocks;
}
std::vector<wxString> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlockNames( const wxString& aNickname )
{
// TODO(JE) can we kill wxArrayString in internal API?
wxArrayString namesAS;
std::vector<wxString> names;
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
dbplugin( lib )->DesignBlockEnumerate( namesAS, getUri( lib->row ), true, &options );
}
for( const wxString& name : namesAS )
names.emplace_back( name );
return names;
}
DESIGN_BLOCK* DESIGN_BLOCK_LIBRARY_ADAPTER::LoadDesignBlock( const wxString& aNickname,
const wxString& aDesignBlockName, bool aKeepUUID )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
try
{
if( DESIGN_BLOCK* db =
dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), aDesignBlockName, aKeepUUID, &options ) )
{
db->GetLibId().SetLibNickname( aNickname );
return db;
}
}
catch( const IO_ERROR& ioe )
{
wxLogTrace( traceLibraries, "LoadDesignBlock: error loading %s:%s: %s", aNickname, aDesignBlockName,
ioe.What() );
}
}
else
{
wxLogTrace( traceLibraries, "LoadDesignBlock: requested library %s not loaded", aNickname );
}
return nullptr;
}
bool DESIGN_BLOCK_LIBRARY_ADAPTER::DesignBlockExists( const wxString& aNickname, const wxString& aDesignBlockName )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
return dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlockName, &options );
}
return false;
}
const DESIGN_BLOCK* DESIGN_BLOCK_LIBRARY_ADAPTER::GetEnumeratedDesignBlock( const wxString& aNickname,
const wxString& aDesignBlockName )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
return dbplugin( lib )->GetEnumeratedDesignBlock( getUri( lib->row ), aDesignBlockName, &options );
}
return nullptr;
}
DESIGN_BLOCK_LIBRARY_ADAPTER::SAVE_T DESIGN_BLOCK_LIBRARY_ADAPTER::SaveDesignBlock( const wxString& aNickname,
const DESIGN_BLOCK* aDesignBlock,
bool aOverwrite )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
if( !aOverwrite && dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlock->GetName(), &options ) )
return SAVE_SKIPPED;
dbplugin( lib )->DesignBlockSave( getUri( lib->row ), aDesignBlock, &options );
}
return SAVE_OK;
}
void DESIGN_BLOCK_LIBRARY_ADAPTER::DeleteDesignBlock( const wxString& aNickname,
const wxString& aDesignBlockName )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
return dbplugin( lib )->DesignBlockDelete( getUri( lib->row ), aDesignBlockName, &options );
}
}
bool DESIGN_BLOCK_LIBRARY_ADAPTER::IsDesignBlockLibWritable( const wxString& aNickname )
{
if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
{
const LIB_DATA* lib = *maybeLib;
return plugin( lib )->IsLibraryWritable( getUri( lib->row ) );
}
return false;
}
DESIGN_BLOCK* DESIGN_BLOCK_LIBRARY_ADAPTER::DesignBlockLoadWithOptionalNickname( const LIB_ID& aDesignBlockId,
bool aKeepUUID )
{
wxString nickname = aDesignBlockId.GetLibNickname();
wxString DesignBlockname = aDesignBlockId.GetLibItemName();
if( nickname.size() )
return LoadDesignBlock( nickname, DesignBlockname, aKeepUUID );
// nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
for( const wxString& library : GetLibraryNames() )
{
// DesignBlockLoad() returns NULL on not found, does not throw exception
// unless there's an IO_ERROR.
if( DESIGN_BLOCK* ret = LoadDesignBlock( library, DesignBlockname, aKeepUUID ) )
return ret;
}
return nullptr;
}