代码拉取完成,页面将自动刷新
/*
* Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "packagesmanager.h"
#include "deblistmodel.h"
#include <QtConcurrent>
#include <QSet>
#include <QPair>
#include <qapplication.h>
#include <qapt/globals.h>
#include <qapt/package.h>
#include <qobject.h>
using namespace QApt;
QString relationName(const RelationType type)
{
switch (type)
{
case LessOrEqual: return "<=";
case GreaterOrEqual: return ">=";
case LessThan: return "<";
case GreaterThan: return ">";
case Equals: return "=";
case NotEqual: return "!=";
default:;
}
return QString();
}
bool isArchMatches(QString sysArch, const QString &packageArch, const int multiArchType)
{
Q_UNUSED(multiArchType);
if (sysArch.startsWith(':'))
sysArch.remove(0, 1);
if (sysArch == "all" || sysArch == "any")
return true;
// if (multiArchType == MultiArchForeign)
// return true;
return sysArch == packageArch;
}
QString resolvMultiArchAnnotation(const QString &annotation, const QString &debArch, const int multiArchType = InvalidMultiArchType)
{
if (annotation == "native" || annotation == "any")
return QString();
if (multiArchType == MultiArchForeign)
return QString();
QString arch;
if (annotation.isEmpty())
arch = debArch;
else
arch = annotation;
if (!arch.startsWith(':') && !arch.isEmpty())
return arch.prepend(':');
else
return arch;
}
bool dependencyVersionMatch(const int result, const RelationType relation)
{
switch (relation)
{
case LessOrEqual: return result <= 0;
case GreaterOrEqual: return result >= 0;
case LessThan: return result < 0;
case GreaterThan: return result > 0;
case Equals: return result == 0;
case NotEqual: return result != 0;
default:;
}
return true;
}
QSharedPointer<Backend> init_backend()
{
auto b = QSharedPointer<Backend>(new Backend(), &QObject::deleteLater);
if (b->init())
return b;
qFatal("%s", b->initErrorMessage().toStdString().c_str());
return nullptr;
}
PackagesManager::PackagesManager(QObject *parent)
: QObject(parent)
{
m_backendFuture = QtConcurrent::run(init_backend);
}
bool PackagesManager::isBackendReady()
{
return m_backendFuture.isFinished();
}
bool PackagesManager::isArchError(const int idx)
{
auto b = m_backendFuture.result();
auto deb = m_preparedPackages[idx];
const QString arch = deb->architecture();
if (arch == "all" || arch == "any")
return false;
return !b->architectures().contains(deb->architecture());
}
const ConflictResult PackagesManager::packageConflictStat(const int index)
{
auto p = m_preparedPackages[index];
return isConflictSatisfy(p->architecture(), p->conflicts());
}
const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, Package *package)
{
const QString &name = package->name();
qDebug() << "check conflict for package" << name << arch;
const auto ret_installed = isInstalledConflict(name, package->version(), package->architecture());
if (!ret_installed.is_ok())
return ret_installed;
// if (!ret_installed.is_ok())
// {
// bool found_provider = false;
// // check providers
// Backend *b = m_backendFuture.result();
// for (auto *ap : b->availablePackages())
// {
// if (ap->providesList().contains(name))
// {
// found_provider = true;
// break;
// }
// }
// // not found providers, return error
// if (!found_provider)
// return ret_installed;
// }
qDebug() << "check conflict for local installed package is ok.";
const auto ret_package = isConflictSatisfy(arch, package->conflicts());
qDebug() << "check finished, conflict is satisfy:" << package->name() << bool(ret_package.is_ok());
return ret_package;
}
const ConflictResult PackagesManager::isInstalledConflict(const QString &packageName, const QString &packageVersion, const QString &packageArch)
{
static QList<QPair<QString, DependencyInfo>> sysConflicts;
if (sysConflicts.isEmpty())
{
auto b = m_backendFuture.result();
for (Package *p : b->availablePackages())
{
if (!p->isInstalled())
continue;
const auto &conflicts = p->conflicts();
if (conflicts.isEmpty())
continue;
for (const auto &conflict_list : conflicts)
for (const auto &conflict : conflict_list)
sysConflicts << QPair<QString, DependencyInfo>(p->name(), conflict);
}
}
for (const auto &info : sysConflicts)
{
const auto &conflict = info.second;
const auto &pkgName = conflict.packageName();
const auto &pkgVersion = conflict.packageVersion();
const auto &pkgArch = conflict.multiArchAnnotation();
if (pkgName != packageName)
continue;
qDebug() << pkgName << pkgVersion << pkgArch;
// pass if arch not match
if (!pkgArch.isEmpty() && pkgArch != packageArch && pkgArch != "any" && pkgArch != "native")
continue;
if (pkgVersion.isEmpty())
return ConflictResult::err(info.first);
const int relation = Package::compareVersion(packageVersion, conflict.packageVersion());
// match, so is bad
if (dependencyVersionMatch(relation, conflict.relationType()))
return ConflictResult::err(info.first);
}
return ConflictResult::ok(QString());
}
const ConflictResult PackagesManager::isConflictSatisfy(const QString &arch, const QList<DependencyItem> &conflicts)
{
for (const auto &conflict_list : conflicts)
{
for (const auto &conflict : conflict_list)
{
const QString name = conflict.packageName();
Package *p = std::get<0>(packageWithArch(name, arch, conflict.multiArchAnnotation()));
if (!p || !p->isInstalled())
continue;
// arch error, conflicts
if (!isArchMatches(arch, p->architecture(), p->multiArchType()))
{
qDebug() << "conflicts package installed: " << arch << p->name() << p->architecture() << p->multiArchTypeString();
return ConflictResult::err(name);
}
const QString conflict_version = conflict.packageVersion();
const QString installed_version = p->installedVersion();
const auto type = conflict.relationType();
const auto result = Package::compareVersion(installed_version, conflict_version);
// not match, ok
if (!dependencyVersionMatch(result, type))
continue;
// test package
const QString mirror_version = p->availableVersion();
if (mirror_version == installed_version)
continue;
// mirror version is also break
const auto mirror_result = Package::compareVersion(mirror_version, conflict_version);
if (dependencyVersionMatch(mirror_result, type))
{
qDebug() << "conflicts package installed: " << arch << p->name() << p->architecture() << p->multiArchTypeString() << mirror_version << conflict_version;
return ConflictResult::err(name);
}
}
}
return ConflictResult::ok(QString());
}
int PackagesManager::packageInstallStatus(const int index)
{
if (m_packageInstallStatus.contains(index))
return m_packageInstallStatus[index];
const QString packageName = m_preparedPackages[index]->packageName();
const QString packageArch = m_preparedPackages[index]->architecture();
auto b = m_backendFuture.result();
Package *p = b->package(packageName + ":" + packageArch);
int ret = DebListModel::NotInstalled;
do {
if (!p)
break;
const QString installedVersion = p->installedVersion();
if (installedVersion.isEmpty())
break;
const QString packageVersion = m_preparedPackages[index]->version();
const int result = Package::compareVersion(packageVersion, installedVersion);
if (result == 0)
ret = DebListModel::InstalledSameVersion;
else if (result < 0)
ret = DebListModel::InstalledLaterVersion;
else
ret = DebListModel::InstalledEarlierVersion;
} while (false);
m_packageInstallStatus.insert(index, ret);
return ret;
}
PackageDependsStatus PackagesManager::packageDependsStatus(const int index)
{
if (m_packageDependsStatus.contains(index))
return m_packageDependsStatus[index];
if (isArchError(index))
return PackageDependsStatus::_break(QString());
auto deb = m_preparedPackages[index];
const QString architecture = deb->architecture();
PackageDependsStatus ret = PackageDependsStatus::ok();
// Check if some installed packages are present in current package's conflicts part.
const ConflictResult debConflitsResult = isConflictSatisfy(architecture, deb->conflicts());
if (!debConflitsResult.is_ok())
{
qDebug() << "depends break because conflict" << deb->packageName();
ret.package = debConflitsResult.unwrap();
ret.status = DebListModel::DependsBreak;
} else {
// Check if some installed packages list current package as a conflict item.
const ConflictResult localConflictsResult = isInstalledConflict(deb->packageName(), deb->version(), architecture);
if (!localConflictsResult.is_ok())
{
qDebug() << "depends break because conflict with local package" << deb->packageName();
ret.package = localConflictsResult.unwrap();
ret.status = DebListModel::DependsBreak;
} else {
qDebug() << "depends:";
qDebug() << "Check for package" << deb->packageName();
QSet<QString> choose_set;
choose_set << deb->packageName();
ret = checkDependsPackageStatus(choose_set, deb->architecture(), deb->depends());
}
}
if (ret.isBreak())
Q_ASSERT(!ret.package.isEmpty());
m_packageDependsStatus[index] = ret;
qDebug() << "Check finished for package" << deb->packageName() << ret.status;
if (ret.status == DebListModel::DependsAvailable)
{
const auto list = packageAvailableDepends(index);
qDebug() << "available depends:" << list.size() << list;
}
return ret;
}
const QString PackagesManager::packageInstalledVersion(const int index)
{
Q_ASSERT(m_packageInstallStatus.contains(index));
Q_ASSERT(m_packageInstallStatus[index] == DebListModel::InstalledEarlierVersion ||
m_packageInstallStatus[index] == DebListModel::InstalledLaterVersion);
auto b = m_backendFuture.result();
Package *p = b->package(m_preparedPackages[index]->packageName());
return p->installedVersion();
}
const QStringList PackagesManager::packageAvailableDepends(const int index)
{
Q_ASSERT(m_packageDependsStatus.contains(index));
Q_ASSERT(m_packageDependsStatus[index].isAvailable());
auto deb = m_preparedPackages[index];
QSet<QString> choose_set;
const QString debArch = deb->architecture();
const auto &depends = deb->depends();
packageCandidateChoose(choose_set, debArch, depends);
// TODO: check upgrade from conflicts
return choose_set.values();
}
void PackagesManager::packageCandidateChoose(QSet<QString> &choosed_set, const QString &debArch, const QList<DependencyItem> &dependsList)
{
for (auto const &candidate_list : dependsList)
packageCandidateChoose(choosed_set, debArch, candidate_list);
}
void PackagesManager::packageCandidateChoose(QSet<QString> &choosed_set, const QString &debArch, const DependencyItem &candidateList)
{
bool choosed = false;
for (const auto &info : candidateList)
{
Package *dep = std::get<0>(packageWithArch(info.packageName(), debArch, info.multiArchAnnotation()));
if (!dep)
continue;
const auto choosed_name = dep->name() + resolvMultiArchAnnotation(QString(), dep->architecture());
if (choosed_set.contains(choosed_name))
{
choosed = true;
break;
}
if (!dep->installedVersion().isEmpty())
{
// Only check for update if package requirement is >= or >
if (info.relationType() == QApt::RelationType::GreaterOrEqual || info.relationType() == QApt::RelationType::GreaterThan)
{
auto compare_result = QApt::Package::compareVersion(dep->installedVersion(), dep->version());
if (compare_result <= 0) {
// Try to update
// pass if break
QSet<QString> set = choosed_set;
set << choosed_name;
const auto stat = checkDependsPackageStatus(set, dep->architecture(), dep->depends());
if (stat.isBreak())
{
qDebug() << "depends error in choose candidate" << dep->name();
}
else
{
choosed = true;
choosed_set << choosed_name;
packageCandidateChoose(choosed_set, debArch, dep->depends());
}
}
}
return; // Got local version, lol
}
}
for (const auto &info : candidateList)
{
Package *dep = std::get<0>(packageWithArch(info.packageName(), debArch, info.multiArchAnnotation()));
if (!dep)
continue;
const auto choosed_name = dep->name() + resolvMultiArchAnnotation(QString(), dep->architecture());
if (choosed_set.contains(choosed_name))
{
choosed = true;
break;
}
// Upgrade is checked above
// Thus only check for install
if (!dep->installedVersion().isEmpty())
return;
if (!isConflictSatisfy(debArch, dep->conflicts()).is_ok())
{
qDebug() << "conflict error in choose candidate" << dep->name();
continue;
}
// pass if break
QSet<QString> set = choosed_set;
set << choosed_name;
const auto stat = checkDependsPackageStatus(set, dep->architecture(), dep->depends());
if (stat.isBreak())
{
qDebug() << "depends error in choose candidate" << dep->name();
continue;
}
choosed = true;
choosed_set << choosed_name;
packageCandidateChoose(choosed_set, debArch, dep->depends());
break;
}
Q_ASSERT(choosed);
}
const QStringList PackagesManager::packageReverseDependsList(const QString &packageName, const QString &sysArch)
{
Package *p = std::get<0>(packageWithArch(packageName, sysArch));
Q_ASSERT(p);
QSet<QString> ret { packageName };
QQueue<QString> testQueue;
for (const auto &item : p->requiredByList().toSet())
testQueue.append(item);
while (!testQueue.isEmpty())
{
const auto item = testQueue.first();
testQueue.pop_front();
if (ret.contains(item))
continue;
Package *p = std::get<0>(packageWithArch(item, sysArch));
if (!p || !p->isInstalled())
continue;
if (p->recommendsList().contains(packageName))
continue;
ret << item;
// append new reqiure list
for (const auto &r : p->requiredByList())
{
if (ret.contains(r) || testQueue.contains(r))
continue;
testQueue.append(r);
}
}
// remove self
ret.remove(packageName);
return ret.values();
}
void PackagesManager::reset()
{
m_preparedPackages.clear();
m_packageInstallStatus.clear();
m_packageDependsStatus.clear();
m_appendedPackagesMd5.clear();
m_backendFuture.result()->reloadCache();
}
void PackagesManager::resetPackageDependsStatus(const int index)
{
if (!m_packageDependsStatus.contains(index))
return;
// reload backend cache
m_backendFuture.result()->reloadCache();
m_packageDependsStatus.remove(index);
}
void PackagesManager::removePackage(const int index)
{
auto deb = m_preparedPackages[index];
const auto md5 = deb->md5Sum();
m_appendedPackagesMd5.remove(md5);
m_preparedPackages.removeAt(index);
m_preparedPackagesTurnStatus.removeAt(index);
m_packageInstallStatus.clear();
m_packageDependsStatus.clear();
}
void PackagesManager::appendPackage(std::shared_ptr<QApt::DebFile> debPackage,
TurnPackageArchitecture::TurnPackage turnPackage)
{
const auto md5 = debPackage->md5Sum();
if (m_appendedPackagesMd5.contains(md5))
return;
m_preparedPackages << debPackage;
m_appendedPackagesMd5 << md5;
m_preparedPackagesTurnStatus << turnPackage;
}
const PackageDependsStatus PackagesManager::checkDependsPackageStatus(QSet<QString> &choosed_set, const QString &architecture, const QList<DependencyItem> &depends)
{
qDebug() << "depends List size: " << depends.size();
auto index_ = new int(0);
// Print each DependencyItem with proper intendes, using cout
for (const auto &item : depends) {
qDebug() << "DependencyItem: " << *index_;
for (const auto &info : item) {
// If more than 1 items are in the DependencyItem,
// this means that this is a alternative dependency list.
qDebug() << " DependencyInfo: " << DependencyInfo::typeName(info.dependencyType()) << info.packageName() << relationName(info.relationType()) << info.packageVersion();
}
++(*index_);
}
delete index_; index_ = nullptr;
PackageDependsStatus ret = PackageDependsStatus::ok();
for (const auto &candicate_list : depends)
{
const auto r = checkDependsPackageStatus(choosed_set, architecture, candicate_list);
ret.maxEq(r);
if (ret.isBreak())
break;
}
return ret;
}
const PackageDependsStatus PackagesManager::checkDependsPackageStatus(QSet<QString> &choosed_set, const QString &architecture, const DependencyItem &candicate)
{
PackageDependsStatus ret = PackageDependsStatus::_break(QString());
for (const auto &info : candicate)
{
const auto r = checkDependsPackageStatus(choosed_set, architecture, info);
ret.minEq(r);
if (!ret.isBreak())
break;
}
return ret;
}
const PackageDependsStatus PackagesManager::checkDependsPackageStatus(QSet<QString> &choosed_set, const QString &architecture, const DependencyInfo &dependencyInfo)
{
const QString package_name = dependencyInfo.packageName();
auto res_ = packageWithArch(package_name, architecture, dependencyInfo.multiArchAnnotation());
Package *p = std::get<0>(res_);
bool omitVersionRequire = std::get<1>(res_);
if (!p)
{
qDebug() << "depends break because package" << package_name << "not available";
return PackageDependsStatus::_break(package_name);
}
qDebug() << DependencyInfo::typeName(dependencyInfo.dependencyType())
<< package_name
<< p->architecture()
<< relationName(dependencyInfo.relationType())
<< dependencyInfo.packageVersion();
// if (dependencyInfo.packageVersion().isEmpty())
// return PackageDependsStatus::ok();
const RelationType relation = dependencyInfo.relationType();
const QString &installedVersion = p->installedVersion();
if (!installedVersion.isEmpty())
{
if (omitVersionRequire) {
// Here, if the intended deprecated package is required to be (<=) than the installed version, we omit the version check.
if (dependencyInfo.relationType() == QApt::RelationType::GreaterOrEqual ||
dependencyInfo.relationType() == QApt::RelationType::GreaterThan ||
dependencyInfo.relationType() == QApt::RelationType::NoOperand)
qDebug() << "omit version as required";
return PackageDependsStatus::ok();
}
const int result = Package::compareVersion(installedVersion, dependencyInfo.packageVersion());
if (dependencyVersionMatch(result, relation))
return PackageDependsStatus::ok();
else
{
const QString &mirror_version = p->availableVersion();
if (mirror_version != installedVersion)
{
const auto mirror_result = Package::compareVersion(mirror_version, dependencyInfo.packageVersion());
if (dependencyVersionMatch(mirror_result, relation))
{
qDebug() << "availble by upgrade package" << p->name() << p->architecture() << "from" << installedVersion << "to" << mirror_version;
return PackageDependsStatus::available();
}
}
qDebug() << "depends break by" << p->name() << p->architecture() << dependencyInfo.packageVersion();
qDebug() << "installed version not match" << installedVersion;
return PackageDependsStatus::_break(p->name());
}
} else {
const int result = Package::compareVersion(p->version(), dependencyInfo.packageVersion());
if (!dependencyVersionMatch(result, relation))
{
qDebug() << "depends break by" << p->name() << p->architecture() << dependencyInfo.packageVersion();
qDebug() << "available version not match" << p->version();
return PackageDependsStatus::_break(p->name());
}
// is that already choosed?
if (choosed_set.contains(p->name()))
return PackageDependsStatus::ok();
// check arch conflicts
if (p->multiArchType() == MultiArchSame)
{
auto b = backend();
for (const auto &arch : b->architectures())
{
if (arch == p->architecture())
continue;
Package *tp = b->package(p->name() + ":" + arch);
if (tp && tp->isInstalled())
{
qDebug() << "multi arch installed: " << p->name() << p->version() << p->architecture() << "with" << tp->name() << tp->version() << tp->architecture();
return PackageDependsStatus::_break(p->name() + ":" + p->architecture());
}
}
}
// let's check conflicts
if (!isConflictSatisfy(architecture, p).is_ok())
{
qDebug() << "depends break because conflict, ready to find providers" << p->name();
auto b = m_backendFuture.result();
for (auto *ap : b->availablePackages())
{
if (!ap->providesList().contains(p->name()))
continue;
// is that already provide by another package?
if (ap->isInstalled())
{
qDebug() << "find a exist provider: " << ap->name();
return PackageDependsStatus::ok();
}
// provider is ok, switch to provider.
if (isConflictSatisfy(architecture, ap).is_ok())
{
qDebug() << "switch to depends a new provider: " << ap->name();
choosed_set << ap->name();
return PackageDependsStatus::ok();
}
}
qDebug() << "providers not found, still break: " << p->name();
return PackageDependsStatus::_break(p->name());
}
// now, package dependencies status is available or break,
// time to check depends' dependencies, but first, we need
// to add this package to choose list
choosed_set << p->name();
qDebug() << "Check indirect dependencies for package" << p->name();
const auto r = checkDependsPackageStatus(choosed_set, p->architecture(), p->depends());
if (r.isBreak())
{
choosed_set.remove(p->name());
qDebug() << "depends break by direct depends" << p->name() << p->architecture() << r.package;
return PackageDependsStatus::_break(p->name());
}
// const auto &depends = p->depends();
// for (auto const &item : depends)
// {
// PackageDependsStatus rs = PackageDependsStatus::_break(QString());
// for (auto const &info : item)
// {
// const QString arch = resolvMultiArchAnnotation(info.multiArchAnnotation(), p->architecture(), p->multiArchType());
// const auto r = checkDependsPackageStatus(choosed_set, arch, info);
// rs.minEq(r);
// }
// if (rs.isBreak())
// {
// // we are break, remove self
// choosed_set.remove(p->name());
// qDebug() << "depends break by direct depends" << p->name() << p->architecture() << dependencyInfo.packageVersion();
// return PackageDependsStatus::_break(p->name());
// }
// }
qDebug() << "Check finshed for package" << p->name();
return PackageDependsStatus::available();
}
}
std::pair<QApt::Package *, bool> PackagesManager::packageWithArch(const QString &packageName, const QString &sysArch, const QString &annotation)
{
qDebug() << "package with arch" << packageName << sysArch << annotation;
auto b = m_backendFuture.result();
Package *p = b->package(packageName + resolvMultiArchAnnotation(annotation, sysArch));
do {
if (!p)
p = b->package(packageName);
if (!p)
break;
const QString arch = resolvMultiArchAnnotation(annotation, sysArch, p->multiArchType());
// if (!arch.isEmpty())
// reset to check foreign arch
p = b->package(packageName + arch);
} while(false);
if (p)
return {p, false};
qDebug() << "check virtual package providers for package" << packageName << sysArch << annotation;
// check virtual package providers
// Now we can safely omit the version requirement.
// As this is a fine replacement for the deprecated package.
// Setting the second param to true to indicate this
for (auto *ap : b->availablePackages())
if (ap->name() != packageName && ap->providesList().contains(packageName))
return {std::get<0>(packageWithArch(ap->name(), sysArch, annotation)), true};
return {nullptr, false};
}
PackageDependsStatus PackageDependsStatus::ok()
{
return { DebListModel::DependsOk, QString() };
}
PackageDependsStatus PackageDependsStatus::available()
{
return { DebListModel::DependsAvailable, QString() };
}
PackageDependsStatus PackageDependsStatus::_break(const QString &package)
{
return { DebListModel::DependsBreak, package };
}
PackageDependsStatus::PackageDependsStatus() :
PackageDependsStatus(DebListModel::DependsOk, QString())
{
}
PackageDependsStatus::PackageDependsStatus(const int status, const QString &package) :
status(status),
package(package)
{
}
PackageDependsStatus PackageDependsStatus::operator =(const PackageDependsStatus &other)
{
status = other.status;
package = other.package;
return *this;
}
PackageDependsStatus PackageDependsStatus::max(const PackageDependsStatus &other)
{
if (other.status > status)
*this = other;
return *this;
}
PackageDependsStatus PackageDependsStatus::maxEq(const PackageDependsStatus &other)
{
if (other.status >= status)
*this = other;
return *this;
}
PackageDependsStatus PackageDependsStatus::min(const PackageDependsStatus &other)
{
if (other.status < status)
*this = other;
return *this;
}
PackageDependsStatus PackageDependsStatus::minEq(const PackageDependsStatus &other)
{
if (other.status <= status)
*this = other;
return *this;
}
bool PackageDependsStatus::isBreak() const
{
return status == DebListModel::DependsBreak;
}
bool PackageDependsStatus::isAvailable() const
{
return status == DebListModel::DependsAvailable;
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。