From b3ec482eb8c6b2780de022d0a68e5b8f084cd1fd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 16:21:53 +0200 Subject: [PATCH 01/10] WIP (also for other issue #2172) - New virtual method "reload" for Library - Implementing that for file-based libraries - Integrating that into "refresh" - GSI binding for Library#rename, Library#unregister - Trigger changed event on Library#unregister - Library#_destroy will unregister the library - Typos fixed --- src/db/db/dbLibrary.cc | 16 ++++++ src/db/db/dbLibrary.h | 22 ++++++++ src/db/db/dbLibraryManager.cc | 3 ++ src/db/db/gsiDeclDbLibrary.cc | 81 +++++++++++++++++++++++++---- src/db/db/gsiDeclDbNetlist.cc | 2 +- src/lay/lay/layLibraryController.cc | 59 +++++++++++++++------ 6 files changed, 155 insertions(+), 28 deletions(-) diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index d62136018..d590fef3a 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -145,9 +145,25 @@ 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 ()) { + + // if the name changed, reregister the library under the new name + db::LibraryManager::instance ().unregister_lib (this); + set_name (name); + db::LibraryManager::instance ().register_lib (this); + + } +} + void Library::refresh () { + std::string name = reload (); + rename (name); + layout ().refresh (); remap_to (this); } diff --git a/src/db/db/dbLibrary.h b/src/db/db/dbLibrary.h index af8e71c50..6da00df3c 100644 --- a/src/db/db/dbLibrary.h +++ b/src/db/db/dbLibrary.h @@ -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 * diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index a10430e2f..c03cf602a 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -126,6 +126,9 @@ LibraryManager::unregister_lib (Library *library) library->remap_to (0); library->set_id (std::numeric_limits::max ()); + + // issue the change notification + changed_event (); } void diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 6e40c00e2..c7c749065 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -79,6 +79,11 @@ static void register_lib (db::Library *lib, const std::string &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 +102,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 +110,51 @@ static void destroy_lib (db::Library *lib) namespace { +template class LibraryClass - : public Class + : public gsi::Class { public: LibraryClass (const char *module, const char *name, const gsi::Methods &methods, const char *description) - : Class (module, name, methods, description) + : gsi::Class (module, name, methods, description) + { } + + template + LibraryClass (const gsi::Class &base, const char *module, const char *name, const gsi::Methods &methods, const char *description) + : gsi::Class (base, module, name, methods, description) { } virtual void destroy (void *p) const { - db::Library *lib = reinterpret_cast (p); + Base *lib = reinterpret_cast (p); destroy_lib (lib); } }; +class LibraryImpl + : public db::Library +{ +public: + LibraryImpl () : db::Library () { } + + virtual std::string reload () + { + if (cb_reload.can_issue ()) { + return cb_reload.issue (&db::Library::reload); + } else { + return db::Library::reload (); + } + } + + gsi::Callback cb_reload; +}; + } -LibraryClass decl_Library ("db", "Library", - gsi::constructor ("new", &new_lib, - "@brief Creates a new, empty library" - ) + +LibraryClass 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" @@ -166,6 +191,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 will not change library references - " + "i.e. references to the old name will become invalid after calling this method.\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 +217,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 +281,27 @@ 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" +); + +LibraryClass 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.\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" diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index b258f5aad..9ec4ccae4 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1034,7 +1034,7 @@ Class decl_dbEqualDeviceParameters ("db", "EqualDevic Class 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 " diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index fd5607520..0f47743ca 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -32,12 +32,53 @@ #include "dbReader.h" #include "tlLog.h" #include "tlStream.h" +#include "tlFileUtils.h" #include namespace lay { +// ------------------------------------------------------------------------------------------- + +class FileBasedLibrary + : public db::Library +{ +public: + FileBasedLibrary (const std::string &path) + : db::Library (), m_path (path) + { + set_description (tl::filename (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; + } + } + + return name; + } + +private: + std::string m_path; +}; + +// ------------------------------------------------------------------------------------------- + LibraryController::LibraryController () : m_file_watcher (0), dm_sync_files (this, &LibraryController::sync_files) @@ -166,7 +207,6 @@ LibraryController::sync_files () QStringList libs = lp.entryList (name_filters, QDir::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)); try { @@ -187,26 +227,13 @@ LibraryController::sync_files () if (needs_load) { - std::unique_ptr lib (new db::Library ()); - lib->set_description (filename); + std::unique_ptr lib (new FileBasedLibrary (lib_path)); if (! p->second.empty ()) { lib->set_technology (p->second); } - lib->set_name (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 ()) { - lib->set_name (m->second.value.to_string ()); - break; - } - } + lib->set_name (lib->reload ()); if (! p->second.empty ()) { tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'"; From ff2beaf32f2d12750d8875852eb0c5df0c5ad68a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 17:54:26 +0200 Subject: [PATCH 02/10] Bugfix: cells were not replaced by defunc cold proxies after unregistering a lib --- src/db/db/dbLibraryManager.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index c03cf602a..7dde8a647 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -112,6 +112,8 @@ LibraryManager::unregister_lib (Library *library) return; } + library->remap_to (0); + { tl::MutexLocker locker (&m_lock); @@ -124,7 +126,6 @@ LibraryManager::unregister_lib (Library *library) } } - library->remap_to (0); library->set_id (std::numeric_limits::max ()); // issue the change notification From 50452877125358bfad0a3719ee4804d04e1b3d6d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 19:26:11 +0200 Subject: [PATCH 03/10] Bugfixes --- src/db/db/gsiDeclDbLibrary.cc | 26 +++++++++++++++++--------- testdata/ruby/dbLibrary.rb | 22 ++++++++++++++++------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index c7c749065..1423d5ee4 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -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); @@ -135,7 +127,10 @@ class LibraryImpl : public db::Library { public: - LibraryImpl () : db::Library () { } + LibraryImpl () : db::Library () + { + // .. nothing yet .. + } virtual std::string reload () { @@ -151,6 +146,15 @@ public: } +static LibraryImpl *new_lib () +{ + return new LibraryImpl (); +} + +/** + * @brief A basic implementation of the library + */ + LibraryClass 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" @@ -289,6 +293,10 @@ LibraryClass decl_Library ("db", "LibraryBase", "@hide" ); +/** + * @brief The reimplementation stub + */ + LibraryClass decl_LibraryImpl (decl_Library, "db", "Library", gsi::constructor ("new", &new_lib, "@brief Creates a new, empty library" diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb index d575fba82..cab65b2e7 100644 --- a/testdata/ruby/dbLibrary.rb +++ b/testdata/ruby/dbLibrary.rb @@ -45,14 +45,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 @@ -101,6 +104,13 @@ class DBLibrary_TestClass < TestBase lib._destroy assert_equal(lib.destroyed?, true) + end + + def test_4_library_registration + + + + end end From 8b7c15af5b97b38933731052df34c4a743d9d956 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 19:44:34 +0200 Subject: [PATCH 04/10] Tests added --- testdata/ruby/dbLibrary.rb | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb index cab65b2e7..408ca7a84 100644 --- a/testdata/ruby/dbLibrary.rb +++ b/testdata/ruby/dbLibrary.rb @@ -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 @@ -106,10 +119,51 @@ class DBLibrary_TestClass < TestBase end - def test_4_library_registration + 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.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") + + 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") + + 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_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 From 5625fb95dd403eff13642441a5be6d6205cabfcf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 19:50:31 +0200 Subject: [PATCH 05/10] More tests added --- testdata/ruby/dbLibrary.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb index 408ca7a84..cbe6ac78f 100644 --- a/testdata/ruby/dbLibrary.rb +++ b/testdata/ruby/dbLibrary.rb @@ -167,6 +167,37 @@ class DBLibrary_TestClass < TestBase 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, "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 + end load("test_epilogue.rb") From 67de5186111e5eb02112d8fe44e50227078a19a0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 21:14:56 +0200 Subject: [PATCH 06/10] 'Refresh Libraries' feature in File menu, new GSI method Library#refresh_all (static) --- src/db/db/dbLibraryManager.cc | 10 ++++++++++ src/db/db/dbLibraryManager.h | 5 +++++ src/db/db/gsiDeclDbLibrary.cc | 10 ++++++++++ src/lay/lay/gsiDeclLayMainWindow.cc | 1 + src/lay/lay/layMainWindow.cc | 9 +++++++++ src/lay/lay/layMainWindow.h | 1 + 6 files changed, 36 insertions(+) diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index 7dde8a647..5ba169d65 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -248,6 +248,16 @@ LibraryManager::lib_internal (lib_id_type id) const } } +void +LibraryManager::refresh_all () +{ + for (std::vector::iterator l = m_libs.begin (); l != m_libs.end (); ++l) { + if (*l) { + (*l)->refresh (); + } + } +} + void LibraryManager::clear () { diff --git a/src/db/db/dbLibraryManager.h b/src/db/db/dbLibraryManager.h index 749d69255..849daede4 100644 --- a/src/db/db/dbLibraryManager.h +++ b/src/db/db/dbLibraryManager.h @@ -212,6 +212,11 @@ public: */ void clear (); + /** + * @brief Refreshes all libraries + */ + void refresh_all (); + private: std::vector m_libs; lib_name_map m_lib_by_name; diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 1423d5ee4..bb938168b 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -65,6 +65,11 @@ static std::vector 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); @@ -184,6 +189,11 @@ LibraryClass decl_Library ("db", "LibraryBase", "\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", ®ister_lib, gsi::arg ("name"), "@brief Registers the library with the given name\n" "\n" diff --git a/src/lay/lay/gsiDeclLayMainWindow.cc b/src/lay/lay/gsiDeclLayMainWindow.cc index 7c9f47a82..9bb58226d 100644 --- a/src/lay/lay/gsiDeclLayMainWindow.cc +++ b/src/lay/lay/gsiDeclLayMainWindow.cc @@ -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", diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 72430e410..c4d9d4a1b 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2555,6 +2555,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 () { @@ -4036,6 +4042,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") { @@ -4449,6 +4457,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)); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index dee59857d..737e24efc 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -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 (); From d1e440d5656ea624ff307133aec4995d5b209bad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 Oct 2025 21:23:58 +0200 Subject: [PATCH 07/10] Doc updates --- src/db/db/gsiDeclDbLibrary.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index bb938168b..40351109a 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -314,7 +314,8 @@ LibraryClass decl_LibraryImpl (decl_Library, "db", "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.\n" + "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" From b3cc5d73fe70203ec663d2621ec3ae41536aaf46 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 14 Oct 2025 23:13:10 +0200 Subject: [PATCH 08/10] Renaming a library will also change the references --- src/db/db/dbLibrary.cc | 7 +------ src/db/db/dbLibraryManager.cc | 33 +++++++++++++++++++++++++++++++++ src/db/db/dbLibraryManager.h | 5 +++++ src/db/db/gsiDeclDbLibrary.cc | 4 ++-- testdata/ruby/dbLibrary.rb | 9 +++++++++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index d590fef3a..87befc7cf 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -149,12 +149,7 @@ void Library::rename (const std::string &name) { if (name != get_name () && db::LibraryManager::initialized ()) { - - // if the name changed, reregister the library under the new name - db::LibraryManager::instance ().unregister_lib (this); - set_name (name); - db::LibraryManager::instance ().register_lib (this); - + db::LibraryManager::instance ().rename (get_id (), name); } } diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index 5ba169d65..24aa8b3f6 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -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 LibraryManager::lib_by_name (const std::string &name, const std::set &for_technologies) const { diff --git a/src/db/db/dbLibraryManager.h b/src/db/db/dbLibraryManager.h index 849daede4..9dd8f6489 100644 --- a/src/db/db/dbLibraryManager.h +++ b/src/db/db/dbLibraryManager.h @@ -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 * diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 40351109a..d06caba5f 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -216,8 +216,8 @@ LibraryClass decl_Library ("db", "LibraryBase", 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 will not change library references - " - "i.e. references to the old name will become invalid after calling this method.\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." ) + diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb index cbe6ac78f..28286d2c7 100644 --- a/testdata/ruby/dbLibrary.rb +++ b/testdata/ruby/dbLibrary.rb @@ -127,6 +127,7 @@ class DBLibrary_TestClass < TestBase 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") @@ -138,14 +139,22 @@ class DBLibrary_TestClass < TestBase 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, "RBA-unit-test2.A") + end def test_5_reload From bbd473ef47bb97d0907fa8ee51bb7a4a69d2dd1a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 14 Oct 2025 23:50:08 +0200 Subject: [PATCH 09/10] Added 'Cell#change_ref' to modify the library reference of a cell --- src/db/db/gsiDeclDbCell.cc | 57 +++++++++++++++++++++++++++++++++++++- testdata/ruby/dbLibrary.rb | 34 +++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 7138418e7..159dbfec7 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -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 (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 (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,8 +3233,24 @@ Class 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" "\n" diff --git a/testdata/ruby/dbLibrary.rb b/testdata/ruby/dbLibrary.rb index 28286d2c7..ba145c0f5 100644 --- a/testdata/ruby/dbLibrary.rb +++ b/testdata/ruby/dbLibrary.rb @@ -207,6 +207,40 @@ class DBLibrary_TestClass < TestBase 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") From ce0d61947016efe58b2e5aad60d94c090ce552b5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 15 Oct 2025 22:35:27 +0200 Subject: [PATCH 10/10] Force update of cell tree after rename of library --- src/db/db/dbLibrary.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index 87befc7cf..a21736702 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -282,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::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 (); } }