Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add module list command #918

Merged
merged 9 commits into from
Oct 9, 2023
57 changes: 57 additions & 0 deletions dnf5/commands/module/arguments.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

/*
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 Lesser General Public License as published by
the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with libdnf. If not, see <https://www.gnu.org/licenses/>.
*/


#ifndef DNF5_COMMANDS_MODULE_ARGUMENTS_HPP
#define DNF5_COMMANDS_MODULE_ARGUMENTS_HPP


#include <libdnf5-cli/session.hpp>
#include <libdnf5/utils/bgettext/bgettext-lib.h>


namespace dnf5 {


class ModuleEnabledOption : public libdnf5::cli::session::BoolOption {
public:
explicit ModuleEnabledOption(libdnf5::cli::session::Command & command)
: BoolOption(command, "enabled", '\0', _("Show enabled modules."), false) {}
};


class ModuleDisabledOption : public libdnf5::cli::session::BoolOption {
public:
explicit ModuleDisabledOption(libdnf5::cli::session::Command & command)
: BoolOption(command, "disabled", '\0', _("Show disabled modules."), false) {}
};


class ModuleSpecArguments : public libdnf5::cli::session::StringArgumentList {
public:
explicit ModuleSpecArguments(libdnf5::cli::session::Command & command)
: StringArgumentList(command, "module-spec", _("Pattern matching module NSVCAs.")) {}
};


} // namespace dnf5


