From bdaa04db1699fdfca0a24bfbfa6516b2fe7f8e43 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 8 Nov 2024 12:31:30 +0100 Subject: [PATCH] Support repo's Appstream data download and install Repositories can provide Appstream data for the packages they contain. This Appstream data is consumed by applications like the GNOME Software or KDE Discover, thus the users can see the packages (apps) in them. This is to be in pair with PackageKit, which does download and install the repo's Appstream data. As this adds a dependency on the `appstream` library, there is also a CMake option WITH_APPSTREAM to be able to turn the support off. The support is enabled by default. Closes https://github.com/rpm-software-management/dnf5/issues/1564 --- CMakeLists.txt | 5 ++++ include/libdnf5/repo/repo.hpp | 4 +++ libdnf5/CMakeLists.txt | 7 +++++ libdnf5/repo/repo.cpp | 47 ++++++++++++++++++++++++++++++++ libdnf5/repo/repo_downloader.cpp | 7 +++++ libdnf5/repo/repo_downloader.hpp | 3 ++ libdnf5/repo/repo_sack.cpp | 1 + libdnf5/repo/solv_repo.cpp | 15 ++++++++++ libdnf5/repo/solv_repo.hpp | 2 +- 9 files changed, 90 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b031aea84..835a59c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ option(WITH_COMPS "Build with comps groups and environments support" ON) option(WITH_MODULEMD "Build with modulemd modules support" ON) option(WITH_ZCHUNK "Build with zchunk delta compression support" ON) option(WITH_SYSTEMD "Build with systemd and D-Bus features" ON) +option(WITH_APPSTREAM "Build with appstream support" ON) option(ENABLE_SOLV_URPMREORDER "Build with support for URPM-like solution reordering?" OFF) option(ENABLE_SOLV_FOCUSNEW "Build with SOLVER_FLAG_FOCUS_NEW libsolv flag enabled to ensure new dependencies are installed in latests versions?" ON) @@ -129,6 +130,10 @@ if (WITH_MODULEMD) add_definitions(-DWITH_MODULEMD) endif() +if (WITH_APPSTREAM) + add_definitions(-DWITH_APPSTREAM) +endif() + include_directories("${PROJECT_SOURCE_DIR}/include") include_directories("${PROJECT_SOURCE_DIR}/common") diff --git a/include/libdnf5/repo/repo.hpp b/include/libdnf5/repo/repo.hpp index 425982882..c7c4cc8fe 100644 --- a/include/libdnf5/repo/repo.hpp +++ b/include/libdnf5/repo/repo.hpp @@ -144,6 +144,10 @@ class LIBDNF_API Repo { // @replaces libdnf:repo/Repo.hpp:method:Repo.loadCache(bool throwExcept) void read_metadata_cache(); + /// install downloaded appstream data for the repo, if available and + /// if built with the appstream support + void install_appstream(); + /// Checks whether the locally downloaded metadata are in sync with the origin. /// @return `true` if metadata are in sync with the origin, `false` otherwise. bool is_in_sync(); diff --git a/libdnf5/CMakeLists.txt b/libdnf5/CMakeLists.txt index d360901ee..c5e82bc78 100644 --- a/libdnf5/CMakeLists.txt +++ b/libdnf5/CMakeLists.txt @@ -81,6 +81,13 @@ if (WITH_MODULEMD) target_link_libraries(libdnf5_static PRIVATE ${LIBMODULEMD_LIBRARIES}) endif() +if (WITH_APPSTREAM) + pkg_check_modules(APPSTREAM REQUIRED appstream>=1.0) + include_directories(${APPSTREAM_INCLUDE_DIRS}) + target_link_libraries(libdnf5 PRIVATE ${APPSTREAM_LIBRARIES}) + target_link_libraries(libdnf5_static PRIVATE ${APPSTREAM_LIBRARIES}) +endif() + if (ENABLE_SOLV_FOCUSNEW) pkg_check_modules(LIBSOLV REQUIRED libsolv>=0.7.30) else() diff --git a/libdnf5/repo/repo.cpp b/libdnf5/repo/repo.cpp index c621eab67..98e65f293 100644 --- a/libdnf5/repo/repo.cpp +++ b/libdnf5/repo/repo.cpp @@ -24,6 +24,7 @@ constexpr const char * REPOID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno #include "repo_downloader.hpp" #include "rpm/package_sack_impl.hpp" #include "solv_repo.hpp" +#include "utils/string.hpp" #include "libdnf5/common/exception.hpp" #include "libdnf5/conf/const.hpp" @@ -52,6 +53,9 @@ extern "C" { #include #include +#ifdef WITH_APPSTREAM +#include +#endif namespace libdnf5::repo { @@ -203,6 +207,45 @@ void Repo::read_metadata_cache() { p_impl->downloader->load_local(); } +void Repo::install_appstream() { +#ifdef WITH_APPSTREAM + std::string repo_id = p_impl->config.get_id(); + const std::map & metadata_paths = p_impl->downloader->get_metadata_paths(); + std::vector basenames; + /* Let every type starting with any of the basenames be considered as the Appstream + data and pass it to the "appstream" library for installation; example type names + are "appstream", "appstream-icons", "appstream-icons_64x64", "appdata", "appdata-icons". + The list of the variants can be long, thus do not hard code the it, but use + the "starts_with" comparison instead. */ + /* TODO: make the list configurable with this default */ + basenames.push_back("appstream"); + basenames.push_back("appdata"); + + for (size_t i = 0; i < basenames.size(); i++) { + std::string basename = basenames[i]; + for (std::map::const_iterator it = metadata_paths.begin(); it != metadata_paths.end(); + it++) { + const std::string type = it->first; + const std::string path = it->second; + GError * local_error = NULL; + + if (!utils::string::starts_with(type, basename)) + continue; + + if (!as_utils_install_metadata_file( + AS_METADATA_LOCATION_CACHE, path.c_str(), repo_id.c_str(), NULL, &local_error)) { + p_impl->base->get_logger()->debug( + "Failed to install Appstream metadata file '{}' for repo '{}': {}", + path, + repo_id, + local_error ? local_error->message : "Unknown error"); + } + + g_clear_error(&local_error); + } + } +#endif +} bool Repo::is_in_sync() { if (!p_impl->config.get_metalink_option().empty() && !p_impl->config.get_metalink_option().get_value().empty()) { @@ -409,6 +452,10 @@ void Repo::load_available_repo() { } p_impl->solv_repo->load_repo_main(p_impl->downloader->repomd_filename, primary_fn); +#ifdef WITH_APPSTREAM + p_impl->solv_repo->load_repo_ext(RepodataType::APPSTREAM, *p_impl->downloader.get()); + p_impl->solv_repo->load_repo_ext(RepodataType::APPSTREAM_ICONS, *p_impl->downloader.get()); +#endif auto optional_metadata = p_impl->config.get_main_config().get_optional_metadata_types_option().get_value(); diff --git a/libdnf5/repo/repo_downloader.cpp b/libdnf5/repo/repo_downloader.cpp index b142aabbe..c5c8194ab 100644 --- a/libdnf5/repo/repo_downloader.cpp +++ b/libdnf5/repo/repo_downloader.cpp @@ -389,6 +389,9 @@ const std::string & RepoDownloader::get_metadata_path(const std::string & metada return it != metadata_paths.end() ? it->second : empty; } +const std::map & RepoDownloader::get_metadata_paths() const { + return metadata_paths; +} LibrepoHandle RepoDownloader::init_local_handle() { LibrepoHandle h; @@ -477,6 +480,10 @@ void RepoDownloader::common_handle_setup(LibrepoHandle & h) { dlist.push_back(MD_FILENAME_PRIMARY); #ifdef WITH_MODULEMD dlist.push_back(MD_FILENAME_MODULES); +#endif +#ifdef WITH_APPSTREAM + dlist.push_back(MD_FILENAME_APPSTREAM); + dlist.push_back(MD_FILENAME_APPSTREAM_ICONS); #endif if (optional_metadata.extract(libdnf5::METADATA_TYPE_FILELISTS)) { dlist.push_back(MD_FILENAME_FILELISTS); diff --git a/libdnf5/repo/repo_downloader.hpp b/libdnf5/repo/repo_downloader.hpp index 1c14b272b..c98bff4f3 100644 --- a/libdnf5/repo/repo_downloader.hpp +++ b/libdnf5/repo/repo_downloader.hpp @@ -56,6 +56,8 @@ class RepoDownloader { static constexpr const char * MD_FILENAME_GROUP_GZ = "group_gz"; static constexpr const char * MD_FILENAME_GROUP = "group"; static constexpr const char * MD_FILENAME_MODULES = "modules"; + static constexpr const char * MD_FILENAME_APPSTREAM = "appstream"; + static constexpr const char * MD_FILENAME_APPSTREAM_ICONS = "appstream-icons"; RepoDownloader(const libdnf5::BaseWeakPtr & base, const ConfigRepo & config, Repo::Type repo_type); @@ -74,6 +76,7 @@ class RepoDownloader { void * get_user_data() const noexcept; const std::string & get_metadata_path(const std::string & metadata_type) const; + const std::map & get_metadata_paths() const; private: diff --git a/libdnf5/repo/repo_sack.cpp b/libdnf5/repo/repo_sack.cpp index 62e6e92ad..fadaed389 100644 --- a/libdnf5/repo/repo_sack.cpp +++ b/libdnf5/repo/repo_sack.cpp @@ -574,6 +574,7 @@ void RepoSack::Impl::update_and_load_repos(libdnf5::repo::RepoQuery & repos, boo RepoCache(base, cache_dir).remove_attribute(RepoCache::ATTRIBUTE_EXPIRED); repo->mark_fresh(); repo->read_metadata_cache(); + repo->install_appstream(); repos_for_processing.erase(repos_for_processing.begin() + static_cast(idx)); send_to_sack_loader(repo); diff --git a/libdnf5/repo/solv_repo.cpp b/libdnf5/repo/solv_repo.cpp index 67025e452..efb5c04e3 100644 --- a/libdnf5/repo/solv_repo.cpp +++ b/libdnf5/repo/solv_repo.cpp @@ -179,6 +179,10 @@ static const char * repodata_type_to_name(RepodataType type) { return RepoDownloader::MD_FILENAME_GROUP; case RepodataType::OTHER: return RepoDownloader::MD_FILENAME_OTHER; + case RepodataType::APPSTREAM: + return RepoDownloader::MD_FILENAME_APPSTREAM; + case RepodataType::APPSTREAM_ICONS: + return RepoDownloader::MD_FILENAME_APPSTREAM_ICONS; } libdnf_throw_assertion("Unknown RepodataType: {}", utils::to_underlying(type)); @@ -197,6 +201,9 @@ static int repodata_type_to_flags(RepodataType type) { return 0; case RepodataType::OTHER: return REPO_EXTEND_SOLVABLES | REPO_LOCALPOOL; + case RepodataType::APPSTREAM: + case RepodataType::APPSTREAM_ICONS: + return 0; } libdnf_throw_assertion("Unknown RepodataType: {}", utils::to_underlying(type)); @@ -313,6 +320,8 @@ void SolvRepo::load_system_repo_ext(RepodataType type) { case RepodataType::OTHER: case RepodataType::PRESTO: case RepodataType::UPDATEINFO: + case RepodataType::APPSTREAM: + case RepodataType::APPSTREAM_ICONS: throw SolvError(M_("Unsupported extended repodata type for the system repo: \"{}\"."), type_name); } } @@ -375,6 +384,12 @@ void SolvRepo::load_repo_ext(RepodataType type, const RepoDownloader & downloade case RepodataType::OTHER: res = repo_add_rpmmd(repo, ext_file.get(), 0, REPO_EXTEND_SOLVABLES); break; + case RepodataType::APPSTREAM: + res = repo_add_rpmmd(repo, ext_file.get(), 0, REPO_EXTEND_SOLVABLES); + break; + case RepodataType::APPSTREAM_ICONS: + res = repo_add_rpmmd(repo, ext_file.get(), 0, REPO_EXTEND_SOLVABLES); + break; } if (res != 0) { diff --git a/libdnf5/repo/solv_repo.hpp b/libdnf5/repo/solv_repo.hpp index ae6e62e47..2fdcb27b0 100644 --- a/libdnf5/repo/solv_repo.hpp +++ b/libdnf5/repo/solv_repo.hpp @@ -52,7 +52,7 @@ struct SolvUserdata { namespace libdnf5::repo { using LibsolvRepo = ::Repo; -enum class RepodataType { FILELISTS, PRESTO, UPDATEINFO, COMPS, OTHER }; +enum class RepodataType { FILELISTS, PRESTO, UPDATEINFO, COMPS, OTHER, APPSTREAM, APPSTREAM_ICONS }; class SolvError : public Error {