From abb0f1de7830cd76341d1e32ffce3bf3813bf2b9 Mon Sep 17 00:00:00 2001 From: renbin Date: Wed, 16 Oct 2024 13:33:16 +0800 Subject: [PATCH] fix: failed to detect replaceable packages The current version only detects the 'replace' field, 'or depends', 'provides depends' of reverse dependency packages not recognized. Add targetPackageCanReplace() to detect 'or depends', 'provides depends'. If the installed package can be replaced, continue installation. Log: Fix uable to install replaceable packages. Bug: https://pms.uniontech.com/bug-view-275221.html --- src/deb-installer/manager/packagesmanager.cpp | 93 ++++++++++++++++++- src/deb-installer/manager/packagesmanager.h | 6 +- tests/src/manager/ut_packagemanager.cpp | 24 ++--- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/deb-installer/manager/packagesmanager.cpp b/src/deb-installer/manager/packagesmanager.cpp index 4a023956..eee1ef72 100644 --- a/src/deb-installer/manager/packagesmanager.cpp +++ b/src/deb-installer/manager/packagesmanager.cpp @@ -409,7 +409,7 @@ const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, Pac return ret_installed; } - const auto conflictStatus = isConflictSatisfy(arch, package->conflicts(), package->replaces()); + const auto conflictStatus = isConflictSatisfy(arch, package->conflicts(), package->replaces(), package); return conflictStatus; } @@ -484,7 +484,8 @@ const ConflictResult PackagesManager::isInstalledConflict(const QString &package const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, const QList &conflicts, - const QList &replaces) + const QList &replaces, + QApt::Package *targetPackage) { for (const auto &conflict_list : conflicts) { for (const auto &conflict : conflict_list) { @@ -550,6 +551,11 @@ const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, } } + // check current package and conflict package provides same package, can be replaced + if (conflict_yes && targetPackage) { + conflict_yes = !targetPackageCanReplace(targetPackage, package); + } + if (!conflict_yes) { package = nullptr; continue; @@ -566,6 +572,87 @@ const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, return ConflictResult::ok(QString()); } +/** + @brief Check if \a targetPackage can replace conflict package \a installedPackage. + If \a targetPackage provides \a installedPackage 's reverse depends package dependencies, + @return True if \a targetPackage meets provides depends or or depends, otherwise false. + */ +bool PackagesManager::targetPackageCanReplace(QApt::Package *targetPackage, QApt::Package *installedPackage) +{ + if (!targetPackage || !installedPackage) { + return false; + } + + Backend *backend = PackageAnalyzer::instance().backendPtr(); + if (!backend) { + return false; + } + + auto rdepends = installedPackage->requiredByList().toSet(); + // itself package + rdepends.remove(targetPackage->name()); + // conflict package + rdepends.remove(installedPackage->name()); + + // provides package + auto targetProvides = targetPackage->providesList().toSet(); + auto installedProvides = installedPackage->providesList().toSet(); + auto canReplaceProvides = targetProvides.subtract(installedProvides); + + bool replaceable = false; + bool containsInstalledProvides = false; + + // requiredByList contains depends, conflicts, recommends, etc. + // we focus on depends, so only check provides depends and or depends. + for (const QString &rdependName: rdepends) { + QApt::Package *rdependPackage = backend->package(rdependName); + if (!rdependPackage || !rdependPackage->isInstalled()) { + continue; + } + + QList rdependsDep = rdependPackage->depends(); + for (const DependencyItem &item : rdependsDep) { + replaceable = false; + + // or depends + for (const DependencyInfo &info : item) { + // support provides + if (canReplaceProvides.contains(info.packageName())) { + replaceable = true; + containsInstalledProvides = true; + break; + } + + // support depends + if (info.packageName() == targetPackage->name()) { + replaceable = true; + break; + } + + if (info.packageName() == installedPackage->name()) { + containsInstalledProvides = true; + } + } + + // current or depends contains installedPackage but not contains targetPackage. + if (!replaceable && containsInstalledProvides) { + qWarning() << QString("Package (%1) can't replace (%2), not support (%3)") + .arg(targetPackage->name()) + .arg(installedPackage->name()) + .arg(rdependPackage->name()); + return false; + } + + // the current package satisfies the constraint + if (replaceable) { + break; + } + } + } + + return true; +} + const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, const QList &conflicts) { for (const auto &conflict_list : conflicts) { @@ -1857,9 +1944,9 @@ const PackageDependsStatus PackagesManager::checkDependsPackageStatus(QSetavailablePackages()) { if (!availablePackage->providesList().contains(package->name())) { diff --git a/src/deb-installer/manager/packagesmanager.h b/src/deb-installer/manager/packagesmanager.h index 9bf20c46..f913ba65 100644 --- a/src/deb-installer/manager/packagesmanager.h +++ b/src/deb-installer/manager/packagesmanager.h @@ -376,7 +376,11 @@ public slots: //带replaces的检查,如果判定待安装包可以替换冲突包,则认为不构成冲突 const ConflictResult isConflictSatisfy(const QString &arch, const QList &conflicts, - const QList &replaces); + const QList &replaces, + QApt::Package *targetPackage = nullptr); + + // detect if targetPackage can replace installedPackage + bool targetPackageCanReplace(QApt::Package *targetPackage, QApt::Package *installedPackage); //// 依赖查找 获取查找包是否为消极的反向依赖 private: diff --git a/tests/src/manager/ut_packagemanager.cpp b/tests/src/manager/ut_packagemanager.cpp index 152526c3..89eb3ffb 100644 --- a/tests/src/manager/ut_packagemanager.cpp +++ b/tests/src/manager/ut_packagemanager.cpp @@ -827,12 +827,14 @@ TEST_F(UT_packagesManager, PackageManager_UT_isConflictSatisfy_0003) ASSERT_FALSE(cr.is_ok()); } -const ConflictResult stub_isConflictSatisfy(const QString &, const QList &, const QList &) +const ConflictResult stub_isConflictSatisfy(const QString &, const QList &, const QList &, + QApt::Package *) { return ConflictResult::ok("1"); } -const ConflictResult stub_isConflictSatisfy_error(const QString &, const QList &, const QList &) +const ConflictResult stub_isConflictSatisfy_error(const QString &, const QList &, const QList &, + QApt::Package *) { return ConflictResult::err("1"); } @@ -855,7 +857,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_isConflictSatisfy_0004) stub.set(ADDR(Package, replaces), deb_replaces_null); stub.set(ADDR(PackagesManager, isInstalledConflict), stub_isInstalledConflict_ok); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); ConflictResult cr = m_packageManager->isConflictSatisfy("i386", &package); @@ -1167,7 +1169,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_getPackageDependsStatus_05) stub.set(ADDR(Package, isInstalled), stub_isInstalled); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy_error); PackageDependsStatus pd = m_packageManager->getPackageDependsStatus(0); @@ -1224,7 +1226,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_getPackageDependsStatus_06) stub.set(ADDR(PackagesManager, dealInvalidPackage), stub_dealInvalidPackage); stub.set(ADDR(PackagesManager, isBlackApplication), stub_isBlackApplication_false); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &, @@ -1290,7 +1292,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_getPackageDependsStatus_07) stub.set(ADDR(PackagesManager, dealInvalidPackage), stub_dealInvalidPackage); stub.set(ADDR(PackagesManager, isBlackApplication), stub_isBlackApplication_false); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, @@ -2025,7 +2027,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_packageCandidateChoose) stub.set(ADDR(Package, depends), deb_conflicts_null); stub.set((QApt::Package * (QApt::Backend::*)(const QString & name) const)ADDR(Backend, package), packagesManager_package); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &, @@ -2061,7 +2063,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_packageCandidateChoose_1) stub.set(ADDR(Package, depends), deb_conflicts_null); stub.set((QApt::Package * (QApt::Backend::*)(const QString & name) const)ADDR(Backend, package), packagesManager_package); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &, @@ -2097,7 +2099,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_packageCandidateChoose_2) stub.set(ADDR(Package, depends), deb_conflicts_null); stub.set((QApt::Package * (QApt::Backend::*)(const QString & name) const)ADDR(Backend, package), package_package); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &, @@ -2133,7 +2135,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_packageCandidateChoose_3) stub.set(ADDR(Package, depends), deb_conflicts_null); stub.set((QApt::Package * (QApt::Backend::*)(const QString & name) const)ADDR(Backend, package), package_package); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy_error); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &, const QList &)) @@ -2168,7 +2170,7 @@ TEST_F(UT_packagesManager, PackageManager_UT_packageCandidateChoose_4) stub.set(ADDR(Package, depends), deb_conflicts_null); stub.set((QApt::Package * (QApt::Backend::*)(const QString & name) const)ADDR(Backend, package), package_package); - stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &)) + stub.set((const ConflictResult(PackagesManager::*)(const QString &, const QList &, const QList &, QApt::Package*)) ADDR(PackagesManager, isConflictSatisfy), stub_isConflictSatisfy_error); stub.set((const PackageDependsStatus(PackagesManager::*)(QSet &, const QString &,