From 2d2dc7ccb2fff55fea9d5a87e98411d04f175f16 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 21 May 2024 22:37:35 +0300 Subject: Fix post-simulation assertion in pkg-build (GH issue #382) --- bpkg/pkg-build.cxx | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) (limited to 'bpkg') diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index fac79c2..adf7938 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -6871,12 +6871,81 @@ namespace bpkg // if (!rescan) { + // Return true if the specified package is loaded as a + // prerequisite of some dependent package cached in the session + // and contained in a different database. Also unload this + // package from all such dependents. + // + auto unload_prereq = [&ses, &sp_session] + (const shared_ptr& sp, + const odb::database* db) + { + bool r (false); + + for (const auto& dps: ses.map ()) + { + // Skip dependents from the same database. + // + if (dps.first == db) + continue; + + if (const selected_packages* sps = sp_session (dps.second)) + { + for (const auto& p: *sps) + { + for (auto& pr: p.second->prerequisites) + { + const lazy_shared_ptr& lp (pr.first); + + if (lp.loaded () && lp.get_eager () == sp) + { + lp.unload (); + r = true; + } + } + } + } + } + + return r; + }; + for (const auto& dps: ses.map ()) { if (const selected_packages* sps = sp_session (dps.second)) { if (old_sp.find (dps.first) == old_sp.end ()) - assert (sps->empty ()); + { + // Note that the reason for these packages to still be + // present in the session is that they may be referenced + // as prerequisites by some dependent packages from other + // databases. For example: + // + // new session: A (D1, 2) -> B (D1, 2) -> C (D2, 2) + // old session: A + // + // Here C is the package in question, package A is present + // in both sessions, D* are databases, the numbers are the + // package reference counts, and the arrows denote the + // loaded prerequisite lazy pointers. + // + // Let's verify that by unloading these packages from such + // dependents and rescan. + // + if (!sps->empty ()) + { + for (const auto& p: *sps) + { + if (unload_prereq (p.second, dps.first)) + rescan = true; + } + + // If we didn't unload any of these packages, then we + // consider this as a bug. + // + assert (rescan); + } + } } } } -- cgit v1.1