From b64c69d47a3f317d46d3f35ff74c7120aa1c7ad2 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 | 116 +++++++++++++++++- src/deb-installer/manager/packagesmanager.h | 6 +- tests/src/manager/ut_packagemanager.cpp | 24 ++-- 3 files changed, 131 insertions(+), 15 deletions(-) diff --git a/src/deb-installer/manager/packagesmanager.cpp b/src/deb-installer/manager/packagesmanager.cpp index 4a023956..5c61ad24 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,110 @@ 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->providesListEnhance(); + auto installedProvides = installedPackage->providesListEnhance(); + QMap canReplaceProvides; + for (auto itr = installedProvides.begin(); itr != installedProvides.end(); ++itr) { + if (targetProvides.contains(itr.key())) { + canReplaceProvides.insert(itr.key(), targetProvides.value(itr.key())); + } + } + + 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())) { + containsInstalledProvides = true; + + // check version match + QString version = canReplaceProvides.value(info.packageName()); + if (!version.isEmpty()) { + const auto type = info.relationType(); + const auto result = Package::compareVersion(version, info.packageVersion()); + if (!dependencyVersionMatch(result, type)) { + replaceable = false; + break; + } + } + + replaceable = true; + break; + } + + // support depends + if (info.packageName() == targetPackage->name()) { + const auto type = info.relationType(); + const auto result = Package::compareVersion(targetPackage->version(), info.packageVersion()); + if (!dependencyVersionMatch(result, type)) { + replaceable = false; + } + + 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 +1967,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 &,