Use custom function to properly parse matchspec (#2433)

Make MPool::matchspec2id properly process channels
This commit is contained in:
Antoine Prouvost 2023-04-03 16:39:43 +02:00 committed by GitHub
parent e5451c94f9
commit 0345b9d7f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 122 additions and 129 deletions

View File

@ -8,6 +8,7 @@
#define MAMBA_CORE_MATCH_SPEC
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
@ -18,7 +19,7 @@ namespace mamba
public:
MatchSpec() = default;
MatchSpec(const std::string& i_spec);
MatchSpec(std::string_view i_spec);
void parse();
std::string conda_build_form() const;

View File

@ -17,6 +17,8 @@
namespace mamba
{
class MatchSpec;
/**
* Pool of solvable involved in resolving en environment.
*
@ -38,7 +40,7 @@ namespace mamba
void create_whatprovides();
std::vector<Id> select_solvables(Id id, bool sorted = false) const;
Id matchspec2id(const std::string& ms);
Id matchspec2id(const MatchSpec& ms);
std::optional<PackageInfo> id2pkginfo(Id solv_id) const;
std::optional<std::string> dep2str(Id dep_id) const;

View File

@ -104,7 +104,6 @@ namespace mamba
private:
void add_channel_specific_job(const MatchSpec& ms, int job_flag);
void add_reinstall_job(MatchSpec& ms, int job_flag);
std::vector<std::pair<int, int>> m_flags;

View File

@ -27,8 +27,7 @@ namespace mamba
return split_str;
}
MatchSpec::MatchSpec(const std::string& i_spec)
MatchSpec::MatchSpec(std::string_view i_spec)
: spec(i_spec)
{
parse();

View File

@ -18,6 +18,7 @@ extern "C" // Incomplete header
#include <spdlog/spdlog.h>
#include "mamba/core/context.hpp"
#include "mamba/core/match_spec.hpp"
#include "mamba/core/output.hpp"
#include "mamba/core/pool.hpp"
#include "mamba/core/util_string.hpp"
@ -148,10 +149,85 @@ namespace mamba
return solvables.as<std::vector>();
}
Id MPool::matchspec2id(const std::string& ms)
namespace
{
Id id = pool_conda_matchspec(pool(), ms.c_str());
if (!id)
bool channel_match(Solvable* s, const Channel& needle)
{
MRepo* mrepo = reinterpret_cast<MRepo*>(s->repo->appdata);
const Channel* chan = mrepo->channel();
if (!chan)
{
return false;
}
if ((*chan) == needle)
{
return true;
}
auto& custom_multichannels = Context::instance().custom_multichannels;
auto x = custom_multichannels.find(needle.name());
if (x != custom_multichannels.end())
{
for (auto el : (x->second))
{
const Channel& inner = make_channel(el);
if ((*chan) == inner)
{
return true;
}
}
}
return false;
}
::Id add_channel_specific_matchspec(::Pool* pool, const MatchSpec& ms)
{
// Poor man's ms repr to match waht the user provided
std::string const repr = fmt::format("{}::{}", ms.channel, ms.conda_build_form());
// Already added, return that id
if (::Id repr_id = pool_str2id(pool, repr.c_str(), /* .create= */ false); repr_id != 0)
{
return repr_id;
}
solv::ObjQueue selected_pkgs;
// conda_build_form does **NOT** contain the channel info
::Id match = pool_conda_matchspec(pool, ms.conda_build_form().c_str());
const Channel& c = make_channel(ms.channel);
for (Id* wp = pool_whatprovides_ptr(pool, match); *wp; wp++)
{
if (channel_match(pool_id2solvable(pool, *wp), c))
{
selected_pkgs.push_back(*wp);
}
}
::Id const repr_id = pool_str2id(pool, repr.c_str(), /* .create= */ true);
::Id const offset = pool_queuetowhatprovides(pool, selected_pkgs.raw());
pool_set_whatprovides(pool, repr_id, offset);
return repr_id;
}
}
::Id MPool::matchspec2id(const MatchSpec& ms)
{
::Id id = 0;
if (ms.channel.empty())
{
id = pool_conda_matchspec(pool(), ms.conda_build_form().c_str());
}
else
{
// Working around shortcomings of ``pool_conda_matchspec``
// The channels are not processed.
id = add_channel_specific_matchspec(pool(), ms);
}
if (id == 0)
{
throw std::runtime_error("libsolv error: could not create matchspec from string");
}

View File

@ -163,7 +163,10 @@ namespace mamba
problem.target_id,
PackageNode{ std::move(target).value() }
);
node_id cons_id = add_solvable(problem.dep_id, ConstraintNode{ dep.value() });
node_id cons_id = add_solvable(
problem.dep_id,
ConstraintNode{ { dep.value() } }
);
MatchSpec edge(dep.value());
m_graph.add_edge(src_id, cons_id, std::move(edge));
add_conflict(cons_id, tgt_id);
@ -225,7 +228,7 @@ namespace mamba
MatchSpec edge(dep.value());
node_id dep_id = add_solvable(
problem.dep_id,
UnresolvedDependencyNode{ std::move(dep).value() }
UnresolvedDependencyNode{ { std::move(dep).value() } }
);
m_graph.add_edge(m_root_node, dep_id, std::move(edge));
break;
@ -248,7 +251,7 @@ namespace mamba
);
node_id dep_id = add_solvable(
problem.dep_id,
UnresolvedDependencyNode{ std::move(dep).value() }
UnresolvedDependencyNode{ { std::move(dep).value() } }
);
m_graph.add_edge(src_id, dep_id, std::move(edge));
break;

