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 &,