Merge branch 'master' into feature/issue-2147

This commit is contained in:
Matthias Koefferlein 2025-10-19 18:20:53 +02:00
commit 6d234d4ea5
24 changed files with 680 additions and 134 deletions

View File

@ -66,7 +66,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.1.4
uses: pypa/cibuildwheel@v3.2.0
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@ -81,7 +81,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.1.4
uses: pypa/cibuildwheel@v3.2.0
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}

View File

@ -145,9 +145,20 @@ Library::is_retired (const db::cell_index_type library_cell_index) const
return (i != m_refcount.end () && j != m_retired_count.end () && i->second == j->second);
}
void
Library::rename (const std::string &name)
{
if (name != get_name () && db::LibraryManager::initialized ()) {
db::LibraryManager::instance ().rename (get_id (), name);
}
}
void
Library::refresh ()
{
std::string name = reload ();
rename (name);
layout ().refresh ();
remap_to (this);
}
@ -271,6 +282,9 @@ Library::remap_to (db::Library *other)
// Do a cleanup later since the referrers now might have invalid proxy instances
for (std::set<db::Layout *>::const_iterator c = needs_cleanup.begin (); c != needs_cleanup.end (); ++c) {
(*c)->cleanup ();
// forces an update of the cell tree in the application - this will reflect the changed name
// of the library reference
(*c)->invalidate_hier ();
}
}

View File

@ -65,6 +65,20 @@ public:
*/
virtual ~Library ();
/**
* @brief Called to reload the library
*
* If the library is a file-based one, this method can be reimplemented to reload
* the file. This method must not change the name of the library, but return a new
* name in case it has changed.
*
* @return The new name of the library
*/
virtual std::string reload ()
{
return get_name ();
}
/**
* @brief The layout object
*
@ -211,6 +225,14 @@ public:
*/
void refresh ();
/**
* @brief Renames the library
*
* Unlike "set_name", this method will take care of properly re-registering the library
* under the new name.
*/
void rename (const std::string &name);
/**
* @brief Remap the library proxies to a different library
*

View File

@ -67,6 +67,39 @@ LibraryManager::~LibraryManager ()
clear ();
}
void
LibraryManager::rename (lib_id_type lib_id, const std::string &name)
{
db::Library *lib = 0;
{
tl::MutexLocker locker (&m_lock);
lib = lib_internal (lib_id);
if (! lib) {
return;
}
std::string org_name = lib->get_name ();
for (auto it = m_lib_by_name.find (org_name);it != m_lib_by_name.end () && it->first == org_name; ++it) {
if (it->second == lib_id) {
m_lib_by_name.erase (it);
break;
}
}
m_lib_by_name.insert (std::make_pair (name, lib_id));
lib->set_name (name);
}
// triggers a layout update
lib->remap_to (lib);
// issue the change notification
changed_event ();
}
std::pair<bool, lib_id_type>
LibraryManager::lib_by_name (const std::string &name, const std::set<std::string> &for_technologies) const
{
@ -112,6 +145,8 @@ LibraryManager::unregister_lib (Library *library)
return;
}
library->remap_to (0);
{
tl::MutexLocker locker (&m_lock);
@ -124,8 +159,10 @@ LibraryManager::unregister_lib (Library *library)
}
}
library->remap_to (0);
library->set_id (std::numeric_limits<lib_id_type>::max ());
// issue the change notification
changed_event ();
}
void
@ -244,6 +281,16 @@ LibraryManager::lib_internal (lib_id_type id) const
}
}
void
LibraryManager::refresh_all ()
{
for (std::vector<Library *>::iterator l = m_libs.begin (); l != m_libs.end (); ++l) {
if (*l) {
(*l)->refresh ();
}
}
}
void
LibraryManager::clear ()
{

View File

@ -91,6 +91,11 @@ public:
return m_lib_by_name.end ();
}
/**
* @brief Renames a library
*/
void rename (lib_id_type lib_id, const std::string &name);
/**
* @brief Get the library by name which is valid for all given technologies
*
@ -212,6 +217,11 @@ public:
*/
void clear ();
/**
* @brief Refreshes all libraries
*/
void refresh_all ();
private:
std::vector<Library *> m_libs;
lib_name_map m_lib_by_name;

View File