View File

@ -11,10 +11,6 @@
#include <fmt/ostream.h>
#include <solv/pool.h>
#include <solv/solver.h>
extern "C" // Incomplete header
{
#include <solv/conda.h>
}
#include "mamba/core/channel.hpp"
#include "mamba/core/context.hpp"
@ -212,74 +208,11 @@ namespace mamba
MSolver::~MSolver() = default;
inline bool channel_match(Solvable* s, const Channel& needle)
{
MRepo* mrepo = reinterpret_cast<MRepo*>(s->repo->appdata);
const Channel* chan = mrepo->channel();
if (!chan)
{
return false;
}
if ((*chan) == needle)
{
return true;
}
auto& custom_multichannels = Context::instance().custom_multichannels;
auto x = custom_multichannels.find(needle.name());
if (x != custom_multichannels.end())
{
for (auto el : (x->second))
{
const Channel& inner = make_channel(el);
if ((*chan) == inner)
{
return true;
}
}
}
return false;
}
void MSolver::add_global_job(int job_flag)
{
m_jobs->push_back(job_flag, 0);
}
void MSolver::add_channel_specific_job(const MatchSpec& ms, int job_flag)
{
Pool* pool = m_pool;
solv::ObjQueue selected_pkgs;
// conda_build_form does **NOT** contain the channel info
Id match = pool_conda_matchspec(pool, ms.conda_build_form().c_str());
const Channel& c = make_channel(ms.channel);
for (Id* wp = pool_whatprovides_ptr(pool, match); *wp; wp++)
{
if (channel_match(pool_id2solvable(pool, *wp), c))
{
selected_pkgs.push_back(*wp);
}
}
if (selected_pkgs.size() == 0)
{
LOG_ERROR << "Selected channel specific (or force-reinstall) job, but "
"package is not available from channel. Solve job will fail.";
}
Id offset = pool_queuetowhatprovides(pool, selected_pkgs.raw());
// Poor man's ms repr to match waht the user provided
std::string const repr = fmt::format("{}::{}", ms.channel, ms.conda_build_form());
Id repr_id = pool_str2id(pool, repr.c_str(), 1);
// We add a new entry into the whatprovides to reflect the channel specific job
pool_set_whatprovides(pool, repr_id, offset);
// We ask to install that new entry
m_jobs->push_back(job_flag, repr_id);
}
void MSolver::add_reinstall_job(MatchSpec& ms, int job_flag)
{
Pool* const pool = m_pool;
@ -335,12 +268,15 @@ namespace mamba
);
LOG_INFO << "Reinstall " << modified_spec.conda_build_form() << " from channel "
<< selected_channel;
return add_channel_specific_job(modified_spec, job_flag);
m_jobs->push_back(
job_flag | SOLVER_SOLVABLE_PROVIDES,
m_pool.matchspec2id(modified_spec)
);
return;
}
}
}
Id inst_id = pool_conda_matchspec(m_pool, ms.conda_build_form().c_str());
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, inst_id);
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id(ms));
}
void MSolver::add_jobs(const std::vector<std::string>& jobs, int job_flag)
@ -363,56 +299,34 @@ namespace mamba
m_neuter_specs.emplace_back(job); // not used for the moment
}
::Id const job_id = m_pool.matchspec2id(ms);
// This is checking if SOLVER_ERASE and SOLVER_INSTALL are set
// which are the flags for SOLVER_UPDATE
if (((job_flag & SOLVER_UPDATE) ^ SOLVER_UPDATE) == 0)
if ((job_flag & SOLVER_UPDATE) == SOLVER_UPDATE)
{
// ignoring update specs here for now
if (!ms.is_simple())
{
Id inst_id = pool_conda_matchspec(m_pool, ms.conda_build_form().c_str());
m_jobs->push_back(SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, inst_id);
m_jobs->push_back(SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, job_id);
}
if (ms.channel.empty())
{
Id update_id = pool_conda_matchspec(m_pool, ms.name.c_str());
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, update_id);
}
else
{
add_channel_specific_job(ms, job_flag);
}
continue;
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, job_id);
}
if (!ms.channel.empty())
{
if (job_type == SOLVER_ERASE)
{
throw std::runtime_error("Cannot remove channel-specific spec '" + job + "'");
}
add_channel_specific_job(ms, job_flag);
}
else if (job_flag & SOLVER_INSTALL && force_reinstall)
else if ((job_flag & SOLVER_INSTALL) && force_reinstall)
{
add_reinstall_job(ms, job_flag);
}
else
{
// Todo remove double parsing?
LOG_INFO << "Adding job: " << ms.conda_build_form();
Id inst_id = pool_conda_matchspec(m_pool, ms.conda_build_form().c_str());
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, inst_id);
LOG_INFO << "Adding job: " << ms.str();
m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, job_id);
}
}
}
void MSolver::add_constraint(const std::string& job)
{
MatchSpec ms(job);
Id inst_id = pool_conda_matchspec(m_pool, ms.conda_build_form().c_str());
m_jobs->push_back(SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, inst_id);
m_jobs->push_back(SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id({ job }));
}
void MSolver::add_pin(const std::string& pin)
@ -445,20 +359,11 @@ namespace mamba
// }
// }
Id match = pool_conda_matchspec(pool, ms.conda_build_form().c_str());
Id match = m_pool.matchspec2id(ms);
std::set<Id> matching_solvables;
const Channel& c = make_channel(ms.channel);
for (Id* wp = pool_whatprovides_ptr(pool, match); *wp; wp++)
{
if (!ms.channel.empty())
{
if (!channel_match(pool_id2solvable(pool, *wp), c))
{
continue;
}
}
matching_solvables.insert(*wp);
}

