From 4b016cee3ff42c36349714c1b38b394b4317174f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Mon, 25 Sep 2023 08:24:40 +0200 Subject: [PATCH] Avoid reinstalling installonly packages marked for ERASE Without this patch reinstalling installonly pkg marked for ERASE might be a valid smallest solution to our job. For example when user wants to install through a provide we select all packages that provide it and put them inside a `job install oneof ...` if one of the providers is also marked for ERASE due to installonly limit libsolv might decide to reinstall it. To make sure it doesn't happen mark the available package also as ERASE. https://github.com/openSUSE/libsolv/issues/540 https://issues.redhat.com/browse/RHEL-1253 (https://bugzilla.redhat.com/show_bug.cgi?id=2163474) Conflict:NA Reference:https://github.com/rpm-software-management/libdnf/commit/4b016cee3ff42c36349714c1b38b394b4317174f --- libdnf/goal/Goal.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/libdnf/goal/Goal.cpp b/libdnf/goal/Goal.cpp index 42c078286..269f6b398 100644 --- a/libdnf/goal/Goal.cpp +++ b/libdnf/goal/Goal.cpp @@ -643,6 +643,12 @@ erase_flags2libsolv(int flags) return ret; } +static bool +NameSolvableComparator(const Solvable * first, const Solvable * second) +{ + return first->name < second->name; +} + Goal::Goal(const Goal & goal_src) : pImpl(new Impl(*goal_src.pImpl)) {} Goal::Impl::Impl(const Goal::Impl & goal_src) @@ -1436,10 +1442,24 @@ Goal::Impl::limitInstallonlyPackages(Solver *solv, Queue *job) for (int i = 0; i < onlies->count; ++i) { Id p, pp; IdQueue q, installing; + std::vector available_unused_providers; + // Add all providers of installonly provides that are marked for install + // to `q` IdQueue those that are not marked for install and are not already + // installed are added to available_unused_providers. FOR_PKG_PROVIDES(p, pp, onlies->elements[i]) - if (solver_get_decisionlevel(solv, p) > 0) + // According to libsolv-bindings the decision level is positive for installs + // and negative for conflicts (conflicts with another package or dependency + // conflicts = dependencies cannot be met). + if (solver_get_decisionlevel(solv, p) > 0) { q.pushBack(p); + } else { + Solvable *s = pool_id2solvable(pool, p); + if (s->repo != pool->installed) { + available_unused_providers.push_back(s); + } + } + if (q.size() <= (int) dnf_sack_get_installonly_limit(sack)) { continue; } @@ -1457,6 +1477,7 @@ Goal::Impl::limitInstallonlyPackages(Solver *solv, Queue *job) struct InstallonliesSortCallback s_cb = {pool, dnf_sack_running_kernel(sack)}; solv_sort(q.data(), q.size(), sizeof(q[0]), sort_packages, &s_cb); + std::sort(available_unused_providers.begin(), available_unused_providers.end(), NameSolvableComparator); IdQueue same_names; while (q.size() > 0) { same_name_subqueue(pool, q.getQueue(), same_names.getQueue()); @@ -1466,8 +1487,18 @@ Goal::Impl::limitInstallonlyPackages(Solver *solv, Queue *job) for (int j = 0; j < same_names.size(); ++j) { Id id = same_names[j]; Id action = SOLVER_ERASE; - if (j < (int) dnf_sack_get_installonly_limit(sack)) + if (j < (int) dnf_sack_get_installonly_limit(sack)) { action = SOLVER_INSTALL; + } else { + // We want to avoid reinstalling packages marked for ERASE, therefore + // if some unused provider is also available we need to mark it ERASE as well. + Solvable *s = pool_id2solvable(pool, id); + auto low = std::lower_bound(available_unused_providers.begin(), available_unused_providers.end(), s, NameSolvableComparator); + while (low != available_unused_providers.end() && (*low)->name == s->name) { + queue_push2(job, SOLVER_ERASE | SOLVER_SOLVABLE, pool_solvable2id(pool, *low)); + ++low; + } + } queue_push2(job, action | SOLVER_SOLVABLE, id); } }