diff --git a/libdiscover/backends/PackageKitBackend/PackageKitBackend.h b/libdiscover/backends/PackageKitBackend/PackageKitBackend.h --- a/libdiscover/backends/PackageKitBackend/PackageKitBackend.h +++ b/libdiscover/backends/PackageKitBackend/PackageKitBackend.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,9 @@ void addPackageToUpdate(PackageKit::Transaction::Info, const QString& pkgid, const QString& summary); void getUpdatesFinished(PackageKit::Transaction::Exit,uint); + Q_SIGNALS: + void loadedAppStream(); + private: friend class PackageKitResource; template @@ -109,6 +113,7 @@ bool m_hasSecurityUpdates = false; QSet m_packagesToAdd; QSet m_packagesToDelete; + bool m_appstreamInitialized = false; struct Packages { QHash packages; @@ -122,6 +127,7 @@ Packages m_packages; QSharedPointer m_reviews; QPointer m_getUpdatesTransaction; + QThreadPool m_threadPool; }; #endif // PACKAGEKITBACKEND_H diff --git a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp --- a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -120,7 +122,11 @@ }, this); } -PackageKitBackend::~PackageKitBackend() = default; +PackageKitBackend::~PackageKitBackend() +{ + m_threadPool.waitForDone(200); + m_threadPool.clear(); +} void PackageKitBackend::updateProxy() { @@ -158,75 +164,107 @@ Q_ASSERT(m_isFetching>=0); } -void PackageKitBackend::reloadPackageList() +struct DelayedAppStreamLoad { + QVector components; + QHash missingComponents; + bool correct = true; +}; + +static DelayedAppStreamLoad loadAppStream(AppStream::Pool* appdata) { - acquireFetching(true); - if (m_refresher) { - disconnect(m_refresher.data(), &PackageKit::Transaction::finished, this, &PackageKitBackend::reloadPackageList); - } + DelayedAppStreamLoad ret; QString error; - m_appdata.reset(new AppStream::Pool); - const bool b = m_appdata->load(&error); - if (!b && m_packages.packages.isEmpty()) { + ret.correct = appdata->load(&error); + if (!ret.correct) { qWarning() << "Could not open the AppStream metadata pool" << error; - - QTimer::singleShot(0, this, [this]() { - Q_EMIT passiveMessage(i18n("Please make sure that Appstream is properly set up on your system")); - }); } - const auto components = m_appdata->components(); - QStringList neededPackages; - neededPackages.reserve(components.size()); + const auto components = appdata->components(); + ret.components.reserve(components.size()); foreach(const AppStream::Component& component, components) { if (component.kind() == AppStream::Component::KindFirmware) continue; const auto pkgNames = component.packageNames(); if (pkgNames.isEmpty()) { const auto entries = component.launchable(AppStream::Launchable::KindDesktopId).entries(); if (component.kind() == AppStream::Component::KindDesktopApp && !entries.isEmpty()) { - const QString file = locateService(entries.first()); + const QString file = PackageKitBackend::locateService(entries.first()); if (!file.isEmpty()) { - acquireFetching(true); - auto trans = PackageKit::Daemon::searchFiles(file); - connect(trans, &PackageKit::Transaction::package, this, [trans](PackageKit::Transaction::Info info, const QString &packageID){ - if (info == PackageKit::Transaction::InfoInstalled) - trans->setProperty("installedPackage", packageID); - }); - connect(trans, &PackageKit::Transaction::finished, this, [this, trans, component](PackageKit::Transaction::Exit status) { - const auto pkgidVal = trans->property("installedPackage"); - if (status == PackageKit::Transaction::ExitSuccess && !pkgidVal.isNull()) { - const auto pkgid = pkgidVal.toString(); - auto res = addComponent(component, {PackageKit::Daemon::packageName(pkgid)}); - res->clearPackageIds(); - res->addPackageId(PackageKit::Transaction::InfoInstalled, pkgid, true); - } - acquireFetching(false); - }); - continue; + ret.missingComponents[file] = component; } } - - qCDebug(LIBDISCOVER_BACKEND_LOG) << "no packages for" << component.id(); - continue; + } else { + ret.components << component; } - neededPackages += pkgNames; + } + return ret; +} - addComponent(component, pkgNames); +void PackageKitBackend::reloadPackageList() +{ + acquireFetching(true); + if (m_refresher) { + disconnect(m_refresher.data(), &PackageKit::Transaction::finished, this, &PackageKitBackend::reloadPackageList); } - acquireFetching(false); - if (!neededPackages.isEmpty()) { - neededPackages.removeDuplicates(); - resolvePackages(neededPackages); - } else { - qCDebug(LIBDISCOVER_BACKEND_LOG) << "empty appstream db"; - if (PackageKit::Daemon::backendName() == QLatin1String("aptcc") || PackageKit::Daemon::backendName().isEmpty()) { - checkForUpdates(); + m_appdata.reset(new AppStream::Pool); + + auto fw = new QFutureWatcher(this); + connect(fw, &QFutureWatcher::finished, this, [this, fw]() { + const auto data = fw->result(); + fw->deleteLater(); + + if (!data.correct && m_packages.packages.isEmpty()) { + QTimer::singleShot(0, this, [this]() { + Q_EMIT passiveMessage(i18n("Please make sure that Appstream is properly set up on your system")); + }); } - } + QStringList neededPackages; + neededPackages.reserve(data.components.size()); + for (const auto &component: data.components) { + const auto pkgNames = component.packageNames(); + addComponent(component, pkgNames); + neededPackages << pkgNames; + } + for (auto it = data.missingComponents.constBegin(), itEnd = data.missingComponents.constEnd(); it != itEnd; ++it) { + acquireFetching(true); + const auto file = it.key(); + const auto component = it.value(); + auto trans = PackageKit::Daemon::searchFiles(file); + connect(trans, &PackageKit::Transaction::package, this, [trans](PackageKit::Transaction::Info info, const QString &packageID){ + if (info == PackageKit::Transaction::InfoInstalled) + trans->setProperty("installedPackage", packageID); + }); + connect(trans, &PackageKit::Transaction::finished, this, [this, trans, component](PackageKit::Transaction::Exit status) { + const auto pkgidVal = trans->property("installedPackage"); + if (status == PackageKit::Transaction::ExitSuccess && !pkgidVal.isNull()) { + const auto pkgid = pkgidVal.toString(); + auto res = addComponent(component, {PackageKit::Daemon::packageName(pkgid)}); + res->clearPackageIds(); + res->addPackageId(PackageKit::Transaction::InfoInstalled, pkgid, true); + } + acquireFetching(false); + }); + } + + if (!neededPackages.isEmpty()) { + neededPackages.removeDuplicates(); + resolvePackages(neededPackages); + } else { + qCDebug(LIBDISCOVER_BACKEND_LOG) << "empty appstream db"; + if (PackageKit::Daemon::backendName() == QLatin1String("aptcc") || PackageKit::Daemon::backendName().isEmpty()) { + checkForUpdates(); + } + } + acquireFetching(false); + if (!m_appstreamInitialized) { + m_appstreamInitialized = true; + Q_EMIT loadedAppStream(); + } + }); + fw->setFuture(QtConcurrent::run(&m_threadPool, &loadAppStream, m_appdata.get())); } AppPackageKitResource* PackageKitBackend::addComponent(const AppStream::Component& component, const QStringList& pkgNames) @@ -417,6 +455,7 @@ QList PackageKitBackend::componentsById(const QString& id) const { + Q_ASSERT(m_appstreamInitialized); return m_appdata->componentsById(id); } @@ -430,48 +469,52 @@ } else if (filter.search.isEmpty()) { return new ResultsStream(QStringLiteral("PackageKitStream-all"), kFilter>(m_packages.packages, [](AbstractResource* res) { return res->type() != AbstractResource::Technical && !qobject_cast(res)->extendsItself(); })); } else { - const QList components = m_appdata->search(filter.search); - const QStringList ids = kTransform(components, [](const AppStream::Component& comp) { return comp.id(); }); auto stream = new ResultsStream(QStringLiteral("PackageKitStream-search")); - if (!ids.isEmpty()) { - const auto resources = kFilter>(resourcesByPackageNames>(ids), [](AbstractResource* res){ return !qobject_cast(res)->extendsItself(); }); - QTimer::singleShot(0, this, [stream, resources] () { + const auto f = [this, stream, filter] () { + const QList components = m_appdata->search(filter.search); + const QStringList ids = kTransform(components, [](const AppStream::Component& comp) { return comp.id(); }); + if (!ids.isEmpty()) { + const auto resources = kFilter>(resourcesByPackageNames>(ids), [](AbstractResource* res){ return !qobject_cast(res)->extendsItself(); }); Q_EMIT stream->resourcesFound(resources); - }); - } + } - PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(filter.search, PackageKit::Transaction::FilterArch); - connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch); - connect(tArch, &PackageKit::Transaction::package, stream, [stream](PackageKit::Transaction::Info /*info*/, const QString &packageId){ - stream->setProperty("packageId", packageId); - }); - connect(tArch, &PackageKit::Transaction::finished, stream, [stream, ids, this](PackageKit::Transaction::Exit status) { - getPackagesFinished(); - if (status == PackageKit::Transaction::Exit::ExitSuccess) { - const auto packageId = stream->property("packageId"); - if (!packageId.isNull()) { - const auto res = resourcesByPackageNames>({PackageKit::Daemon::packageName(packageId.toString())}); - Q_EMIT stream->resourcesFound(kFilter>(res, [ids](AbstractResource* res){ return !ids.contains(res->appstreamId()); })); + PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(filter.search, PackageKit::Transaction::FilterArch); + connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch); + connect(tArch, &PackageKit::Transaction::package, stream, [stream](PackageKit::Transaction::Info /*info*/, const QString &packageId){ + stream->setProperty("packageId", packageId); + }); + connect(tArch, &PackageKit::Transaction::finished, stream, [stream, ids, this](PackageKit::Transaction::Exit status) { + getPackagesFinished(); + if (status == PackageKit::Transaction::Exit::ExitSuccess) { + const auto packageId = stream->property("packageId"); + if (!packageId.isNull()) { + const auto res = resourcesByPackageNames>({PackageKit::Daemon::packageName(packageId.toString())}); + Q_EMIT stream->resourcesFound(kFilter>(res, [ids](AbstractResource* res){ return !ids.contains(res->appstreamId()); })); + } } - } - stream->finish(); - }, Qt::QueuedConnection); + stream->finish(); + }, Qt::QueuedConnection); + }; + if (!m_appstreamInitialized) { + connect(this, &PackageKitBackend::loadedAppStream, stream, f); + } else { + QTimer::singleShot(0, this, f); + } return stream; } } ResultsStream * PackageKitBackend::findResourceByPackageName(const QUrl& url) { - AbstractResource* pkg = nullptr; if (url.isLocalFile()) { QMimeDatabase db; const auto mime = db.mimeTypeForUrl(url); if ( mime.inherits(QStringLiteral("application/vnd.debian.binary-package")) || mime.inherits(QStringLiteral("application/x-rpm")) || mime.inherits(QStringLiteral("application/x-tar")) || mime.inherits(QStringLiteral("application/x-xz-compressed-tar")) ) { - pkg = new LocalFilePKResource(url, this); + return new ResultsStream(QStringLiteral("PackageKitStream-localpkg"), QVector{ new LocalFilePKResource(url, this)}); } } else if (url.scheme() == QLatin1String("appstream")) { static const QMap deprecatedAppstreamIds = { @@ -488,20 +531,33 @@ if (appstreamId.isEmpty()) Q_EMIT passiveMessage(i18n("Malformed appstream url '%1'", url.toDisplayString())); else { - const auto deprecatedHost = deprecatedAppstreamIds.value(appstreamId); //try this as fallback - for (auto it = m_packages.packages.constBegin(), itEnd = m_packages.packages.constEnd(); it != itEnd; ++it) { - if (it.key().compare(appstreamId, Qt::CaseInsensitive) == 0 - || it.key().compare(deprecatedHost, Qt::CaseInsensitive) == 0 - || (appstreamId.endsWith(QLatin1String(".desktop")) && appstreamId.compare(it.key()+QLatin1String(".desktop"), Qt::CaseInsensitive) == 0)) { - pkg = it.value(); - break; + auto stream = new ResultsStream(QStringLiteral("PackageKitStream-appstream-url")); + const auto f = [this, appstreamId, stream] () { + AbstractResource* pkg = nullptr; + const auto deprecatedHost = deprecatedAppstreamIds.value(appstreamId); //try this as fallback + for (auto it = m_packages.packages.constBegin(), itEnd = m_packages.packages.constEnd(); it != itEnd; ++it) { + if (it.key().compare(appstreamId, Qt::CaseInsensitive) == 0 + || it.key().compare(deprecatedHost, Qt::CaseInsensitive) == 0 + || (appstreamId.endsWith(QLatin1String(".desktop")) && appstreamId.compare(it.key()+QLatin1String(".desktop"), Qt::CaseInsensitive) == 0)) { + pkg = it.value(); + break; + } } + if (pkg) + Q_EMIT stream->resourcesFound({pkg}); + stream->finish(); + // if (!pkg) + // qCDebug(LIBDISCOVER_BACKEND_LOG) << "could not find" << host << deprecatedHost; + }; + if (!m_appstreamInitialized) { + connect(this, &PackageKitBackend::loadedAppStream, stream, f); + } else { + QTimer::singleShot(0, this, f); } -// if (!pkg) -// qCDebug(LIBDISCOVER_BACKEND_LOG) << "could not find" << host << deprecatedHost; + return stream; } } - return new ResultsStream(QStringLiteral("PackageKitStream-url"), pkg ? QVector{pkg} : QVector{}); + return new ResultsStream(QStringLiteral("PackageKitStream-unknown-url"), {}); } bool PackageKitBackend::hasSecurityUpdates() const