diff --git a/libdnf5/rpm/solv/goal_private.cpp b/libdnf5/rpm/solv/goal_private.cpp index 6573c9bae..188087a5b 100644 --- a/libdnf5/rpm/solv/goal_private.cpp +++ b/libdnf5/rpm/solv/goal_private.cpp @@ -19,6 +19,7 @@ along with libdnf. If not, see . #include "goal_private.hpp" +#include "rpm/package_sack_impl.hpp" #include "solv/pool.hpp" #include "libdnf5/common/exception.hpp" @@ -223,6 +224,9 @@ int obsq_cmp(const Id * ap, const Id * bp, const ObsoleteCmpData * s_cb) { return *ap - *bp; } +inline bool name_solvable_cmp_key(const Solvable * first, const Solvable * second) { + return first->name < second->name; +} } // namespace @@ -243,14 +247,27 @@ bool GoalPrivate::limit_installonly_packages(libdnf5::solv::IdQueue & job, Id ru Id p; Id pp; libdnf5::solv::IdQueue q; + std::vector available_unused_providers; libdnf5::solv::IdQueue installing; + + // 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_PROVIDES(p, pp, installonly[i]) { // TODO(jmracek) Replase the test by cached data from sack.p_impl->get_solvables() if (!spool.is_package(p)) { continue; } + // 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 (libsolv_solver.get_decisionlevel(p) > 0) { q.push_back(p); + } else { + Solvable * solvable = spool.id2solvable(p); + if (!spool.is_installed(solvable)) { + available_unused_providers.push_back(solvable); + } } } if (q.size() <= static_cast(installonly_limit)) { @@ -270,6 +287,7 @@ bool GoalPrivate::limit_installonly_packages(libdnf5::solv::IdQueue & job, Id ru const InstallonlyCmpData installonly_cmp_data{spool, running_kernel}; q.sort(&installonly_cmp, &installonly_cmp_data); + std::sort(available_unused_providers.begin(), available_unused_providers.end(), name_solvable_cmp_key); libdnf5::solv::IdQueue same_names; while (q.size() > 0) { @@ -283,6 +301,19 @@ bool GoalPrivate::limit_installonly_packages(libdnf5::solv::IdQueue & job, Id ru Id action = SOLVER_ERASE; if (j < static_cast(installonly_limit)) { 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 * solvable = spool.id2solvable(id); + auto low = std::lower_bound( + available_unused_providers.begin(), + available_unused_providers.end(), + solvable, + nevra_solvable_cmp_key); + while (low != available_unused_providers.end() && (*low)->name == solvable->name) { + job.push_back(SOLVER_ERASE | SOLVER_SOLVABLE, spool.solvable2id(*low)); + ++low; + } } job.push_back(action | SOLVER_SOLVABLE, id); }