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..b9e7035a8 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,44 @@ 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 +451,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 {