From 7cec679d39f29d23f74a72b8da44f6f36a1e7958 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 31 Mar 2026 21:35:33 +0200 Subject: [PATCH] Taking care of complex library reference scenarios where libraries self-reference and under the presence of stale references. Main issue is order of resolution and indirect references due to replication. Solution is to repeat resolution until saturated. A test is supplied. --- src/db/db/dbFileBasedLibrary.cc | 5 + src/db/db/dbLayout.cc | 12 +- src/db/db/dbLayout.h | 11 +- src/db/db/dbLibrary.cc | 152 ++++++++++++++------------ src/db/db/dbLibraryManager.cc | 33 +++++- src/db/db/dbTestSupport.cc | 53 +++++++-- src/db/db/dbTestSupport.h | 3 +- src/db/unit_tests/dbLibrariesTests.cc | 136 +++++++++++++++++++++++ src/lay/lay/layLibraryController.cc | 24 ++-- src/tl/tl/tlUniqueId.h | 11 ++ testdata/libman/design.gds | Bin 0 -> 41932 bytes testdata/libman/design_au1.gds | Bin 0 -> 13144 bytes testdata/libman/design_au2.gds | Bin 0 -> 8742 bytes testdata/libman/design_au3.gds | Bin 0 -> 7778 bytes testdata/libman/design_au4.gds | Bin 0 -> 7778 bytes testdata/libman/design_au5.gds | Bin 0 -> 7778 bytes testdata/libman/libs/EX.gds | Bin 0 -> 18210 bytes testdata/libman/libs/EX2.gds | Bin 0 -> 986 bytes testdata/libman/libs/NOEX.gds | Bin 0 -> 20476 bytes testdata/libman/libs/NOEX2.gds | Bin 0 -> 1082 bytes 20 files changed, 349 insertions(+), 91 deletions(-) create mode 100644 testdata/libman/design.gds create mode 100644 testdata/libman/design_au1.gds create mode 100644 testdata/libman/design_au2.gds create mode 100644 testdata/libman/design_au3.gds create mode 100644 testdata/libman/design_au4.gds create mode 100644 testdata/libman/design_au5.gds create mode 100644 testdata/libman/libs/EX.gds create mode 100644 testdata/libman/libs/EX2.gds create mode 100644 testdata/libman/libs/NOEX.gds create mode 100644 testdata/libman/libs/NOEX2.gds diff --git a/src/db/db/dbFileBasedLibrary.cc b/src/db/db/dbFileBasedLibrary.cc index 63086fda1..10025e650 100644 --- a/src/db/db/dbFileBasedLibrary.cc +++ b/src/db/db/dbFileBasedLibrary.cc @@ -37,6 +37,11 @@ FileBasedLibrary::FileBasedLibrary (const std::string &path, const std::string & : db::Library (), m_name (name), m_path (path), m_is_loaded (false) { set_description (tl::filename (path)); + + // preliminary name, may be replaced later + if (! name.empty ()) { + set_name (name); + } } void diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 40557f375..9411dcef3 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2987,6 +2987,14 @@ Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrC void Layout::restore_proxies (ImportLayerMapping *layer_mapping) +{ + if (restore_proxies_without_cleanup (layer_mapping)) { + cleanup (); + } +} + +bool +Layout::restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping) { std::vector cold_proxies; @@ -3004,9 +3012,7 @@ Layout::restore_proxies (ImportLayerMapping *layer_mapping) } } - if (needs_cleanup) { - cleanup (); - } + return needs_cleanup; } bool diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index ed62a60ab..e68b5746c 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1115,7 +1115,16 @@ public: * Library updates may enabled lost connections which are help in cold proxies. This method will recover * these connections. */ - void restore_proxies(ImportLayerMapping *layer_mapping = 0); + void restore_proxies (ImportLayerMapping *layer_mapping = 0); + + /** + * @brief Restores proxies as far as possible, no cleanup included + * + * This method is equivalent to "restore_proxies", but does not include a cleanup. + * Instead it returns a value of true, indicating that something got changed + * and a cleanup is required. + */ + bool restore_proxies_without_cleanup (ImportLayerMapping *layer_mapping = 0); /** * @brief Replaces the given cell index with the new cell diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index e3e4c353e..fd457ba57 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -208,110 +208,128 @@ Library::remap_to (db::Library *other, db::Layout *original_layout) // Hint: in the loop over the referrers we might unregister (delete from m_referrers) a referrer because no more cells refer to us. // Hence we must not directly iterate of m_referrers. - std::vector > referrers; + std::vector referrers; for (std::map::const_iterator r = m_referrers.begin (); r != m_referrers.end (); ++r) { - referrers.push_back (*r); + referrers.push_back (r->first); } + // Sort for deterministic order of resolution + std::sort (referrers.begin (), referrers.end (), tl::sort_by_id ()); + // Remember the layouts that will finally need a cleanup std::set needs_cleanup; - for (std::vector >::const_iterator r = referrers.begin (); r != referrers.end (); ++r) { + // NOTE: resolution may create new references due to replicas. + // Hence, loop until no further references are resolved. + bool any = true; + while (any) { - std::vector > pcells_to_map; - std::vector lib_cells_to_map; + any = false; - for (auto c = r->first->begin (); c != r->first->end (); ++c) { + for (std::vector::const_iterator r = referrers.begin (); r != referrers.end (); ++r) { - db::LibraryProxy *lib_proxy = dynamic_cast (c.operator-> ()); - if (lib_proxy && lib_proxy->lib_id () == get_id ()) { + std::vector > pcells_to_map; + std::vector lib_cells_to_map; + + for (auto c = (*r)->begin (); c != (*r)->end (); ++c) { + + db::LibraryProxy *lib_proxy = dynamic_cast (c.operator-> ()); + if (lib_proxy && lib_proxy->lib_id () == get_id ()) { + + if (! original_layout->is_valid_cell_index (lib_proxy->library_cell_index ())) { + // safety feature, should not happen + continue; + } + + db::Cell *lib_cell = &original_layout->cell (lib_proxy->library_cell_index ()); + db::PCellVariant *lib_pcell = dynamic_cast (lib_cell); + if (lib_pcell) { + pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell)); + } else { + lib_cells_to_map.push_back (lib_proxy); + } + + needs_cleanup.insert (*r); + any = true; - db::Cell *lib_cell = &original_layout->cell (lib_proxy->library_cell_index ()); - db::PCellVariant *lib_pcell = dynamic_cast (lib_cell); - if (lib_pcell) { - pcells_to_map.push_back (std::make_pair (lib_proxy, lib_pcell)); - } else { - lib_cells_to_map.push_back (lib_proxy); } - needs_cleanup.insert (r->first); - } - } + // We do PCell resolution before the library proxy resolution. The reason is that + // PCells may generate library proxies in their instantiation. Hence we must instantiate + // the PCells before we can resolve them. + for (std::vector >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) { - // We do PCell resolution before the library proxy resolution. The reason is that - // PCells may generate library proxies in their instantiation. Hence we must instantiate - // the PCells before we can resolve them. - for (std::vector >::const_iterator lp = pcells_to_map.begin (); lp != pcells_to_map.end (); ++lp) { + db::cell_index_type ci = lp->first->Cell::cell_index (); + db::PCellVariant *lib_pcell = lp->second; - db::cell_index_type ci = lp->first->Cell::cell_index (); - db::PCellVariant *lib_pcell = lp->second; + std::pair pn (false, 0); + if (other) { + pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ()); + } - std::pair pn (false, 0); - if (other) { - pn = other->layout ().pcell_by_name (original_layout->cell (lp->first->library_cell_index ()).get_basic_name ().c_str ()); - } - - if (! pn.first) { - - // substitute by a cold proxy - db::LayoutOrCellContextInfo info; - r->first->get_context_info (ci, info); - r->first->create_cold_proxy_as (info, ci); - - } else { - - const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ()); - const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second); - if (! old_pcell_decl || ! new_pcell_decl) { + if (! pn.first) { // substitute by a cold proxy db::LayoutOrCellContextInfo info; - r->first->get_context_info (ci, info); - r->first->create_cold_proxy_as (info, ci); + (*r)->get_context_info (ci, info); + (*r)->create_cold_proxy_as (info, ci); } else { - db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ()); + const db::PCellDeclaration *old_pcell_decl = original_layout->pcell_declaration (lib_pcell->pcell_id ()); + const db::PCellDeclaration *new_pcell_decl = other->layout ().pcell_declaration (pn.second); + if (! old_pcell_decl || ! new_pcell_decl) { + + // substitute by a cold proxy + db::LayoutOrCellContextInfo info; + (*r)->get_context_info (ci, info); + (*r)->create_cold_proxy_as (info, ci); + + } else { + + db::pcell_parameters_type new_parameters = new_pcell_decl->map_parameters (lib_pcell->parameters_by_name ()); + + // coerce the new parameters if requested + try { + db::pcell_parameters_type plist = new_parameters; + new_pcell_decl->coerce_parameters (other->layout (), plist); + plist.swap (new_parameters); + } catch (tl::Exception &ex) { + // ignore exception - we will do that again on update() to establish an error message + tl::error << ex.msg (); + } + + lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters)); - // coerce the new parameters if requested - try { - db::pcell_parameters_type plist = new_parameters; - new_pcell_decl->coerce_parameters (other->layout (), plist); - plist.swap (new_parameters); - } catch (tl::Exception &ex) { - // ignore exception - we will do that again on update() to establish an error message - tl::error << ex.msg (); } - lp->first->remap (other->get_id (), other->layout ().get_pcell_variant (pn.second, new_parameters)); - } } - } + for (std::vector::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) { - for (std::vector::const_iterator lp = lib_cells_to_map.begin (); lp != lib_cells_to_map.end (); ++lp) { + db::cell_index_type ci = (*lp)->Cell::cell_index (); - db::cell_index_type ci = (*lp)->Cell::cell_index (); + std::pair cn (false, 0); + if (other) { + cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ())); + } - std::pair cn (false, 0); - if (other) { - cn = other->layout ().cell_by_name (original_layout->cell_name ((*lp)->library_cell_index ())); - } + if (! cn.first) { - if (! cn.first) { + // substitute by a cold proxy + db::LayoutOrCellContextInfo info; + (*r)->get_context_info (ci, info); + (*r)->create_cold_proxy_as (info, ci); - // substitute by a cold proxy - db::LayoutOrCellContextInfo info; - r->first->get_context_info (ci, info); - r->first->create_cold_proxy_as (info, ci); + } else { - } else { + (*lp)->remap (other->get_id (), cn.second); - (*lp)->remap (other->get_id (), cn.second); + } } diff --git a/src/db/db/dbLibraryManager.cc b/src/db/db/dbLibraryManager.cc index 304c27e9f..b1d837985 100644 --- a/src/db/db/dbLibraryManager.cc +++ b/src/db/db/dbLibraryManager.cc @@ -249,13 +249,38 @@ LibraryManager::register_lib (Library *library) // "restore_proxies" takes care not to re-substitute cold proxies. const tl::weak_collection &cold_proxies = db::ColdProxy::cold_proxies_per_lib_name (library->get_name ()); - std::set to_refresh; + + std::set to_refresh_set; for (tl::weak_collection::const_iterator p = cold_proxies.begin (); p != cold_proxies.end (); ++p) { - to_refresh.insert (const_cast (p->layout ())); + to_refresh_set.insert (const_cast (p->layout ())); } - for (std::set::const_iterator l = to_refresh.begin (); l != to_refresh.end (); ++l) { - (*l)->restore_proxies (0); + // Sort for deterministic order of resolution + std::vector to_refresh (to_refresh_set.begin (), to_refresh_set.end ()); + std::sort (to_refresh.begin (), to_refresh.end (), tl::sort_by_id ()); + + std::set needs_cleanup; + + // NOTE: "restore proxies" can create new proxies, because indirect references. + // Hence we need to repeat the process. + bool any = true; + while (any) { + + any = false; + + for (auto l = to_refresh.begin (); l != to_refresh.end (); ++l) { + if ((*l)->restore_proxies_without_cleanup ()) { + any = true; + needs_cleanup.insert (*l); + } + + } + + } + + // do the cleanup + for (auto l = needs_cleanup.begin (); l != needs_cleanup.end (); ++l) { + (*l)->cleanup (); } // issue the change notification diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index de3f9789d..711eb8f2d 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -135,13 +135,52 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: db::Reader reader (stream); reader.read (layout_au, options); - equal = db::compare_layouts (*subject, layout_au, - (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) - | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) - | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) - | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) - /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ - , tolerance, 100 /*max diff lines*/); + if ((norm & WithoutCellNames) != 0) { + + // in this case, the layouts need to have one top cell + + db::cell_index_type top_subject = 0, top_au = 0; + size_t n_top_subject = 0, n_top_au = 0; + + for (auto t = subject->begin_top_down (); t != subject->end_top_cells (); ++t) { + top_subject = *t; + ++n_top_subject; + } + + for (auto t = layout_au.begin_top_down (); t != layout_au.end_top_cells (); ++t) { + top_au = *t; + ++n_top_au; + } + + if (n_top_subject != 1) { + throw tl::Exception (tl::sprintf ("With smart cell mapping, the subject layout must have a single top cell")); + } + if (n_top_au != 1) { + throw tl::Exception (tl::sprintf ("With smart cell mapping, the reference layout must have a single top cell")); + } + + equal = db::compare_layouts (*subject, top_subject, layout_au, top_au, + (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) + | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) + | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) + | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta + | db::layout_diff::f_smart_cell_mapping) + /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ + , tolerance, 100 /*max diff lines*/); + + + } else { + + equal = db::compare_layouts (*subject, layout_au, + (n > 0 ? db::layout_diff::f_silent : db::layout_diff::f_verbose) + | ((norm & AsPolygons) != 0 ? db::layout_diff::f_boxes_as_polygons + db::layout_diff::f_paths_as_polygons : 0) + | ((norm & WithArrays) != 0 ? 0 : db::layout_diff::f_flatten_array_insts) + | ((norm & WithMeta) == 0 ? 0 : db::layout_diff::f_with_meta) + /*| db::layout_diff::f_no_text_details | db::layout_diff::f_no_text_orientation*/ + , tolerance, 100 /*max diff lines*/); + + } + if (equal && n > 0) { tl::info << tl::sprintf ("Found match on golden reference variant %s", fn); } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 368c1bad8..df013f8a1 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -59,7 +59,8 @@ enum NormalizationMode NoContext = 8, // write tmp file without context AsPolygons = 16, // paths and boxes are treated as polygons WithArrays = 32, // do not flatten arrays - WithMeta = 64 // with meta info + WithMeta = 64, // with meta info + WithoutCellNames = 128 // smart cell name mapping }; /** diff --git a/src/db/unit_tests/dbLibrariesTests.cc b/src/db/unit_tests/dbLibrariesTests.cc index 1d48e852e..bbe74b65d 100644 --- a/src/db/unit_tests/dbLibrariesTests.cc +++ b/src/db/unit_tests/dbLibrariesTests.cc @@ -32,6 +32,8 @@ #include "dbReader.h" #include "dbLayoutDiff.h" #include "dbTestSupport.h" +#include "dbFileBasedLibrary.h" +#include "dbColdProxy.h" #include "tlStream.h" #include "tlStaticObjects.h" #include "tlUnitTest.h" @@ -698,3 +700,137 @@ TEST(6_issue996) CHECKPOINT (); db::compare_layouts (this, ly, tl::testdata () + "/gds/lib_test6b.gds", db::NormalizationMode (db::WriteGDS2 + db::NoContext)); } + +static size_t num_top_cells (const db::Layout &layout) +{ + size_t n = 0; + for (auto t = layout.begin_top_down (); t != layout.end_top_cells (); ++t) { + ++n; + } + return n; +} + +static size_t num_cells (const db::Layout &layout) +{ + size_t n = 0; + for (auto t = layout.begin_top_down (); t != layout.end_top_down (); ++t) { + ++n; + } + return n; +} + +static size_t num_defunct (const db::Layout &layout) +{ + size_t ndefunct = 0; + for (auto c = layout.begin (); c != layout.end (); ++c) { + if (dynamic_cast (c.operator-> ())) { + ++ndefunct; + } + } + return ndefunct; +} + +// monster lib refresh issue +// (monster lib is a layout with manifold library references to existing and non-existing libraries) +TEST(7_monsterlib) +{ + std::pair lib; + + // tabula rasa + lib = db::LibraryManager::instance ().lib_by_name ("EX"); + if (lib.first) { + db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second)); + } + lib = db::LibraryManager::instance ().lib_by_name ("EX2"); + if (lib.first) { + db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second)); + } + lib = db::LibraryManager::instance ().lib_by_name ("NOEX"); + if (lib.first) { + db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second)); + } + lib = db::LibraryManager::instance ().lib_by_name ("NOEX2"); + if (lib.first) { + db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (lib.second)); + } + + // first, read the layout with only EX and EX2 in place + + db::FileBasedLibrary *lib_ex = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/EX.gds", "EX"); + lib_ex->load (); + db::LibraryManager::instance ().register_lib (lib_ex); + db::FileBasedLibrary *lib_ex2 = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/EX2.gds", "EX2"); + lib_ex2->load (); + db::LibraryManager::instance ().register_lib (lib_ex2); + + db::Layout layout; + layout.do_cleanup (true); + + { + tl::InputStream is (tl::testsrc () + "/testdata/libman/design.gds"); + db::Reader reader (is); + reader.read (layout); + } + + // as NOEX and NOEX2 are not present, a number of references are defunct (aka cold proxies) + EXPECT_EQ (num_defunct (layout), size_t (15)); + EXPECT_EQ (num_cells (layout), size_t (46)); + EXPECT_EQ (num_top_cells (layout), size_t (1)); + + // NOTE: normalization would spoil the layout, so don't do it + // The golden layout is a spoiled version that uses preliminary cell versions for the + // unresolved references of NOEX and NOEX2. This is intentional to test the replication. + // Also note, that the golden file has static cells. + db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au1.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons)); + + // then, establish NOEX and NOEX2 too - this will update the libraries in the layout that was read + + db::FileBasedLibrary *lib_noex = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/NOEX.gds", "NOEX"); + lib_noex->load (); + db::LibraryManager::instance ().register_lib (lib_noex); + db::FileBasedLibrary *lib_noex2 = new db::FileBasedLibrary (tl::testsrc () + "/testdata/libman/libs/NOEX2.gds", "NOEX2"); + lib_noex2->load (); + db::LibraryManager::instance ().register_lib (lib_noex2); + + // all references now need to be resolved + EXPECT_EQ (num_defunct (layout), size_t (0)); + EXPECT_EQ (num_cells (layout), size_t (34)); + EXPECT_EQ (num_top_cells (layout), size_t (1)); + + db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au2.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons)); + + // refresh must not change the layout + lib_ex->refresh (); + lib_ex2->refresh (); + lib_noex->refresh (); + lib_noex2->refresh (); + + // all references now need to be resolved + EXPECT_EQ (num_defunct (layout), size_t (0)); + EXPECT_EQ (num_cells (layout), size_t (29)); + EXPECT_EQ (num_top_cells (layout), size_t (1)); + + db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au3.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons)); + + db::LibraryManager::instance ().delete_lib (lib_noex); + db::LibraryManager::instance ().delete_lib (lib_noex2); + + // after removing the libraries, we have defunct cells again + EXPECT_EQ (num_defunct (layout), size_t (8)); + EXPECT_EQ (num_cells (layout), size_t (29)); + EXPECT_EQ (num_top_cells (layout), size_t (1)); + + // but the layout did not change + db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au4.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons)); + + db::LibraryManager::instance ().delete_lib (lib_ex); + db::LibraryManager::instance ().delete_lib (lib_ex2); + + // after removing all libraries, we have even more defunct cells (i.e. all, except top) + EXPECT_EQ (num_defunct (layout), size_t (16)); + EXPECT_EQ (num_cells (layout), size_t (29)); + EXPECT_EQ (num_top_cells (layout), size_t (1)); + + // but the layout did not change + db::compare_layouts (_this, layout, tl::testsrc () + "/testdata/libman/design_au5.gds", db::NormalizationMode (db::NoNormalization | db::WithoutCellNames | db::AsPolygons)); +} diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index e507b7e93..227396c48 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -289,22 +289,30 @@ LibraryController::sync_files () } for (std::map::const_iterator lf = m_lib_files.begin (); lf != m_lib_files.end (); ++lf) { + + std::pair li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech); + if (! li.first) { + continue; // should not happen + } + + db::Library *lib = db::LibraryManager::instance ().lib (li.second); + if (new_names.find (lf->second.name) == new_names.end ()) { + try { - std::pair li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech); - if (li.first) { - if (! lf->second.tech.empty ()) { - tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first; - } else { - tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first; - } - db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (li.second)); + if (! lf->second.tech.empty ()) { + tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first; + } else { + tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first; } + db::LibraryManager::instance ().delete_lib (lib); } catch (tl::Exception &ex) { tl::error << ex.msg (); } catch (...) { } + } + } // establish the new libraries diff --git a/src/tl/tl/tlUniqueId.h b/src/tl/tl/tlUniqueId.h index 54bf1f9d7..8d065a8af 100644 --- a/src/tl/tl/tlUniqueId.h +++ b/src/tl/tl/tlUniqueId.h @@ -75,6 +75,17 @@ inline id_type id_of (const UniqueId *o) return o ? o->m_id : 0; } +/** + * @brief A sorting operator of pointers by ID + */ +struct sort_by_id +{ + bool operator () (const UniqueId *a, const UniqueId *b) const + { + return id_of (a) < id_of (b); + } +}; + } // namespace tl #endif diff --git a/testdata/libman/design.gds b/testdata/libman/design.gds new file mode 100644 index 0000000000000000000000000000000000000000..452c0f56d9564883c63e27486b8fe13c5b417dd5 GIT binary patch literal 41932 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 literal 0 HcmV?d00001 diff --git a/testdata/libman/design_au1.gds b/testdata/libman/design_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..5419c775426281eabf3b77fe528734a0715c6398 GIT binary patch literal 13144 zcmeI2zi%8>5XZ;s^ZMdABq2c%VH6<_@k2=D?$*R$|EGx2vkyg1KNc~{Gkl|k zr8?)ud*1`z$J#Vo6UvW%ejl83^y7bB|2wzF{5S49-ohUu^z-ibR~%&?{ZPiy&$_k_ zl=1LsnH(WGKS9pRH$+Z7bM*WJk@r3nF>3uv)A@_Cul|>u{(A#YBlklVIJ4`d<84JP zA$?KXSYh#nvj$ef3XHdT^q~nTnql2S3+=qE7Fy0Ml&pYuvf$`uT) zZTEbGYrYLmZYT8v74kX5Qr(^}KPPJ^HcEZ%OXS=~ZKEXM%Vza{GF>%2vQ7d~+pO!n z_4m=L@9Un0s#oQ>jyTKWHh9EoP<~XZpv5?^7!TSQ=Fo3-Mx(49iu-%oiF$sen&cSC$1PpZT}(-B4si1X8Scl_Mjf6(Sy}ML0$oYY_~z z&}K1$84jDLv$jSWhT}m1$GbCH$+bJG?KVvUS=(=A-$eFB#T?&6)&#ZxAt2hc?9KcS zWAIZ(>Bmno&gRj_y5_4GPlJ-3;m?ew4EJXieOVI7#%4{&Z9OOY4T~(R=dv1$E%^R1 z-Ppoq62oTYHkw70^t2Yx(~|aCjIG}=JUtfpuUr;VZ%$M1Y6nODn`Qe_#|_y}>k)ct z634s#lm5~ZcgTc{e52CTiwWLMVVY~=_*<#75{HH zU8z9Bq*vpMktb7*-psNkJb-#K)O20|5uB`)7eIu?st42?3<*Z7Yk#v}^51puR8NK0 zCd-gn5*2^r&pr6EJC7Q|)*bha$<0%o=D7UnMUkWD4Nmmc Z)3JxsIjrdXPR=c@SeR!kYQ)>=$p6Pr8m<5U literal 0 HcmV?d00001 diff --git a/testdata/libman/design_au2.gds b/testdata/libman/design_au2.gds new file mode 100644 index 0000000000000000000000000000000000000000..c3e3d4925b4d600a192c13659ee3e3db34567572 GIT binary patch literal 8742 zcmeI2KW`LC5XH;m^>{G`n?EOnFhb~j;*XHpi=s7~ z8j40g*svY3eKu#~*?Vh7@zu*qX8VUn&M!V*xbpVjkLNnCPCtM8&#Fz0Y%nj5S69|n zt_dGHC^SGH%{f^1}QQI*7V0wgl5n`vcl{<|_Xo-))v+soRaP2MGX-W_rR9%|IFR}Dfz({q63TAgxc9bQ zTe}Lz#ORzgntA>4rtI|VXOM`BA#5-La&gjuSw^$WYYcPfmrfC~b{WH*@ANZH^FYS4 zW~Oa3-D6tyuf0&Le_M6;*1cXT=CgCfnEz_~tL|LHaeQ&c8e?Y7z2S>%oC?P|;a5^A zcH}Eye3Y-T=0rHzlJAb8PiDGKG|bMvFcYfZ@p}|o6Mv???$*qs`FeTOnc>UKV>A|6 z^S=$2H}c5!PsXG6x*lmy{QOL6rI|FIMu~5w_2RT?UUgb2ck&ADJy@@#cNPkI=T@2C zL04oIYroz>gG{*Q0Hs@mjFm0{Gi_U!^mK_B@w(H@*WYLTjG!dZ`7nY41oe9gbzDkR zQD+S72=~<(3SFL=&g+?0@r(}sCVDkFzt2DDspeoybXsD&=M_?l+HLV( z^ipE^FEgu})<$Q4aqTUvsI^_QEeGu{6}izn&IfO|-a9Vk-_ietYS3Re%dIrGqyYK# zq2N`TU1-?ERuXxXWMV7Hu9;vd$u3o>Z|HAjm9TntErlJVx8!@j@Rn)Df2U9SsuWiD zX(gWx3q_AY+!K!QcId*j+|Kpy^Boud=uA)cu}E7rEgl2MSM%H zmqj(GQHy!T(mIs=g(B2b-{(Kb37LLBt*2l`PQ0G3QWF=#aG ED+j(2G5`Po literal 0 HcmV?d00001 diff --git a/testdata/libman/design_au3.gds b/testdata/libman/design_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..10b4c7ba8be4ade0033f738d8979e21197914ea9 GIT binary patch literal 7778 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 literal 0 HcmV?d00001 diff --git a/testdata/libman/libs/NOEX2.gds b/testdata/libman/libs/NOEX2.gds new file mode 100644 index 0000000000000000000000000000000000000000..1d86242b2aef8e1a5763951c17b8dd4336f1235c GIT binary patch literal 1082 zcma)5O=}ZD7=CtkGug&Arlm+JDJTghu>seDMFLBqC4oXndgviwC9KIt18FMV#gDsp zdno-8{s2#&iiiG`(t{U24xW79nOVDP{TLXYo#%N!p4pdygb+OxEs?!|jTOwJfkOHl zIUzgUW^WBxv@;vkFQ;$zs$Uy-KCOHRmSPk$f6lxiWw~75>UMUUz1{wHr`4q+oE+dT zNaiqGu#qgeDv(FME_b$??Y6IU=H^f!t{R6g{sQ7hV$4>E2 zIsSp>h4wr6{hpToFOSDT#PR0#V2|zXO@A|p#sk2ar=7)Lr{_vNBgZ9=a{>Y~FKAeM zshO$SiT8l@8Tf%cqK9-gXdRJWr=1{uv(6l2YAIj8^gHH$T$@)?lTmHrS+mbII&H&$ zOzF3^h7&^Kxu^ZkAmuTy@+ppauA9_hRPh`~%lNp;XPtf8H!0|wM(dRDHQm!-!V~`Q z|L^YndrnfymUqRgIwL1%y3@?h6O^}jn(lRedqXJSpy!=zXBo)6rN#ymo-pz4IQR|> CCXxC8 literal 0 HcmV?d00001