@ -974,6 +974,45 @@ static db::Library *library (const db::Cell *cell)
}
}
static void change_library_ref (db::Cell *cell, db::lib_id_type lib_id, db::cell_index_type cell_index)
{
db::LibraryProxy *l = dynamic_cast<db::LibraryProxy *> (cell);
if (! l) {
throw tl::Exception (tl::to_string (tr ("Cell is not a library reference - cannot change that reference")));
}
const db::Library *lib = db::LibraryManager::instance ().lib (lib_id);
if (! lib) {
throw tl::Exception (tl::to_string (tr ("'lib_id' is not a valid library ID")));
}
if (! lib->layout ().is_valid_cell_index (cell_index)) {
throw tl::Exception (tl::to_string (tr ("'cell_index' is not a valid cell index in the context of the library")));
}
l->remap (lib_id, cell_index);
}
static void change_library_ref2 (db::Cell *cell, const std::string &lib_name, const std::string &cell_name)
{
db::LibraryProxy *l = dynamic_cast<db::LibraryProxy *> (cell);
if (! l) {
throw tl::Exception (tl::to_string (tr ("Cell is not a library reference - cannot change that reference")));
}
db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (lib_name);
if (! lib) {
throw tl::Exception (tl::to_string (tr ("Not a valid library name: ")) + lib_name);
}
auto cbn = lib->layout ().cell_by_name (cell_name.c_str ());
if (! cbn.first) {
throw tl::Exception (tl::to_string (tr ("Not a valid cell name: ")) + cell_name);
}
l->remap (lib->get_id (), cbn.second);
}
static const db::Layout *layout_const (const db::Cell *cell)
{
return cell->layout ();
@ -3194,7 +3233,23 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"@brief Returns a reference to the library from which the cell is imported\n"
"if the cell is not imported from a library, this reference is nil.\n"
"\n"
"this method has been introduced in version 0.22.\n"
"This method has been introduced in version 0.22.\n"
) +
gsi::method_ext ("change_ref", &change_library_ref, gsi::arg ("lib_id"), gsi::arg ("lib_cell_index"),
"@brief Changes the reference to a different library cell\n"
"This method requires a cell that is a library reference (i.e. \\is_library_cell? is true). It will "
"change that reference to a new cell, potentially from a different library.\n"
"The library is given by library ID, the cell by cell inside inside that library.\n"
"\n"
"This method has been introduced in version 0.30.5.\n"
) +
gsi::method_ext ("change_ref", &change_library_ref2, gsi::arg ("lib_name"), gsi::arg ("cell_name"),
"@brief Changes the reference to a different library cell\n"
"This method requires a cell that is a library reference (i.e. \\is_library_cell? is true). It will "
"change that reference to a new cell, potentially from a different library.\n"
"This version takes a library name and cell name (from that library).\n"
"\n"
"This method has been introduced in version 0.30.5.\n"
) +
gsi::method_ext ("layout", &layout,
"@brief Returns a reference to the layout where the cell resides\n"

View File

@ -297,6 +297,20 @@ static void dump_mem_statistics (const db::Layout *layout, bool detailed)
ms.print ();
}
static void check_cell_index (const db::Layout *layout, db::cell_index_type ci)
{
if (! layout->is_valid_cell_index (ci)) {
throw tl::Exception (tl::to_string (tr ("Not a valid cell index: ")) + tl::to_string (ci));
}
}
static void check_layer (const db::Layout *layout, unsigned int layer)
{
if (! layout->is_valid_layer (layer) && ! layout->is_special_layer (layer)) {
throw tl::Exception (tl::to_string (tr ("Invalid layer index: ")) + tl::to_string (layer));
}
}
static bool layout_has_prop_id (const db::Layout *l)
{
return l->prop_id () != 0;
@ -630,30 +644,45 @@ static tl::Variant get_property_from_id (db::properties_id_type id, const tl::Va
static void
delete_cells (db::Layout *layout, const std::vector<db::cell_index_type> &cell_indices)
{
for (auto ci = cell_indices.begin (); ci != cell_indices.end (); ++ci) {
check_cell_index (layout, *ci);
}
layout->delete_cells (cell_indices.begin (), cell_indices.end ());
}
static void
delete_cell_rec (db::Layout *layout, db::cell_index_type cell_index)
{
check_cell_index (layout, cell_index);
layout->delete_cell_rec (cell_index);
}
static void
prune_cell (db::Layout *layout, db::cell_index_type cell_index, int levels)
{
check_cell_index (layout, cell_index);
layout->prune_cell (cell_index, levels);
}
static void
prune_subcells (db::Layout *layout, db::cell_index_type cell_index, int levels)
{
check_cell_index (layout, cell_index);
layout->prune_subcells (cell_index, levels);
}
static void
flatten (db::Layout *layout, db::cell_index_type cell_index, int levels, bool prune)
{
check_cell_index (layout, cell_index);
layout->flatten (layout->cell (cell_index), levels, prune);
}
static void
flatten_into (db::Layout *layout, db::cell_index_type cell_index, db::cell_index_type target_cell_index, const db::ICplxTrans &t, int levels)
{
check_cell_index (layout, cell_index);
check_cell_index (layout, target_cell_index);
layout->flatten (layout->cell (cell_index), layout->cell (target_cell_index), t, levels);
}
@ -698,20 +727,11 @@ write_bytes (db::Layout *layout, const db::SaveLayoutOptions &options)
return std::vector<char> (byte_stream.data (), byte_stream.data () + byte_stream.size ());
}
static void check_layer (const db::Layout *layout, unsigned int layer)
{
if (! layout->is_valid_layer (layer) && ! layout->is_special_layer (layer)) {
throw tl::Exception (tl::to_string (tr ("Invalid layer index")));
}
}
static db::RecursiveShapeIterator
begin_shapes (const db::Layout *layout, db::cell_index_type starting_cell, unsigned int layer)
{
if (! layout->is_valid_layer (layer)) {
throw tl::Exception (tl::to_string (tr ("Invalid layer index")));
}
check_layer (layout, layer);
check_cell_index (layout, starting_cell);
return db::RecursiveShapeIterator (*layout, layout->cell (starting_cell), layer);
}
@ -725,9 +745,7 @@ static db::RecursiveShapeIterator
begin_shapes_touching (const db::Layout *layout, db::cell_index_type starting_cell, unsigned int layer, db::Box region)
{
check_layer (layout, layer);
if (! layout->is_valid_cell_index (starting_cell)) {
throw tl::Exception (tl::to_string (tr ("Invalid cell index")));
}
check_cell_index (layout, starting_cell);
return db::RecursiveShapeIterator (*layout, layout->cell (starting_cell), layer, region, false);
}
@ -741,9 +759,7 @@ static db::RecursiveShapeIterator
begin_shapes_overlapping (const db::Layout *layout, db::cell_index_type starting_cell, unsigned int layer, db::Box region)
{
check_layer (layout, layer);
if (! layout->is_valid_cell_index (starting_cell)) {
throw tl::Exception (tl::to_string (tr ("Invalid cell index")));
}
check_cell_index (layout, starting_cell);
return db::RecursiveShapeIterator (*layout, layout->cell (starting_cell), layer, region, true);
}
@ -757,9 +773,7 @@ static db::RecursiveShapeIterator
begin_shapes_touching_um (const db::Layout *layout, db::cell_index_type starting_cell, unsigned int layer, db::DBox region)
{
check_layer (layout, layer);
if (! layout->is_valid_cell_index (starting_cell)) {
throw tl::Exception (tl::to_string (tr ("Invalid cell index")));
}
check_cell_index (layout, starting_cell);
return db::RecursiveShapeIterator (*layout, layout->cell (starting_cell), layer, db::CplxTrans (layout->dbu ()).inverted () * region, false);
}
@ -773,9 +787,7 @@ static db::RecursiveShapeIterator
begin_shapes_overlapping_um (const db::Layout *layout, db::cell_index_type starting_cell, unsigned int layer, db::DBox region)
{
check_layer (layout, layer);
if (! layout->is_valid_cell_index (starting_cell)) {
throw tl::Exception (tl::to_string (tr ("Invalid cell index")));
}
check_cell_index (layout, starting_cell);
return db::RecursiveShapeIterator (*layout, layout->cell (starting_cell), layer, db::CplxTrans (layout->dbu ()).inverted () * region, true);
}
@ -847,11 +859,21 @@ static std::vector<std::string> pcell_names (const db::Layout *layout)
return res;
}
static void delete_cell (db::Layout *ly, db::cell_index_type ci)
{
check_cell_index (ly, ci);
ly->delete_cell (ci);
}
static void rename_cell (db::Layout *ly, db::cell_index_type ci, const std::string &name)
{
check_cell_index (ly, ci);
ly->rename_cell (ci, name.c_str ());
}
static db::Cell *cell_from_index (db::Layout *ly, db::cell_index_type ci)
{
if (! ly->is_valid_cell_index (ci)) {
throw tl::Exception (tl::to_string (tr ("Not a valid cell index: ")) + tl::to_string (ci));
}
check_cell_index (ly, ci);
return &ly->cell (ci);
}
@ -1094,17 +1116,17 @@ static void set_properties (db::Layout *layout, unsigned int index, const db::La
}
}
void break_polygons2 (db::Layout *layout, unsigned int layer, size_t max_vertex_count, double max_area_ratio)
static void break_polygons2 (db::Layout *layout, unsigned int layer, size_t max_vertex_count, double max_area_ratio)
{
db::break_polygons (*layout, layer, max_vertex_count, max_area_ratio);
}
void break_polygons1 (db::Layout *layout, size_t max_vertex_count, double max_area_ratio)
static void break_polygons1 (db::Layout *layout, size_t max_vertex_count, double max_area_ratio)
{
db::break_polygons (*layout, max_vertex_count, max_area_ratio);
}
void delete_layer_from_info (db::Layout *layout, const db::LayerProperties &info)
static void delete_layer_from_info (db::Layout *layout, const db::LayerProperties &info)
{
int li = layout->get_layer_maybe (info);
if (li >= 0) {
@ -1112,7 +1134,7 @@ void delete_layer_from_info (db::Layout *layout, const db::LayerProperties &info
}
}
void clear_layer_from_info (db::Layout *layout, const db::LayerProperties &info)
static void clear_layer_from_info (db::Layout *layout, const db::LayerProperties &info)
{
int li = layout->get_layer_maybe (info);
if (li >= 0) {
@ -1120,7 +1142,7 @@ void clear_layer_from_info (db::Layout *layout, const db::LayerProperties &info)
}
}
void clear_layer_from_info_with_flags (db::Layout *layout, const db::LayerProperties &info, unsigned int flags)
static void clear_layer_from_info_with_flags (db::Layout *layout, const db::LayerProperties &info, unsigned int flags)
{
int li = layout->get_layer_maybe (info);
if (li >= 0) {
@ -1128,6 +1150,34 @@ void clear_layer_from_info_with_flags (db::Layout *layout, const db::LayerProper
}
}
static void insert_region (db::Layout *layout, db::cell_index_type cell_index, int layer_index, const db::Region &r)
{
check_cell_index (layout, cell_index);
check_layer (layout, layer_index);
layout->insert (cell_index, layer_index, r);
}
static void insert_edges (db::Layout *layout, db::cell_index_type cell_index, int layer_index, const db::Edges &e)
{
check_cell_index (layout, cell_index);
check_layer (layout, layer_index);
layout->insert (cell_index, layer_index, e);
}
static void insert_texts (db::Layout *layout, db::cell_index_type cell_index, int layer_index, const db::Texts &t)
{
check_cell_index (layout, cell_index);
check_layer (layout, layer_index);
layout->insert (cell_index, layer_index, t);
}
static void insert_edge_pairs (db::Layout *layout, db::cell_index_type cell_index, int layer_index, const db::EdgePairs &ep)
{
check_cell_index (layout, cell_index);
check_layer (layout, layer_index);
layout->insert (cell_index, layer_index, ep);
}
Class<db::Layout> decl_Layout ("db", "Layout",
gsi::constructor ("new", &layout_ctor_with_manager, gsi::arg ("manager"),
"@brief Creates a layout object attached to a manager\n"
@ -1584,13 +1634,13 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"From version 0.23 on this method is deprecated because another method exists which is more convenient because "
"is returns a \\Cell object (\\create_cell).\n"
) +
gsi::method ("rename_cell", &db::Layout::rename_cell, gsi::arg ("index"), gsi::arg ("name"),
gsi::method_ext ("rename_cell", &rename_cell, gsi::arg ("index"), gsi::arg ("name"),
"@brief Renames the cell with given index\n"
"The cell with the given index is renamed to the given name. NOTE: it is not ensured that the name is unique. "
"This method allows assigning identical names to different cells which usually breaks things.\n"
"Consider using \\unique_cell_name to generate truely unique names.\n"
) +
gsi::method ("delete_cell", &db::Layout::delete_cell, gsi::arg ("cell_index"),
gsi::method_ext ("delete_cell", &delete_cell, gsi::arg ("cell_index"),
"@brief Deletes a cell \n"
"\n"
"This deletes a cell but not the sub cells of the cell.\n"
@ -1641,7 +1691,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.20.\n"
) +
gsi::method ("delete_cell_rec", &db::Layout::delete_cell_rec, gsi::arg ("cell_index"),
gsi::method_ext ("delete_cell_rec", &delete_cell_rec, gsi::arg ("cell_index"),
"@brief Deletes a cell plus all subcells\n"
"\n"
"This deletes a cell and also all sub cells of the cell.\n"
@ -1651,7 +1701,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.20.\n"
) +
gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::Region &)) &db::Layout::insert,
gsi::method_ext ("insert", &insert_region,
gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"),
"@brief Inserts a region into the given cell and layer\n"
"If the region is (conceptionally) a flat region, it will be inserted into the cell's shapes "
@ -1663,7 +1713,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.26.\n"
) +
gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::Edges &)) &db::Layout::insert,
gsi::method_ext ("insert", &insert_edges,
gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("edges"),
"@brief Inserts an edge collection into the given cell and layer\n"
"If the edge collection is (conceptionally) flat, it will be inserted into the cell's shapes "
@ -1675,7 +1725,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.26.\n"
) +
gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::EdgePairs &)) &db::Layout::insert,
gsi::method_ext ("insert", &insert_edge_pairs,
gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("edge_pairs"),
"@brief Inserts an edge pair collection into the given cell and layer\n"
"If the edge pair collection is (conceptionally) flat, it will be inserted into the cell's shapes "
@ -1687,7 +1737,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n"
"This method has been introduced in version 0.27.\n"
) +
gsi::method ("insert", (void (db::Layout::*) (db::cell_index_type, int, const db::Texts &)) &db::Layout::insert,
gsi::method_ext ("insert", &insert_texts,
gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("texts"),
"@brief Inserts an text collection into the given cell and layer\n"
"If the text collection is (conceptionally) flat, it will be inserted into the cell's shapes "

View File

@ -37,14 +37,6 @@ namespace gsi
// ---------------------------------------------------------------
// db::Library binding
/**
* @brief A basic implementation of the library
*/
static db::Library *new_lib ()
{
return new db::Library ();
}
static db::Library *library_by_name (const std::string &name, const std::string &for_technology)
{
return db::LibraryManager::instance ().lib_ptr_by_name (name, for_technology);
@ -73,12 +65,22 @@ static std::vector<db::lib_id_type> library_ids ()
return r;
}
static void refresh_all ()
{
db::LibraryManager::instance ().refresh_all ();
}
static void register_lib (db::Library *lib, const std::string &name)
{
lib->set_name (name);
db::LibraryManager::instance ().register_lib (lib);
}
static void unregister_lib (db::Library *lib)
{
db::LibraryManager::instance ().unregister_lib (lib);
}
static void delete_lib (db::Library *lib)
{
db::LibraryManager::instance ().delete_lib (lib);
@ -97,7 +99,7 @@ static std::string get_technology (db::Library *lib)
static void destroy_lib (db::Library *lib)
{
if (db::LibraryManager::instance ().lib_ptr_by_name (lib->get_name ()) == lib) {
// Library is registered -> do not delete
delete_lib (lib);
} else {
delete lib;
}
@ -105,31 +107,63 @@ static void destroy_lib (db::Library *lib)
namespace {
template <class Base>
class LibraryClass
: public Class<db::Library>
: public gsi::Class<Base>
{
public:
LibraryClass (const char *module, const char *name, const gsi::Methods &methods, const char *description)
: Class<db::Library> (module, name, methods, description)
: gsi::Class<Base> (module, name, methods, description)
{ }
template <class B>
LibraryClass (const gsi::Class<B> &base, const char *module, const char *name, const gsi::Methods &methods, const char *description)
: gsi::Class<Base> (base, module, name, methods, description)
{ }
virtual void destroy (void *p) const
{
db::Library *lib = reinterpret_cast<db::Library *> (p);
Base *lib = reinterpret_cast<Base *> (p);
destroy_lib (lib);
}
};
class LibraryImpl
: public db::Library
{
public:
LibraryImpl () : db::Library ()
{
// .. nothing yet ..
}
LibraryClass decl_Library ("db", "Library",
gsi::constructor ("new", &new_lib,
"@brief Creates a new, empty library"
) +
virtual std::string reload ()
{
if (cb_reload.can_issue ()) {
return cb_reload.issue<db::Library, std::string> (&db::Library::reload);
} else {
return db::Library::reload ();
}
}
gsi::Callback cb_reload;
};
}
static LibraryImpl *new_lib ()
{
return new LibraryImpl ();
}
/**
* @brief A basic implementation of the library
*/
LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"),
"@brief Gets a library by name\n"
"Returns the library object for the given name. If the name is not a valid\n"
"library name, nil is returned.\n"
"Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n"
"\n"
"Different libraries can be registered under the same names for different technologies. When a technology name is given in 'for_technologies', "
"the first library matching this technology is returned. If no technology is given, the first library is returned.\n"
@ -155,6 +189,11 @@ LibraryClass decl_Library ("db", "Library",
"\n"
"This method has been introduced in version 0.27."
) +
gsi::method ("refresh_all", &refresh_all,
"@brief Calls \\refresh on all libraries.\n"
"\n"
"This convenience method has been introduced in version 0.30.4."
) +
gsi::method_ext ("register", &register_lib, gsi::arg ("name"),
"@brief Registers the library with the given name\n"
"\n"
@ -166,6 +205,22 @@ LibraryClass decl_Library ("db", "Library",
"\n"
"The technology specific behaviour has been introduced in version 0.27."
) +
gsi::method_ext ("unregister", &unregister_lib,
"@brief Unregisters the library\n"
"\n"
"Unregisters the library from the system. This will break all references of cells "
"using this library and make them 'defunct'.\n"
"\n"
"This method has been introduced in version 0.30.5."
) +
gsi::method ("rename", &db::Library::rename, gsi::arg ("name"),
"@brief Renames the library\n"
"\n"
"Re-registers the library under a new name. Note that this method will also change the references "
"to the library.\n"
"\n"
"This method has been introduced in version 0.30.5."
) +
gsi::method_ext ("delete", &delete_lib,
"@brief Deletes the library\n"
"\n"
@ -176,7 +231,7 @@ LibraryClass decl_Library ("db", "Library",
) +
gsi::method ("name", &db::Library::get_name,
"@brief Returns the libraries' name\n"
"The name is set when the library is registered and cannot be changed\n"
"The name is set when the library is registered. To change it use \\rename.\n"
) +
gsi::method ("id", &db::Library::get_id,
"@brief Returns the library's ID\n"
@ -240,9 +295,32 @@ LibraryClass decl_Library ("db", "Library",
"@brief Updates all layouts using this library.\n"
"This method will retire cells or update layouts in the attached clients.\n"
"It will also recompute the PCells inside the library. "
"Starting with version 0.30.5, this method will also call 'reload' on all libraries to "
"refresh cells located in external files.\n"
"\n"
"This method has been introduced in version 0.27.8."
),
"@hide"
);
/**
* @brief The reimplementation stub
*/
LibraryClass<LibraryImpl> decl_LibraryImpl (decl_Library, "db", "Library",
gsi::constructor ("new", &new_lib,
"@brief Creates a new, empty library"
) +
gsi::callback ("reload", &LibraryImpl::reload, &LibraryImpl::cb_reload,
"@brief Reloads resources for the library.\n"
"Reimplement this method if you like to reload resources the library was created from - "
"for example layout files. Make sure you return the new name of the library from this function. "
"If you do not want to change the name of the library, return the current name (i.e. the value of \\name).\n"
"\n"
"@return The new name of the library or the original name if it did not change.\n"
"\n"
"This method is called on \\refresh. It was introduced in version 0.30.5.\n"
),
"@brief A Library \n"
"\n"
"A library is basically a wrapper around a layout object. The layout object\n"

View File

@ -1034,7 +1034,7 @@ Class<db::EqualDeviceParameters> decl_dbEqualDeviceParameters ("db", "EqualDevic
Class<GenericDeviceParameterCompare> decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare",
gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"),
"@brief Compares the parameters of two devices for a begin less than b. "
"@brief Compares the parameters of two devices for a begin less than b.\n"
"Returns true, if the parameters of device a are considered less than those of device b."
"The 'less' implementation needs to ensure strict weak ordering. Specifically, less(a,b) == false and less(b,a) implies that a is equal to b and "
"less(a,b) == true implies that less(b,a) is false and vice versa. If not, an internal error "

View File

@ -150,8 +150,7 @@ ShapeEditService::get_edit_layer ()
mp_layout = &(cv->layout ());
mp_cell = cv.cell ();
// fetches the last configuration for the given layer
view ()->set_active_cellview_index (cv_index);
view ()->set_active_cellview_index_silent (cv_index);
}
void
@ -173,8 +172,9 @@ ShapeEditService::change_edit_layer (const db::LayerProperties &lp)
close_editor_hooks (false);
}
view ()->set_active_cellview_index_silent (m_cv_index);
// fetches the last configuration for the given layer
view ()->set_active_cellview_index (m_cv_index);
config_recent_for_layer (lp, m_cv_index);
if (editing ()) {
@ -237,7 +237,9 @@ ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl
return;
}
view ()->set_active_cellview_index (cv_index);
// NOTE: we don't want side effects during this operation - i.e. some that
// change the selection. Hence no events here.
view ()->set_active_cellview_index_silent (cv_index);
const lay::ParsedLayerSource &source = cl->source (true /*real*/);
int layer = cl->layer_index ();

View File

@ -73,6 +73,7 @@ static const char *cm_symbols[] = {
"cm_save",
"cm_save_all",
"cm_reload",
"cm_refresh",
"cm_close",
"cm_close_all",
"cm_clone",

View File

@ -33,12 +33,86 @@
#include "dbCellMapping.h"
#include "tlLog.h"
#include "tlStream.h"
#include "tlFileUtils.h"
#include <QDir>
namespace lay
{
// -------------------------------------------------------------------------------------------
class FileBasedLibrary
: public db::Library
{
public:
FileBasedLibrary (const std::string &path)
: db::Library (), m_path (path)
{
set_description (tl::filename (path));
}
void merge_with_other_layout (const std::string &path)
{
m_other_paths.push_back (path);
merge_impl (path);
}
virtual std::string reload ()
{
std::string name = tl::basename (m_path);
layout ().clear ();
tl::InputStream stream (m_path);
db::Reader reader (stream);
reader.read (layout ());
// Use the libname if there is one
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
name = m->second.value.to_string ();
break;
}
}
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
merge_impl (*p);
}
return name;
}
private:
std::string m_path;
std::list<std::string> m_other_paths;
void merge_impl (const std::string &path)
{
db::Layout ly;
tl::InputStream stream (path);
db::Reader reader (stream);
reader.read (ly);
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
std::string cn = ly.cell_name (*c);
source_cells.push_back (*c);
target_cells.push_back (layout ().add_cell (cn.c_str ()));
}
db::CellMapping cm;
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
layout ().copy_tree_shapes (ly, cm);
}
};
// -------------------------------------------------------------------------------------------
LibraryController::LibraryController ()
: m_file_watcher (0),
dm_sync_files (this, &LibraryController::sync_files)
@ -201,38 +275,23 @@ LibraryController::sync_files ()
} else {
std::map<std::string, db::Library *> libs_by_name_here;
std::map<std::string, FileBasedLibrary *> libs_by_name_here;
// Reload all files
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
std::string filename = tl::to_string (*im);
std::string lib_path = tl::to_string (lp.absoluteFilePath (*im));
QFileInfo fi (tl::to_qstring (lib_path));
try {
std::unique_ptr<db::Library> lib (new db::Library ());
lib->set_description (filename);
std::unique_ptr<FileBasedLibrary> lib (new FileBasedLibrary (lib_path));
if (! p->second.empty ()) {
lib->set_technology (p->second);
}
std::string libname = tl::to_string (QFileInfo (*im).baseName ());
tl::log << "Reading library '" << lib_path << "'";
tl::InputStream stream (lib_path);
db::Reader reader (stream);
reader.read (lib->layout ());
// Use the libname if there is one
db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname");
for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) {
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
libname = m->second.value.to_string ();
break;
}
}
std::string libname = lib->reload ();
// merge with existing lib if there is already one in this folder with the right name
auto il = libs_by_name_here.find (libname);
@ -240,21 +299,7 @@ LibraryController::sync_files ()
tl::log << "Merging with other library file with the same name: " << libname;
db::Library *org_lib = il->second;
db::Layout &org_layout = org_lib->layout ();
std::vector<db::cell_index_type> target_cells, source_cells;
// collect the cells to pull in (all top cells of the library layout)
for (auto c = lib->layout ().begin_top_down (); c != lib->layout ().end_top_cells (); ++c) {
std::string cn = lib->layout ().cell_name (*c);
source_cells.push_back (*c);
target_cells.push_back (org_layout.add_cell (cn.c_str ()));
}
db::CellMapping cm;
cm.create_multi_mapping_full (org_layout, target_cells, lib->layout (), source_cells);
org_layout.copy_tree_shapes (lib->layout (), cm);
il->second->merge_with_other_layout (lib_path);
// now, we can forget the new library as it is included in the first one

View File

@ -2564,6 +2564,12 @@ MainWindow::cm_writer_options ()
mp_layout_save_options->edit_global_options (dispatcher (), db::Technologies::instance ());
}
void
MainWindow::cm_refresh ()
{
db::LibraryManager::instance ().refresh_all ();
}
void
MainWindow::cm_new_panel ()
{
@ -4045,6 +4051,8 @@ MainWindow::menu_activated (const std::string &symbol)
cm_reader_options ();
} else if (symbol == "cm_writer_options") {
cm_writer_options ();
} else if (symbol == "cm_refresh") {
cm_refresh ();
} else if (symbol == "cm_new_panel") {
cm_new_panel ();
} else if (symbol == "cm_new_layout") {
@ -4458,6 +4466,7 @@ public:
menu_entries.push_back (lay::menu_item ("cm_close_all", "close_all:edit", at, tl::to_string (QObject::tr ("Close All(Shift+Ctrl+W)"))));
menu_entries.push_back (lay::menu_item ("cm_clone", "clone", at, tl::to_string (QObject::tr ("Clone Panel"))));
menu_entries.push_back (lay::menu_item ("cm_reload", "reload:edit", at, tl::to_string (QObject::tr ("Reload(Ctrl+R)"))));
menu_entries.push_back (lay::menu_item ("cm_refresh", "refresh:edit", at, tl::to_string (QObject::tr ("Refresh Libraries"))));
menu_entries.push_back (lay::menu_item ("cm_pull_in", "pull_in:edit", at, tl::to_string (QObject::tr ("Pull In Other Layout"))));
menu_entries.push_back (lay::menu_item ("cm_reader_options", "reader_options", at, tl::to_string (QObject::tr ("Reader Options"))));
menu_entries.push_back (lay::separator ("open_recent_group", at));

View File

@ -837,6 +837,7 @@ private:
void cm_pull_in ();
void cm_reader_options ();
void cm_writer_options ();
void cm_refresh ();
void cm_new_panel ();
void cm_new_layout ();
void cm_clone ();

View File

@ -879,6 +879,7 @@ LAYBASIC_PUBLIC Class<lay::LayoutViewBase> decl_LayoutViewBase (decl_Dispatcher,
gsi::method ("active_cellview_index=|#active_setview_index=|#set_active_cellview_index", &lay::LayoutViewBase::set_active_cellview_index, gsi::arg ("index"),
"@brief Makes the cellview with the given index the active one (shown in hierarchy browser)\n"
"See \\active_cellview_index.\n"
"Note, that this changing the active cell view index has side effects such as terminating an editing operation.\n"
"\n"
"This method has been renamed from set_active_cellview_index to active_cellview_index= in version 0.25. "
"The original name is still available, but is deprecated."

View File

@ -110,6 +110,8 @@ Editables::del (db::Transaction *transaction)
e->del ();
}
signal_selection_changed ();
} catch (...) {
trans_holder->cancel ();
throw;

View File

@ -475,6 +475,12 @@ LayoutViewBase::shutdown ()
}
}
// NOTE: this must happen before the services are deleted
mp_move_service = 0;
mp_selection_service = 0;
mp_tracker = 0;
mp_zoom_service = 0;
// delete all plugins
std::vector<lay::Plugin *> plugins;
plugins.swap (mp_plugins);
@ -4943,6 +4949,19 @@ LayoutViewBase::active_cellview_index () const
return m_active_cellview_index;
}
void
LayoutViewBase::set_active_cellview_index_silent (int index)
{
enable_active_cellview_changed_event (false);
try {
set_active_cellview_index (index);
enable_active_cellview_changed_event (true, true /*silent*/);
} catch (...) {
enable_active_cellview_changed_event (true, true /*silent*/);
throw;
}
}
void
LayoutViewBase::set_active_cellview_index (int index)
{

View File

@ -2151,6 +2151,12 @@ public:
*/
virtual void set_active_cellview_index (int index);
/**
* @brief Select a certain cellview for the active one
* This version does not emit any events while changing the cellview index
*/
void set_active_cellview_index_silent (int index);
/**
* @brief An event triggered if the active cellview changes
* This event is triggered after the active cellview changed.

View File

@ -707,19 +707,20 @@ HierarchyControlPanel::update_required ()
}
void
HierarchyControlPanel::select_active (int cellview_index)
HierarchyControlPanel::select_active (int cellview_index, bool silent)
{
if (cellview_index != m_active_index) {
mp_selector->setCurrentIndex (cellview_index);
selection_changed (cellview_index);
change_active_cellview (cellview_index);
if (! silent) {
emit active_cellview_changed (cellview_index);
}
}
}
void
HierarchyControlPanel::selection_changed (int index)
HierarchyControlPanel::change_active_cellview (int index)
{
if (index != m_active_index) {
search_editing_finished ();
m_active_index = index;
@ -742,9 +743,14 @@ HierarchyControlPanel::selection_changed (int index)
for (std::vector <QToolButton *>::const_iterator f = mp_cell_list_headers.begin (); f != mp_cell_list_headers.end (); ++f, ++i) {
(*f)->setChecked (i == index);
}
}
void
HierarchyControlPanel::selection_changed (int index)
{
if (index != m_active_index) {
change_active_cellview (index);
emit active_cellview_changed (index);
}
}

View File

@ -140,7 +140,7 @@ public:
* selects the active cellview by index. The index must be
* a valid index within the context of the layout view.
*/
void select_active (int cellview_index);
void select_active (int cellview_index, bool silent = false);
/**
* @brief Get the active cellview
@ -346,6 +346,9 @@ private:
// ask for cell copy mode
bool ask_for_cell_copy_mode (const db::Layout &layout, const std::vector<cell_path_type> &paths, int &cell_copy_mode);
// changes the active cellview
void change_active_cellview (int index);
};
} // namespace lay

View File

@ -341,6 +341,8 @@ BEGIN_PROTECTED
// confine the selection
auto prev_selected = mp_editables->selection_size ();
mp_tree_model->begin_reset_model ();
auto selection = mp_ui->tree->selectionModel ()->selectedIndexes ();
@ -409,6 +411,10 @@ BEGIN_PROTECTED
update_controls ();
if (m_objects != prev_selected) {
mp_editables->signal_selection_changed ();
}
END_PROTECTED
}

View File

@ -1392,7 +1392,8 @@ LayoutView::set_active_cellview_index (int index)
{
if (index >= 0 && index < int (cellviews ())) {
if (mp_hierarchy_panel) {
mp_hierarchy_panel->select_active (index);
// NOTE: we don't send events from here, that is done in "LayoutViewBase::set_active_cellview_index"
mp_hierarchy_panel->select_active (index, true /*no events*/);
}
LayoutViewBase::set_active_cellview_index (index);
}