View File

@ -1022,6 +1022,9 @@ class Pool:
def __init__(self) -> None: ...
def create_whatprovides(self) -> None: ...
def id2pkginfo(self, id: int) -> typing.Optional[PackageInfo]: ...
@typing.overload
def matchspec2id(self, ms: MatchSpec) -> int: ...
@typing.overload
def matchspec2id(self, ms: str) -> int: ...
def select_solvables(self, id: int, sorted: bool = False) -> typing.List[int]: ...
def set_debuglevel(self) -> None: ...

View File

@ -140,12 +140,22 @@ PYBIND11_MODULE(bindings, m)
py::add_ostream_redirect(m, "ostream_redirect");
py::class_<MatchSpec>(m, "MatchSpec")
.def(py::init<>())
.def(py::init<const std::string&>())
.def("conda_build_form", &MatchSpec::conda_build_form);
py::class_<MPool>(m, "Pool")
.def(py::init<>())
.def("set_debuglevel", &MPool::set_debuglevel)
.def("create_whatprovides", &MPool::create_whatprovides)
.def("select_solvables", &MPool::select_solvables, py::arg("id"), py::arg("sorted") = false)
.def("matchspec2id", &MPool::matchspec2id, py::arg("ms"))
.def(
"matchspec2id",
[](MPool& self, std::string_view ms) { return self.matchspec2id({ ms }); },
py::arg("ms")
)
.def("id2pkginfo", &MPool::id2pkginfo, py::arg("id"));
py::class_<MultiPackageCache>(m, "MultiPackageCache")
@ -260,11 +270,6 @@ PYBIND11_MODULE(bindings, m)
.def_readwrite("description", &MSolverProblem::description)
.def("__str__", [](const MSolverProblem& self) { return self.description; });
py::class_<MatchSpec>(m, "MatchSpec")
.def(py::init<>())
.def(py::init<const std::string&>())
.def("conda_build_form", &MatchSpec::conda_build_form);
using PbGraph = ProblemsGraph;
auto pyPbGraph = py::class_<PbGraph>(m, "ProblemsGraph");

View File

@ -44,11 +44,11 @@ update_self(const std::optional<std::string>& version)
std::string matchspec = version ? fmt::format("micromamba={}", version.value())
: fmt::format("micromamba>{}", umamba::version());
auto solvable_ids = pool.select_solvables(pool.matchspec2id(matchspec), true);
auto solvable_ids = pool.select_solvables(pool.matchspec2id({ matchspec }), true);
if (solvable_ids.empty())
{
if (pool.select_solvables(pool.matchspec2id("micromamba")).empty())
if (pool.select_solvables(pool.matchspec2id({ "micromamba" })).empty())
{
throw mamba::mamba_error(
"No micromamba found in the loaded channels. Add 'conda-forge' to your config file.",