Skip to content

Commit

Permalink
modules: Respect defaults when enabling multiple streams of a module
Browse files Browse the repository at this point in the history
  • Loading branch information
pkratoch committed Jan 9, 2024
1 parent 05aeae5 commit 3539458
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 3 deletions.
83 changes: 80 additions & 3 deletions libdnf5/module/module_sack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
#include "module/module_metadata.hpp"
#include "module/module_sack_impl.hpp"
#include "solv/solv_map.hpp"
#include "utils/string.hpp"

#include "libdnf5/base/base.hpp"
#include "libdnf5/base/base_weak.hpp"
Expand Down Expand Up @@ -777,14 +778,90 @@ bool ModuleSack::Impl::enable(const std::string & name, const std::string & stre
}


// module dict { name : {stream : [solvable id] }
static std::map<std::string, std::map<std::string, std::vector<int>>> create_module_dict(
const ModuleQuery & module_query) {
std::map<std::string, std::map<std::string, std::vector<int>>> module_dict;
for (const auto & module_item : module_query.list()) {
module_dict[module_item.get_name()][module_item.get_stream()].push_back(module_item.get_id().id);
}
return module_dict;
}


std::vector<std::string> ModuleSack::Impl::prune_module_dict(
std::map<std::string, std::map<std::string, std::vector<int>>> & module_dict) {
// Vector of module names with multiple streams to enable
std::vector<std::string> multiple_stream_modules;

for (auto & module_dict_iter : module_dict) {
auto name = module_dict_iter.first;
auto & stream_dict = module_dict_iter.second;
auto module_status = module_db->get_status(name);

// Multiple streams match the requested spec
if (stream_dict.size() > 1) {
// Get stream that is either enabled (for ENABLED module), or default (otherwise)
std::string enabled_or_default_stream;
if (module_status == ModuleStatus::ENABLED) {
enabled_or_default_stream = module_db->get_enabled_stream(name);
} else {
enabled_or_default_stream = module_sack->get_default_stream(name);
}

// Module doesn't have any enabled nor default stream
if (enabled_or_default_stream.empty()) {
multiple_stream_modules.emplace_back(name);
continue;
}

// The enabled or default stream is not one of the possible streams
if (stream_dict.find(enabled_or_default_stream) == stream_dict.end()) {
multiple_stream_modules.emplace_back(name);
continue;
}

// Remove all streams except for the enabled_or_default_stream
for (auto iter = stream_dict.begin(); iter != stream_dict.end();) {
if (iter->first != enabled_or_default_stream) {
stream_dict.erase(iter++);
} else {
++iter;
}
}
}
}
return multiple_stream_modules;
}


bool ModuleSack::Impl::enable(const std::string & module_spec, bool count) {
module_db->initialize();

// For the given module_spec, create a dict { name : {stream : [solvable id] }
auto module_dict = create_module_dict(module_spec_to_query(base, module_spec));
// Keep only enabled or default streams if possible
auto multiple_stream_modules = prune_module_dict(module_dict);
// If there are any modules with multiple streams to be enabled, throw an error
if (!multiple_stream_modules.empty()) {
throw EnableMultipleStreamsError(
M_("Cannot enable multiple streams of one module at the same time. Affected modules: {}"),
utils::string::join(multiple_stream_modules, ", "));
}

bool changed = false;
libdnf5::solv::IdQueue queue;
for (const auto & module_item : module_spec_to_query(base, module_spec)) {
queue.push_back(module_item.get_id().id);
changed |= enable(module_item.get_name(), module_item.get_stream(), count);
for (const auto & module_dict_iter : module_dict) {
std::string name = module_dict_iter.first;
for (const auto & stream_dict_iter : module_dict_iter.second) {
// Enable this stream
changed |= enable(name, stream_dict_iter.first, count);
// Create a queue of ids for the stream to be enabled, because it better matches user requirements
// than just "module(name:stream)" provides. (E.g. user might have requested specific context or version.)
for (const auto & id : stream_dict_iter.second) {
queue.push_back(id);
}
}
}
modules_to_enable.push_back(queue);

Expand Down
6 changes: 6 additions & 0 deletions libdnf5/module/module_sack_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ class ModuleSack::Impl {
/// @return If platform id was detected, it returns a pair where the first item is the platform
/// module name and second is the platform stream. Otherwise std::nullopt is returned.
std::optional<std::pair<std::string, std::string>> detect_platform_name_and_stream() const;

/// @brief Keep only one stream for each module. If more than one stream is originally there, keep only
// the enabled or default one if possible.
/// @return List of module names with multiple streams and no enabled or default one.
std::vector<std::string> prune_module_dict(
std::map<std::string, std::map<std::string, std::vector<int>>> & module_dict);
};

inline const std::vector<std::unique_ptr<ModuleItem>> & ModuleSack::Impl::get_modules() {
Expand Down

0 comments on commit 3539458

Please sign in to comment.