View File

@ -315,6 +315,12 @@ class DBLayoutTests1_TestClass < TestBase
# delete_cell
l = RBA::Layout.new
begin
l.delete_cell(0) # must throw an exception
assert_equal(true, false)
rescue
end
l.insert_layer_at(0, RBA::LayerInfo.new(1, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))
@ -371,6 +377,12 @@ class DBLayoutTests1_TestClass < TestBase
# delete_cells
l = RBA::Layout.new
begin
l.delete_cells([0, 1]) # must throw an exception
assert_equal(true, false)
rescue
end
l.insert_layer_at(0, RBA::LayerInfo.new(1, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))
@ -412,6 +424,12 @@ class DBLayoutTests1_TestClass < TestBase
# prune_cell
l = RBA::Layout.new
begin
l.prune_cell(0) # must throw an exception
assert_equal(true, false)
rescue
end
l.insert_layer_at(0, RBA::LayerInfo.new(1, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))
@ -488,6 +506,12 @@ class DBLayoutTests1_TestClass < TestBase
# delete_cell_rec
l = RBA::Layout.new
begin
l.delete_cell_rec(0) # must throw an exception
assert_equal(true, false)
rescue
end
l.insert_layer_at(0, RBA::LayerInfo.new(1, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))
@ -643,6 +667,12 @@ class DBLayoutTests1_TestClass < TestBase
# prune_subcells
l = RBA::Layout.new
begin
l.prune_subcells(0) # must throw an exception
assert_equal(true, false)
rescue
end
l.insert_layer_at(0, RBA::LayerInfo.new(1, 0))
c0 = l.cell(l.add_cell("c0"))
c1 = l.cell(l.add_cell("c1"))

