diff --git a/dnf5.spec b/dnf5.spec index d7dda0cfc..3b66ab1bd 100644 --- a/dnf5.spec +++ b/dnf5.spec @@ -609,6 +609,23 @@ Libdnf5 plugin that allows to run actions (external executables) on hooks. %{_mandir}/man8/libdnf5-actions.8.* %endif +# ========== libdnf5-plugin-appstream ========== + +%if %{with appstream} + +%package -n libdnf5-plugin-appstream +Summary: Libdnf5 plugin to install repo Appstream data +License: LGPL-2.1-or-later +Requires: libdnf5%{?_isa} = %{version}-%{release} + +%description -n libdnf5-plugin-appstream +Libdnf5 plugin that installs repository's Appstream data, for repositories which provide them. + +%files -n libdnf5-plugin-appstream +%{_libdir}/libdnf5/plugins/appstream.so +%config %{_sysconfdir}/dnf/libdnf5-plugins/appstream.conf + +%endif # ========== libdnf5-plugin-plugin_rhsm ========== diff --git a/include/libdnf5/repo/repo.hpp b/include/libdnf5/repo/repo.hpp index 292bd2a88..291359840 100644 --- a/include/libdnf5/repo/repo.hpp +++ b/include/libdnf5/repo/repo.hpp @@ -144,10 +144,6 @@ class LIBDNF_API Repo { // @replaces libdnf:repo/Repo.hpp:method:Repo.loadCache(bool throwExcept) void read_metadata_cache(); - /// Installs 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(); @@ -318,6 +314,9 @@ class LIBDNF_API Repo { // @replaces libdnf:repo/Repo.hpp:method:Repo.downloadMetadata(const std::string & destdir) void download_metadata(const std::string & destdir); + /// Returns a list of pairs of the rpmmd type and filename of the Appstream data of the repo + std::vector> get_appstream_metadata() const; + private: class LIBDNF_LOCAL Impl; friend class RepoSack; diff --git a/libdnf5-plugins/CMakeLists.txt b/libdnf5-plugins/CMakeLists.txt index f97124f09..005c6a8c3 100644 --- a/libdnf5-plugins/CMakeLists.txt +++ b/libdnf5-plugins/CMakeLists.txt @@ -4,3 +4,7 @@ set(CMAKE_C_VISIBILITY_PRESET hidden) add_subdirectory("actions") add_subdirectory("python_plugins_loader") add_subdirectory("rhsm") + +if(WITH_APPSTREAM) + add_subdirectory("appstream") +endif() diff --git a/libdnf5-plugins/appstream/CMakeLists.txt b/libdnf5-plugins/appstream/CMakeLists.txt new file mode 100644 index 000000000..5d204bb46 --- /dev/null +++ b/libdnf5-plugins/appstream/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(appstream_plugin MODULE appstream.cpp) + +# disable the 'lib' prefix in order to create appstream.so +set_target_properties(appstream_plugin PROPERTIES PREFIX "") +set_target_properties(appstream_plugin PROPERTIES OUTPUT_NAME "appstream") + +pkg_check_modules(APPSTREAM REQUIRED appstream>=0.16) +include_directories(${APPSTREAM_INCLUDE_DIRS}) +target_link_libraries(appstream_plugin PRIVATE ${APPSTREAM_LIBRARIES}) +target_link_libraries(appstream_plugin PRIVATE libdnf5) + +install(TARGETS appstream_plugin LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/libdnf5/plugins/") +install(FILES "appstream.conf" DESTINATION "${CMAKE_INSTALL_FULL_SYSCONFDIR}/dnf/libdnf5-plugins") diff --git a/libdnf5-plugins/appstream/appstream.conf b/libdnf5-plugins/appstream/appstream.conf new file mode 100644 index 000000000..a32ac3a78 --- /dev/null +++ b/libdnf5-plugins/appstream/appstream.conf @@ -0,0 +1,3 @@ +[main] +name = appstream +enabled = 1 diff --git a/libdnf5-plugins/appstream/appstream.cpp b/libdnf5-plugins/appstream/appstream.cpp new file mode 100644 index 000000000..69ef119d9 --- /dev/null +++ b/libdnf5-plugins/appstream/appstream.cpp @@ -0,0 +1,131 @@ +/* +Copyright Contributors to the libdnf project. + +This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ + +Libdnf 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 2 of the License, or +(at your option) any later version. + +Libdnf 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 libdnf. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include + +using namespace libdnf5; + +namespace { + +constexpr const char * PLUGIN_NAME{"appstream"}; +constexpr libdnf5::plugin::Version PLUGIN_VERSION{.major = 1, .minor = 0, .micro = 0}; + +constexpr const char * attrs[]{"author.name", "author.email", "description", nullptr}; +constexpr const char * attrs_value[]{"Milan Crha", "mcrha@redhat.com", "install repo Appstream data."}; + +class AppstreamPlugin : public plugin::IPlugin { +public: + AppstreamPlugin(libdnf5::plugin::IPluginData & data, libdnf5::ConfigParser &) : IPlugin(data) {} + virtual ~AppstreamPlugin() = default; + + PluginAPIVersion get_api_version() const noexcept override { return PLUGIN_API_VERSION; } + + const char * get_name() const noexcept override { return PLUGIN_NAME; } + + plugin::Version get_version() const noexcept override { return PLUGIN_VERSION; } + + const char * const * get_attributes() const noexcept override { return attrs; } + + const char * get_attribute(const char * attribute) const noexcept override { + for (size_t i = 0; attrs[i]; ++i) { + if (std::strcmp(attribute, attrs[i]) == 0) { + return attrs_value[i]; + } + } + return nullptr; + } + + void repos_loaded() override { + Base & base = get_base(); + repo::RepoQuery repos(base); + repos.filter_enabled(true); + for (const auto & repo : repos) { + switch (repo->get_type()) { + case repo::Repo::Type::AVAILABLE: + case repo::Repo::Type::SYSTEM: + install_appstream(repo.get()); + break; + case repo::Repo::Type::COMMANDLINE:; + } + } + } + +private: + void install_appstream(libdnf5::repo::Repo * repo); +}; + +void AppstreamPlugin::install_appstream(libdnf5::repo::Repo * repo) { + libdnf5::Base & base = get_base(); + if (!repo->get_config().get_main_config().get_optional_metadata_types_option().get_value().contains( + libdnf5::METADATA_TYPE_APPSTREAM)) + return; + + std::string repo_id = repo->get_config().get_id(); + auto appstream_metadata = repo->get_appstream_metadata(); + for (auto & item : appstream_metadata) { + const std::string path = item.second; + GError * local_error = NULL; + + if (!as_utils_install_metadata_file( + AS_METADATA_LOCATION_CACHE, path.c_str(), repo_id.c_str(), NULL, &local_error)) { + 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); + } +} + +} // namespace + + +PluginAPIVersion libdnf_plugin_get_api_version(void) { + return PLUGIN_API_VERSION; +} + +const char * libdnf_plugin_get_name(void) { + return PLUGIN_NAME; +} + +plugin::Version libdnf_plugin_get_version(void) { + return PLUGIN_VERSION; +} + +plugin::IPlugin * libdnf_plugin_new_instance( + [[maybe_unused]] LibraryVersion library_version, + libdnf5::plugin::IPluginData & data, + libdnf5::ConfigParser & parser) try { + return new AppstreamPlugin(data, parser); +} catch (...) { + return nullptr; +} + +void libdnf_plugin_delete_instance(plugin::IPlugin * plugin_object) { + delete plugin_object; +} diff --git a/libdnf5/CMakeLists.txt b/libdnf5/CMakeLists.txt index b2a0798ce..d360901ee 100644 --- a/libdnf5/CMakeLists.txt +++ b/libdnf5/CMakeLists.txt @@ -81,13 +81,6 @@ if (WITH_MODULEMD) target_link_libraries(libdnf5_static PRIVATE ${LIBMODULEMD_LIBRARIES}) endif() -if (WITH_APPSTREAM) - pkg_check_modules(APPSTREAM REQUIRED appstream>=0.16) - 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 c031438f2..3b9e8dcc7 100644 --- a/libdnf5/repo/repo.cpp +++ b/libdnf5/repo/repo.cpp @@ -52,10 +52,6 @@ extern "C" { #include #include -#ifdef WITH_APPSTREAM -#include -#endif - namespace libdnf5::repo { static void is_readable_rpm(const std::string & fn) { @@ -206,30 +202,8 @@ void Repo::read_metadata_cache() { p_impl->downloader->load_local(); } -void Repo::install_appstream() { -#ifdef WITH_APPSTREAM - if (!p_impl->config.get_main_config().get_optional_metadata_types_option().get_value().contains( - libdnf5::METADATA_TYPE_APPSTREAM)) - return; - - std::string repo_id = p_impl->config.get_id(); - auto appstream_metadata = p_impl->downloader->get_appstream_metadata(); - for (auto & item : appstream_metadata) { - const std::string path = item.second; - GError * local_error = NULL; - - 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 +std::vector> Repo::get_appstream_metadata() const { + return get_downloader().get_appstream_metadata(); } bool Repo::is_in_sync() { diff --git a/libdnf5/repo/repo_sack.cpp b/libdnf5/repo/repo_sack.cpp index 295c09b87..8e7e9366c 100644 --- a/libdnf5/repo/repo_sack.cpp +++ b/libdnf5/repo/repo_sack.cpp @@ -575,7 +575,6 @@ 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);