#endif // DNF5_COMMANDS_MODULE_ARGUMENTS_HPP
8 changes: 4 additions & 4 deletions dnf5/commands/module/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ void ModuleCommand::register_subcommands() {
auto & cmd = *get_argument_parser_command();

// query commands
// auto * query_commands_group = parser.add_new_group("module_query_commands");
// query_commands_group->set_header("Query Commands:");
// cmd.register_group(query_commands_group);
// register_subcommand(std::make_unique<ModuleListCommand>(get_context()), query_commands_group);
auto * query_commands_group = parser.add_new_group("module_query_commands");
query_commands_group->set_header("Query Commands:");
cmd.register_group(query_commands_group);
register_subcommand(std::make_unique<ModuleListCommand>(get_context()), query_commands_group);
// register_subcommand(std::make_unique<ModuleInfoCommand>(get_context()), query_commands_group);
// register_subcommand(std::make_unique<ModuleProvidesCommand>(get_context()), query_commands_group);

Expand Down
42 changes: 41 additions & 1 deletion dnf5/commands/module/module_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,53 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.

#include "module_list.hpp"

#include <libdnf5-cli/output/modulelist.hpp>
#include <libdnf5/module/module_query.hpp>

namespace dnf5 {

using namespace libdnf5::cli;

void ModuleListCommand::set_argument_parser() {
auto & cmd = *get_argument_parser_command();
cmd.set_description("List module streams");

enabled = std::make_unique<ModuleEnabledOption>(*this);
disabled = std::make_unique<ModuleDisabledOption>(*this);
module_specs = std::make_unique<ModuleSpecArguments>(*this);
}

void ModuleListCommand::configure() {
auto & context = get_context();
context.set_load_system_repo(true);
context.set_load_available_repos(Context::LoadAvailableRepos::ENABLED);
}

void ModuleListCommand::run() {}
void ModuleListCommand::run() {
libdnf5::module::ModuleQuery query(get_context().base, true);
auto module_specs_str = module_specs->get_value();
if (module_specs_str.size() > 0) {
for (const auto & module_spec : module_specs_str) {
for (const auto & nsvcap : libdnf5::module::Nsvcap::parse(module_spec)) {
libdnf5::module::ModuleQuery nsvcap_query(get_context().base, false);
nsvcap_query.filter_nsvca(nsvcap, libdnf5::sack::QueryCmp::GLOB);
if (!nsvcap_query.empty()) {
query |= nsvcap_query;
}
}
}
} else {
query = libdnf5::module::ModuleQuery(get_context().base, false);
}

if (enabled->get_value()) {
query.filter_enabled();
} else if (disabled->get_value()) {
query.filter_disabled();
}

output::print_modulelist_table(query.list());
output::print_modulelist_table_hint();
}

} // namespace dnf5
8 changes: 8 additions & 0 deletions dnf5/commands/module/module_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#ifndef DNF5_COMMANDS_MODULE_MODULE_LIST_HPP
#define DNF5_COMMANDS_MODULE_MODULE_LIST_HPP

#include "arguments.hpp"

#include <dnf5/context.hpp>

namespace dnf5 {
Expand All @@ -28,7 +30,13 @@ class ModuleListCommand : public Command {
public:
explicit ModuleListCommand(Context & context) : Command(context, "list") {}
void set_argument_parser() override;
void configure() override;
void run() override;

private:
std::unique_ptr<ModuleEnabledOption> enabled{nullptr};
std::unique_ptr<ModuleDisabledOption> disabled{nullptr};
std::unique_ptr<ModuleSpecArguments> module_specs{nullptr};
};

} // namespace dnf5
Expand Down
4 changes: 4 additions & 0 deletions doc/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ List command
packages already listed in the `Installed Packages` section to reduce duplicities. But if the `--available` modifier
is used, dnf5 considers all versions available in the enabled repositories, regardless of which version is installed.

Module command
--------------
* Dropped `--all` option since this behavior is the default one.

Repoclosure command
-------------------
* Dropped `--pkg`` option. Positional arguments can be used to specify packages to check closure for.
Expand Down
4 changes: 3 additions & 1 deletion doc/commands/module.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ Synopsis
Description
===========

Optional ``module-spec`` arguments can be passed to select only specific modules.


Subcommands
===========

``list``
| List module streams.
| List module streams. ``--enabled`` and ``--disabled`` options narrow down the requested list.

``info``
| Print details about module streams.
Expand Down
124 changes: 124 additions & 0 deletions include/libdnf5-cli/output/modulelist.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
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 Lesser General Public License as published by
the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with libdnf. If not, see <https://www.gnu.org/licenses/>.
*/


#ifndef LIBDNF_CLI_OUTPUT_MODULELIST_HPP
#define LIBDNF_CLI_OUTPUT_MODULELIST_HPP

#include "utils/string.hpp"

#include "libdnf5-cli/tty.hpp"

#include <libdnf5/module/module_item.hpp>
#include <libdnf5/module/module_sack.hpp>
#include <libsmartcols/libsmartcols.h>

#include <set>
#include <string>

namespace libdnf5::cli::output {


// module list table columns
enum { COL_MODULE_NAME, COL_MODULE_STREAM, COL_MODULE_PROFILES, COL_MODULE_SUMMARY };


const std::string MODULELIST_TABLE_HINT = _("\nHint: [d]efault, [e]nabled, [x]disabled, [i]nstalled");


static struct libscols_table * create_modulelist_table() {
struct libscols_table * table = scols_new_table();
if (libdnf5::cli::tty::is_interactive()) {
scols_table_enable_colors(table, 1);
}
struct libscols_column * cl = scols_table_new_column(table, "Name", 0.15, 0);
scols_column_set_cmpfunc(cl, scols_cmpstr_cells, NULL);
scols_table_new_column(table, "Stream", 0.15, SCOLS_FL_TRUNC);
scols_table_new_column(table, "Profiles", 0.3, SCOLS_FL_WRAP);
scols_table_new_column(table, "Summary", 0.4, SCOLS_FL_WRAP);
scols_table_enable_maxout(table, 1);
return table;
}


static void add_line_into_modulelist_table(
struct libscols_table * table,
const char * name,
const char * stream,
const char * profiles,
const char * summary) {
struct libscols_line * ln = scols_table_new_line(table, NULL);
scols_line_set_data(ln, COL_MODULE_NAME, name);
scols_line_set_data(ln, COL_MODULE_STREAM, stream);
scols_line_set_data(ln, COL_MODULE_PROFILES, profiles);
scols_line_set_data(ln, COL_MODULE_SUMMARY, summary);
}


template <class Query>
void print_modulelist_table(Query & module_list) {
// TODO(pkratoch): Sort the table
struct libscols_table * table = create_modulelist_table();
std::set<std::pair<std::string, std::string>> name_stream_pairs;
for (auto & module_item : module_list) {
const std::string & name = module_item.get_name();
const std::string & stream = module_item.get_stream();
if (!name_stream_pairs.contains(make_pair(name, stream))) {
// Get stream string (append [e] or [x] if needed)
std::string stream_string = stream;
const module::ModuleStatus & status = module_item.get_status();
if (status == module::ModuleStatus::ENABLED) {
stream_string.append(" [e]");
} else if (status == module::ModuleStatus::DISABLED) {
stream_string.append(" [x]");
} else if (module_item.is_default()) {
stream_string.append(" [d]");
}

// Get profile strings (append [d] or [i] if needed)
std::vector<std::string> profile_strings;
for (auto profile : module_item.get_profiles()) {
// TODO(pkratoch): Also show "[i]" for installed profiles
// TODO(pkratoch): Make sure defaults are loaded
profile_strings.push_back(profile.is_default() ? profile.get_name() + " [d]" : profile.get_name());
jan-kolarik marked this conversation as resolved.
Show resolved Hide resolved
}

add_line_into_modulelist_table(
table,
name.c_str(),
stream_string.c_str(),
utils::string::join(profile_strings, ", ").c_str(),
module_item.get_summary().c_str());
name_stream_pairs.emplace(make_pair(name, stream));
}
}
scols_sort_table(table, scols_table_get_column(table, COL_MODULE_NAME));
scols_print_table(table);
scols_unref_table(table);
}


void print_modulelist_table_hint() {
std::cout << MODULELIST_TABLE_HINT << std::endl;
}


} // namespace libdnf5::cli::output

#endif // LIBDNF_CLI_OUTPUT_MODULELIST_HPP
11 changes: 11 additions & 0 deletions include/libdnf5/module/module_item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class ModuleTest;
namespace libdnf5::module {


enum class ModuleStatus;


struct ModuleItemId {
public:
ModuleItemId() = default;
Expand Down Expand Up @@ -167,6 +170,14 @@ class ModuleItem {
// TODO(jmracek) Read a real decision from the modular solver
bool is_active() const;

/// @return The status of this ModuleItem.
/// @since 5.1.5
ModuleStatus get_status() const;

/// @return Whether this ModuleItem is the default stream of the module.
/// @since 5.1.5
bool is_default() const;

ModuleItem(const ModuleItem & mpkg);
ModuleItem & operator=(const ModuleItem & mpkg);
ModuleItem(ModuleItem && mpkg);
Expand Down
2 changes: 0 additions & 2 deletions include/libdnf5/module/module_profile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ class ModuleProfile {
// @replaces libdnf:module:modulemd/ModuleProfile.hpp:ctor:ModuleProfile.ModuleProfile()
ModuleProfile(const ModuleProfile & src);
ModuleProfile & operator=(const ModuleProfile & src);
ModuleProfile(ModuleProfile && src) = default;
ModuleProfile & operator=(ModuleProfile && src) = default;
~ModuleProfile();

private:
Expand Down
12 changes: 12 additions & 0 deletions include/libdnf5/module/module_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ class ModuleQuery : public libdnf5::sack::Query<ModuleItem> {
/// @since 5.0.6
void filter_nsvca(const Nsvcap & nsvcap, libdnf5::sack::QueryCmp cmp = libdnf5::sack::QueryCmp::EQ);

/// Filter ModuleItems with ModuleStatus::ENABLED.
///
/// @since 5.1.5
void filter_enabled();

/// Filter ModuleItems with ModuleStatus::DISABLED.
///
/// @since 5.1.5
void filter_disabled();

/// Filter ModuleItems by module_spec.
///
/// @param module_spec A module_spec the filter is matched against.
Expand All @@ -168,6 +178,8 @@ class ModuleQuery : public libdnf5::sack::Query<ModuleItem> {
static std::string version(const ModuleItem & obj) { return obj.get_version_str(); }
static std::string context(const ModuleItem & obj) { return obj.get_context(); }
static std::string arch(const ModuleItem & obj) { return obj.get_arch(); }
static bool is_enabled(const ModuleItem & obj);
static bool is_disabled(const ModuleItem & obj);
};

friend ModuleItem;
Expand Down
15 changes: 15 additions & 0 deletions libdnf5/module/module_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ std::vector<ModuleProfile> ModuleItem::get_profiles_internal(const char * name)
}


bool ModuleItem::is_default() const {
return module_sack->get_default_stream(get_name()) == get_stream();
}


bool ModuleItem::get_static_context() const {
return modulemd_module_stream_v2_is_static_context((ModulemdModuleStreamV2 *)md_stream);
}
Expand Down Expand Up @@ -444,4 +449,14 @@ bool ModuleItem::is_active() const {
}


ModuleStatus ModuleItem::get_status() const {
const ModuleStatus & module_status = module_sack->p_impl->module_db->get_status(get_name());
if (module_status == ModuleStatus::ENABLED &&
get_stream() != module_sack->p_impl->module_db->get_enabled_stream(get_name())) {
return ModuleStatus::AVAILABLE;
}
return module_status;
}


} // namespace libdnf5::module
Loading
Loading