View File

@ -23,6 +23,19 @@ end
load("test_prologue.rb")
class MyLibImpl < RBA::Library
def initialize
@reload_count = 0
end
def reload_count
@reload_count
end
def reload
@reload_count += 1
return "RBA-unit-test2"
end
end
class DBLibrary_TestClass < TestBase
def test_1_registration
@ -45,14 +58,17 @@ class DBLibrary_TestClass < TestBase
assert_equal(RBA::Library::library_names.member?("RBA-unit-test"), true)
assert_equal(RBA::Library::library_by_name("RBA-unit-test").id, lib_id)
# destroy should not do anything as libraries are not to be removed through the destructor
lib._destroy
assert_equal(RBA::Library::library_by_name("RBA-unit-test").id, lib_id)
assert_equal(lib.destroyed?, true)
# The library reference is kept internally
lib = nil
GC.start
GC.start
lib = RBA::Library::library_by_name("RBA-unit-test")
assert_equal(lib.destroyed?, false)
lib.delete
assert_equal(lib.name, "RBA-unit-test")
lib._destroy
assert_equal(lib.destroyed?, true)
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
end
@ -103,6 +119,128 @@ class DBLibrary_TestClass < TestBase
end
def test_4_library_registration_and_rename
lib = RBA::Library::new
lib.description = "LIB1"
lib.delete
assert_equal(lib.destroyed?, true)
lib = RBA::Library::new
lib.layout.create_cell("A")
lib.description = "LIB1"
lib.register("RBA-unit-test")
assert_equal(RBA::Library::library_by_name("RBA-unit-test").description, "LIB1")
lib.unregister
assert_equal(lib.destroyed?, false)
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
lib.register("RBA-unit-test")
assert_equal(RBA::Library::library_by_name("RBA-unit-test").description, "LIB1")
ly = RBA::Layout::new
ci = ly.create_cell("A", "RBA-unit-test").cell_index
assert_equal(ly.cell(ci).qname, "RBA-unit-test.A")
lib.rename("RBA-unit-test2")
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
assert_equal(RBA::Library::library_by_name("RBA-unit-test2").description, "LIB1")
assert_equal(ly.cell(ci).qname, "RBA-unit-test2.A")
lib.delete
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
assert_equal(RBA::Library::library_by_name("RBA-unit-test2"), nil)
assert_equal(ly.cell(ci).qname, "<defunct>RBA-unit-test2.A")
end
def test_5_reload
lib = MyLibImpl::new
lib.description = "LIB1"
lib.register("RBA-unit-test")
assert_equal(RBA::Library::library_by_name("RBA-unit-test").description, "LIB1")
lib.refresh
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
assert_equal(RBA::Library::library_by_name("RBA-unit-test2").description, "LIB1")
assert_equal(lib.reload_count, 1)
lib.delete
assert_equal(RBA::Library::library_by_name("RBA-unit-test"), nil)
assert_equal(RBA::Library::library_by_name("RBA-unit-test2"), nil)
end
def test_6_cells_become_defunct_after_unregister
lib = RBA::Library::new
lib.description = "LIB1"
lib.register("RBA-unit-test")
cell_a = lib.layout.create_cell("A")
l1 = lib.layout.layer(1, 0)
cell_a.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000))
ly = RBA::Layout::new
lc = ly.create_cell("A", "RBA-unit-test")
assert_equal(lc.qname, "RBA-unit-test.A")
ci = lc.cell_index
lib.unregister
# NOTE: cleanup has not been called, so we can find the cell using the cell_index
# (the actual cell object is no longer there because it has been replaced by
# a cold proxy)
lc = ly.cell(ci)
assert_equal(lc.qname, "<defunct>RBA-unit-test.A")
# this will restore the reference
lib.register("RBA-unit-test")
lc = ly.cell(ci)
assert_equal(lc.qname, "RBA-unit-test.A")
end
def test_7_change_ref
lib = RBA::Library::new
lib.description = "LIB1"
lib.register("RBA-unit-test")
l1 = lib.layout.layer(1, 0)
cell_a = lib.layout.create_cell("A")
cell_a.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000))
lib2 = RBA::Library::new
lib2.description = "LIB2"
lib2.register("RBA-unit-test2")
l1 = lib2.layout.layer(1, 0)
cell_b = lib2.layout.create_cell("B")
cell_b.shapes(l1).insert(RBA::Box::new(0, 0, 2000, 1000))
ly = RBA::Layout::new
c1 = ly.create_cell("A", "RBA-unit-test")
assert_equal(c1.qname, "RBA-unit-test.A")
c1.change_ref(lib2.id, cell_b.cell_index)
assert_equal(c1.qname, "RBA-unit-test2.B")
ly = RBA::Layout::new
c1 = ly.create_cell("A", "RBA-unit-test")
assert_equal(c1.qname, "RBA-unit-test.A")
c1.change_ref("RBA-unit-test2", "B")
assert_equal(c1.qname, "RBA-unit-test2.B")
end
end
load("test_epilogue.rb")