mirror of https://github.com/mamba-org/mamba.git
MatchSpec use VersionSpec (#3089)
* Don't use members as wip vars * Make conda-build compatible VersionSpec str * Add VersionSpec::is_explicitly_free * Strenghten sat attrs ref * Try pluging VersionSpec in MatchSpec * Change type of MatchSpec::version * Fix MatchSpec::conda_build_str * Adjust MatchSpec tests
This commit is contained in:
parent
108bc9f5a4
commit
b9706dce32
|
@ -14,6 +14,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "mamba/specs/channel_spec.hpp"
|
#include "mamba/specs/channel_spec.hpp"
|
||||||
|
#include "mamba/specs/version_spec.hpp"
|
||||||
|
|
||||||
namespace mamba::specs
|
namespace mamba::specs
|
||||||
{
|
{
|
||||||
|
@ -37,8 +38,8 @@ namespace mamba::specs
|
||||||
[[nodiscard]] auto name() const -> const std::string&;
|
[[nodiscard]] auto name() const -> const std::string&;
|
||||||
void set_name(std::string name);
|
void set_name(std::string name);
|
||||||
|
|
||||||
[[nodiscard]] auto version() const -> const std::string&;
|
[[nodiscard]] auto version() const -> const VersionSpec&;
|
||||||
void set_version(std::string ver);
|
void set_version(VersionSpec ver);
|
||||||
|
|
||||||
[[nodiscard]] auto build_number() const -> const std::string&;
|
[[nodiscard]] auto build_number() const -> const std::string&;
|
||||||
void set_build_number(std::string num);
|
void set_build_number(std::string num);
|
||||||
|
@ -67,9 +68,9 @@ namespace mamba::specs
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::optional<ChannelSpec> m_channel;
|
std::optional<ChannelSpec> m_channel;
|
||||||
|
VersionSpec m_version;
|
||||||
std::string m_name_space;
|
std::string m_name_space;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_version;
|
|
||||||
std::string m_build_number;
|
std::string m_build_number;
|
||||||
std::string m_build_string;
|
std::string m_build_string;
|
||||||
// TODO can put inside channel spec
|
// TODO can put inside channel spec
|
||||||
|
|
|
@ -48,6 +48,13 @@ namespace mamba::specs
|
||||||
|
|
||||||
[[nodiscard]] auto str() const -> std::string;
|
[[nodiscard]] auto str() const -> std::string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alternative string representation of the version spec.
|
||||||
|
*
|
||||||
|
* Attempts to be compatible with conda-build/libsolv.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto str_conda_build() const -> std::string;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
struct free_interval
|
struct free_interval
|
||||||
|
@ -148,6 +155,15 @@ namespace mamba::specs
|
||||||
VersionSpec() = default;
|
VersionSpec() = default;
|
||||||
explicit VersionSpec(tree_type&& tree) noexcept;
|
explicit VersionSpec(tree_type&& tree) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns wether the VersionSpec is unconstrained.
|
||||||
|
*
|
||||||
|
* Due to the complex nature of VersionSpec expressions, it is not always easy to know
|
||||||
|
* whether a complex expression can be simpified to the unconstrained one.
|
||||||
|
* This functions only handles the trivial cases.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto is_explicitly_free() const -> bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string representation of the version spec.
|
* A string representation of the version spec.
|
||||||
*
|
*
|
||||||
|
@ -156,6 +172,13 @@ namespace mamba::specs
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] auto str() const -> std::string;
|
[[nodiscard]] auto str() const -> std::string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alternative string representation of the version spec.
|
||||||
|
*
|
||||||
|
* Attempts to be compatible with conda-build/libsolv.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] auto str_conda_build() const -> std::string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the set described by the VersionSpec contains the given version.
|
* True if the set described by the VersionSpec contains the given version.
|
||||||
*/
|
*/
|
||||||
|
@ -177,6 +200,11 @@ namespace mamba::specs
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<mamba::specs::VersionPredicate>
|
struct fmt::formatter<mamba::specs::VersionPredicate>
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Change the representation of some predicates not understood by conda-build/libsolv.
|
||||||
|
*/
|
||||||
|
bool conda_build_form = false;
|
||||||
|
|
||||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());
|
||||||
|
|
||||||
auto format(const ::mamba::specs::VersionPredicate& pred, format_context& ctx)
|
auto format(const ::mamba::specs::VersionPredicate& pred, format_context& ctx)
|
||||||
|
@ -186,6 +214,11 @@ struct fmt::formatter<mamba::specs::VersionPredicate>
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<mamba::specs::VersionSpec>
|
struct fmt::formatter<mamba::specs::VersionSpec>
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Change the representation of some predicates not understood by conda-build/libsolv.
|
||||||
|
*/
|
||||||
|
bool conda_build_form = false;
|
||||||
|
|
||||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());
|
||||||
|
|
||||||
auto format(const ::mamba::specs::VersionSpec& spec, format_context& ctx) -> decltype(ctx.out());
|
auto format(const ::mamba::specs::VersionSpec& spec, format_context& ctx) -> decltype(ctx.out());
|
||||||
|
|
|
@ -358,7 +358,7 @@ namespace mamba
|
||||||
PackageInfo p(ms.name());
|
PackageInfo p(ms.name());
|
||||||
p.url = ms.url();
|
p.url = ms.url();
|
||||||
p.build_string = ms.build_string();
|
p.build_string = ms.build_string();
|
||||||
p.version = ms.version();
|
p.version = ms.version().str();
|
||||||
if (ms.channel().has_value())
|
if (ms.channel().has_value())
|
||||||
{
|
{
|
||||||
p.channel = ms.channel()->location();
|
p.channel = ms.channel()->location();
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace mamba
|
||||||
|
|
||||||
std::vector<old_node_id_list> groups{};
|
std::vector<old_node_id_list> groups{};
|
||||||
|
|
||||||
std::size_t const n_nodes = node_indices.size();
|
const std::size_t n_nodes = node_indices.size();
|
||||||
std::vector<bool> node_added_to_a_group(n_nodes, false);
|
std::vector<bool> node_added_to_a_group(n_nodes, false);
|
||||||
for (std::size_t i = 0; i < n_nodes; ++i)
|
for (std::size_t i = 0; i < n_nodes; ++i)
|
||||||
{
|
{
|
||||||
|
@ -538,14 +538,45 @@ namespace mamba
|
||||||
* Implementation of CompressedProblemsGraph::RoughCompare *
|
* Implementation of CompressedProblemsGraph::RoughCompare *
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
auto invoke_name(T&& e) -> decltype(auto)
|
||||||
|
{
|
||||||
|
using TT = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
return std::invoke(&TT::name, std::forward<T>(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto invoke_version(T&& e) -> decltype(auto)
|
||||||
|
{
|
||||||
|
using TT = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
using Ver = decltype(std::invoke(&TT::version, std::forward<T>(e)));
|
||||||
|
Ver v = std::invoke(&TT::version, std::forward<T>(e));
|
||||||
|
if constexpr (std::is_same_v<std::decay_t<decltype(v)>, specs::VersionSpec>)
|
||||||
|
{
|
||||||
|
return std::forward<Ver>(v).str();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool CompressedProblemsGraph::RoughCompare<T>::operator()(const T& a, const T& b) const
|
auto CompressedProblemsGraph::RoughCompare<T>::operator()(const T& a, const T& b) const -> bool
|
||||||
{
|
{
|
||||||
auto attrs = [](const auto& x)
|
auto attrs = [](const auto& x)
|
||||||
{
|
{
|
||||||
return std::tie(
|
using Attrs = std::tuple<
|
||||||
std::invoke(&T::name, x),
|
decltype(invoke_name(x)),
|
||||||
std::invoke(&T::version, x),
|
decltype(invoke_version(x)),
|
||||||
|
decltype(std::invoke(&T::build_number, x)),
|
||||||
|
decltype(std::invoke(&T::build_string, x))>;
|
||||||
|
return Attrs(
|
||||||
|
invoke_name(x),
|
||||||
|
invoke_version(x),
|
||||||
std::invoke(&T::build_number, x),
|
std::invoke(&T::build_number, x),
|
||||||
std::invoke(&T::build_string, x)
|
std::invoke(&T::build_string, x)
|
||||||
);
|
);
|
||||||
|
@ -562,16 +593,6 @@ namespace mamba
|
||||||
* Implementation of CompressedProblemsGraph::NamedList *
|
* Implementation of CompressedProblemsGraph::NamedList *
|
||||||
**********************************************************/
|
**********************************************************/
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
template <typename T>
|
|
||||||
decltype(auto) invoke_name(T&& e)
|
|
||||||
{
|
|
||||||
using TT = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
||||||
return std::invoke(&TT::name, std::forward<T>(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename A>
|
template <typename T, typename A>
|
||||||
template <typename InputIterator>
|
template <typename InputIterator>
|
||||||
CompressedProblemsGraph::NamedList<T, A>::NamedList(InputIterator first, InputIterator last)
|
CompressedProblemsGraph::NamedList<T, A>::NamedList(InputIterator first, InputIterator last)
|
||||||
|
@ -650,13 +671,13 @@ namespace mamba
|
||||||
) const -> std::pair<std::string, std::size_t>
|
) const -> std::pair<std::string, std::size_t>
|
||||||
{
|
{
|
||||||
auto versions = std::vector<std::string>(size());
|
auto versions = std::vector<std::string>(size());
|
||||||
auto invoke_version = [](auto&& v) -> decltype(auto)
|
|
||||||
{
|
|
||||||
using TT = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
|
|
||||||
return std::invoke(&TT::version, std::forward<decltype(v)>(v));
|
|
||||||
};
|
|
||||||
// TODO(C++20) *this | std::ranges::transform(invoke_version) | ranges::unique
|
// TODO(C++20) *this | std::ranges::transform(invoke_version) | ranges::unique
|
||||||
std::transform(begin(), end(), versions.begin(), invoke_version);
|
std::transform(
|
||||||
|
begin(),
|
||||||
|
end(),
|
||||||
|
versions.begin(),
|
||||||
|
[](const auto& x) { return invoke_version(x); }
|
||||||
|
);
|
||||||
if (remove_duplicates)
|
if (remove_duplicates)
|
||||||
{
|
{
|
||||||
versions.erase(std::unique(versions.begin(), versions.end()), versions.end());
|
versions.erase(std::unique(versions.begin(), versions.end()), versions.end());
|
||||||
|
@ -1036,7 +1057,7 @@ namespace mamba
|
||||||
|
|
||||||
const TreeNodeIter children_begin = out;
|
const TreeNodeIter children_begin = out;
|
||||||
// TODO(C++20) an enumerate view ``views::zip(views::iota(), children_ids)``
|
// TODO(C++20) an enumerate view ``views::zip(views::iota(), children_ids)``
|
||||||
std::size_t const n_children = children_ids.size();
|
const std::size_t n_children = children_ids.size();
|
||||||
for (std::size_t i = 0; i < n_children; ++i)
|
for (std::size_t i = 0; i < n_children; ++i)
|
||||||
{
|
{
|
||||||
const bool last = (i == n_children - 1);
|
const bool last = (i == n_children - 1);
|
||||||
|
@ -1228,7 +1249,7 @@ namespace mamba
|
||||||
|
|
||||||
void TreeExplainer::write_ancestry(const std::vector<SiblingNumber>& ancestry)
|
void TreeExplainer::write_ancestry(const std::vector<SiblingNumber>& ancestry)
|
||||||
{
|
{
|
||||||
std::size_t const size = ancestry.size();
|
const std::size_t size = ancestry.size();
|
||||||
const auto indents = m_format.indents;
|
const auto indents = m_format.indents;
|
||||||
if (size > 0)
|
if (size > 0)
|
||||||
{
|
{
|
||||||
|
@ -1434,7 +1455,7 @@ namespace mamba
|
||||||
|
|
||||||
void TreeExplainer::write_path(const std::vector<TreeNode>& path)
|
void TreeExplainer::write_path(const std::vector<TreeNode>& path)
|
||||||
{
|
{
|
||||||
std::size_t const length = path.size();
|
const std::size_t length = path.size();
|
||||||
for (std::size_t i = 0; i < length; ++i)
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
const bool last = (i == length - 1);
|
const bool last = (i == length - 1);
|
||||||
|
|
|
@ -96,7 +96,8 @@ namespace mamba
|
||||||
return m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id(ms));
|
return m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id(ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ms.channel().has_value() || !ms.version().empty() || !ms.build_string().empty())
|
if (ms.channel().has_value() || !ms.version().is_explicitly_free()
|
||||||
|
|| !ms.build_string().empty())
|
||||||
{
|
{
|
||||||
Console::stream() << ms.conda_build_form()
|
Console::stream() << ms.conda_build_form()
|
||||||
<< ": overriding channel, version and build from "
|
<< ": overriding channel, version and build from "
|
||||||
|
@ -105,7 +106,7 @@ namespace mamba
|
||||||
|
|
||||||
auto ms_modified = ms;
|
auto ms_modified = ms;
|
||||||
ms_modified.set_channel(specs::ChannelSpec::parse(solvable->channel()));
|
ms_modified.set_channel(specs::ChannelSpec::parse(solvable->channel()));
|
||||||
ms_modified.set_version(std::string(solvable->version()));
|
ms_modified.set_version(specs::VersionSpec::parse(solvable->version()));
|
||||||
ms_modified.set_build_string(std::string(solvable->build_string()));
|
ms_modified.set_build_string(std::string(solvable->build_string()));
|
||||||
|
|
||||||
LOG_INFO << "Reinstall " << ms_modified.conda_build_form() << " from channel "
|
LOG_INFO << "Reinstall " << ms_modified.conda_build_form() << " from channel "
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace mamba
|
||||||
auto& p = out.back();
|
auto& p = out.back();
|
||||||
p.url = ms.url();
|
p.url = ms.url();
|
||||||
p.build_string = ms.build_string();
|
p.build_string = ms.build_string();
|
||||||
p.version = ms.version();
|
p.version = ms.version().str_conda_build();
|
||||||
if (ms.channel().has_value())
|
if (ms.channel().has_value())
|
||||||
{
|
{
|
||||||
p.channel = ms.channel()->location();
|
p.channel = ms.channel()->location();
|
||||||
|
|
|
@ -77,7 +77,7 @@ namespace mamba::specs
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
std::tie(head, tail) = util::rsplit_once(head.value(), '-');
|
std::tie(head, tail) = util::rsplit_once(head.value(), '-');
|
||||||
out.m_version = tail;
|
out.m_version = VersionSpec::parse(tail);
|
||||||
if (!head.has_value())
|
if (!head.has_value())
|
||||||
{
|
{
|
||||||
fail_parse();
|
fail_parse();
|
||||||
|
@ -192,12 +192,14 @@ namespace mamba::specs
|
||||||
spec_str.push_back('*');
|
spec_str.push_back('*');
|
||||||
}
|
}
|
||||||
// This is #6 of the spec parsing
|
// This is #6 of the spec parsing
|
||||||
|
// Look for version *and* build string and separator
|
||||||
|
auto version_and_build = std::string();
|
||||||
static std::regex version_build_re("([^ =<>!~]+)?([><!=~ ].+)?");
|
static std::regex version_build_re("([^ =<>!~]+)?([><!=~ ].+)?");
|
||||||
std::smatch vb_match;
|
std::smatch vb_match;
|
||||||
if (std::regex_match(spec_str, vb_match, version_build_re))
|
if (std::regex_match(spec_str, vb_match, version_build_re))
|
||||||
{
|
{
|
||||||
out.m_name = vb_match[1].str();
|
out.m_name = vb_match[1].str();
|
||||||
out.m_version = util::strip(vb_match[2].str());
|
version_and_build = util::strip(vb_match[2].str());
|
||||||
if (out.m_name.size() == 0)
|
if (out.m_name.size() == 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid spec, no package name found: " + spec_str);
|
throw std::runtime_error("Invalid spec, no package name found: " + spec_str);
|
||||||
|
@ -210,9 +212,9 @@ namespace mamba::specs
|
||||||
|
|
||||||
// # Step 7. otherwise sort out version + build
|
// # Step 7. otherwise sort out version + build
|
||||||
// spec_str = spec_str and spec_str.strip()
|
// spec_str = spec_str and spec_str.strip()
|
||||||
if (!out.m_version.empty())
|
if (!version_and_build.empty())
|
||||||
{
|
{
|
||||||
if (out.m_version.find('[') != out.m_version.npos)
|
if (version_and_build.find('[') != std::string::npos)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(util::concat(
|
throw std::runtime_error(util::concat(
|
||||||
R"(Invalid match spec: multiple bracket sections not allowed ")",
|
R"(Invalid match spec: multiple bracket sections not allowed ")",
|
||||||
|
@ -221,38 +223,15 @@ namespace mamba::specs
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
out.m_version = std::string(util::strip(out.m_version));
|
auto [pv, pb] = parse_version_and_build(version_and_build);
|
||||||
auto [pv, pb] = parse_version_and_build(std::string(util::strip(out.m_version)));
|
|
||||||
|
|
||||||
out.m_version = pv;
|
out.m_version = VersionSpec::parse(pv);
|
||||||
out.m_build_string = pb;
|
out.m_build_string = pb;
|
||||||
|
|
||||||
// translate version '=1.2.3' to '1.2.3*'
|
|
||||||
// is it a simple version starting with '='? i.e. '=1.2.3'
|
|
||||||
if (out.m_version.size() >= 2 && out.m_version[0] == '=')
|
|
||||||
{
|
|
||||||
auto rest = out.m_version.substr(1);
|
|
||||||
if (out.m_version[1] == '=' && out.m_build_string.empty())
|
|
||||||
{
|
|
||||||
out.m_version = out.m_version.substr(2);
|
|
||||||
}
|
|
||||||
else if (rest.find_first_of("=,|") == rest.npos)
|
|
||||||
{
|
|
||||||
if (out.m_build_string.empty() && out.m_version.back() != '*')
|
|
||||||
{
|
|
||||||
out.m_version = util::concat(out.m_version, "*");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out.m_version = rest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else // no-op
|
||||||
{
|
{
|
||||||
out.m_version = "";
|
out.m_version = {};
|
||||||
out.m_build_string = "";
|
out.m_build_string = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO think about using a hash function here, (and elsewhere), like:
|
// TODO think about using a hash function here, (and elsewhere), like:
|
||||||
|
@ -270,7 +249,7 @@ namespace mamba::specs
|
||||||
}
|
}
|
||||||
else if (k == "version")
|
else if (k == "version")
|
||||||
{
|
{
|
||||||
out.m_version = v;
|
out.m_version = VersionSpec::parse(v);
|
||||||
}
|
}
|
||||||
else if (k == "channel")
|
else if (k == "channel")
|
||||||
{
|
{
|
||||||
|
@ -351,12 +330,12 @@ namespace mamba::specs
|
||||||
m_name = std::move(name);
|
m_name = std::move(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MatchSpec::version() const -> const std::string&
|
auto MatchSpec::version() const -> const VersionSpec&
|
||||||
{
|
{
|
||||||
return m_version;
|
return m_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatchSpec::set_version(std::string ver)
|
void MatchSpec::set_version(VersionSpec ver)
|
||||||
{
|
{
|
||||||
m_version = std::move(ver);
|
m_version = std::move(ver);
|
||||||
}
|
}
|
||||||
|
@ -405,14 +384,17 @@ namespace mamba::specs
|
||||||
{
|
{
|
||||||
std::stringstream res;
|
std::stringstream res;
|
||||||
res << m_name;
|
res << m_name;
|
||||||
if (!m_version.empty())
|
if (!m_version.is_explicitly_free())
|
||||||
{
|
{
|
||||||
res << " " << m_version;
|
res << " " << m_version.str_conda_build();
|
||||||
// if (!build.empty() && (build != "*"))
|
}
|
||||||
if (!m_build_string.empty())
|
else if (!m_build_string.empty())
|
||||||
{
|
{
|
||||||
res << " " << m_build_string;
|
res << " *";
|
||||||
}
|
}
|
||||||
|
if (!m_build_string.empty())
|
||||||
|
{
|
||||||
|
res << " " << m_build_string;
|
||||||
}
|
}
|
||||||
return res.str();
|
return res.str();
|
||||||
}
|
}
|
||||||
|
@ -453,50 +435,16 @@ namespace mamba::specs
|
||||||
auto is_complex_relation = [](const std::string& s)
|
auto is_complex_relation = [](const std::string& s)
|
||||||
{ return s.find_first_of("><$^|,") != s.npos; };
|
{ return s.find_first_of("><$^|,") != s.npos; };
|
||||||
|
|
||||||
if (!m_version.empty())
|
if (!m_version.is_explicitly_free())
|
||||||
{
|
{
|
||||||
if (is_complex_relation(m_version))
|
auto ver = m_version.str();
|
||||||
|
if (is_complex_relation(ver)) // TODO do on VersionSpec
|
||||||
{
|
{
|
||||||
formatted_brackets.push_back(util::concat("version='", m_version, "'"));
|
formatted_brackets.push_back(util::concat("version='", ver, "'"));
|
||||||
}
|
|
||||||
else if (util::starts_with(m_version, "!=") || util::starts_with(m_version, "~="))
|
|
||||||
{
|
|
||||||
if (!m_build_string.empty())
|
|
||||||
{
|
|
||||||
formatted_brackets.push_back(util::concat("version='", m_version, "'"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res << " " << m_version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (util::ends_with(m_version, ".*"))
|
|
||||||
{
|
|
||||||
res << "=" + m_version.substr(0, m_version.size() - 2);
|
|
||||||
}
|
|
||||||
else if (m_version.back() == '*')
|
|
||||||
{
|
|
||||||
if (m_version.size() == 1)
|
|
||||||
{
|
|
||||||
res << "=*";
|
|
||||||
}
|
|
||||||
else if (util::starts_with(m_version, "="))
|
|
||||||
{
|
|
||||||
res << m_version.substr(0, m_version.size() - 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res << "=" + m_version.substr(0, m_version.size() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (util::starts_with(m_version, "=="))
|
|
||||||
{
|
|
||||||
res << m_version;
|
|
||||||
version_exact = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res << "==" << m_version;
|
res << ver;
|
||||||
version_exact = true;
|
version_exact = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -566,7 +514,7 @@ namespace mamba::specs
|
||||||
|
|
||||||
auto MatchSpec::is_simple() const -> bool
|
auto MatchSpec::is_simple() const -> bool
|
||||||
{
|
{
|
||||||
return m_version.empty() && m_build_string.empty() && m_build_number.empty();
|
return m_version.is_explicitly_free() && m_build_string.empty() && m_build_number.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MatchSpec::is_file() const -> bool
|
auto MatchSpec::is_file() const -> bool
|
||||||
|
|
|
@ -160,6 +160,11 @@ namespace mamba::specs
|
||||||
return fmt::format("{}", *this);
|
return fmt::format("{}", *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto VersionPredicate::str_conda_build() const -> std::string
|
||||||
|
{
|
||||||
|
return fmt::format("{:b}", *this);
|
||||||
|
}
|
||||||
|
|
||||||
VersionPredicate::VersionPredicate(Version ver, BinaryOperator op)
|
VersionPredicate::VersionPredicate(Version ver, BinaryOperator op)
|
||||||
: m_version(std::move(ver))
|
: m_version(std::move(ver))
|
||||||
, m_operator(std::move(op))
|
, m_operator(std::move(op))
|
||||||
|
@ -181,10 +186,10 @@ auto
|
||||||
fmt::formatter<mamba::specs::VersionPredicate>::parse(format_parse_context& ctx)
|
fmt::formatter<mamba::specs::VersionPredicate>::parse(format_parse_context& ctx)
|
||||||
-> decltype(ctx.begin())
|
-> decltype(ctx.begin())
|
||||||
{
|
{
|
||||||
// make sure that range is empty
|
if (auto it = std::find(ctx.begin(), ctx.end(), 'b'); it < ctx.end())
|
||||||
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
|
|
||||||
{
|
{
|
||||||
throw fmt::format_error("Invalid format");
|
conda_build_form = true;
|
||||||
|
return ++it;
|
||||||
}
|
}
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
@ -239,7 +244,14 @@ fmt::formatter<mamba::specs::VersionPredicate>::format(
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<Op, VersionPredicate::starts_with>)
|
if constexpr (std::is_same_v<Op, VersionPredicate::starts_with>)
|
||||||
{
|
{
|
||||||
out = fmt::format_to(out, "{}{}", VersionSpec::starts_with_str, pred.m_version);
|
if (conda_build_form)
|
||||||
|
{
|
||||||
|
out = fmt::format_to(out, "{}{}", pred.m_version, VersionSpec::glob_suffix_str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out = fmt::format_to(out, "{}{}", VersionSpec::starts_with_str, pred.m_version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<Op, VersionPredicate::not_starts_with>)
|
if constexpr (std::is_same_v<Op, VersionPredicate::not_starts_with>)
|
||||||
{
|
{
|
||||||
|
@ -283,11 +295,23 @@ namespace mamba::specs
|
||||||
return m_tree.evaluate([&point](const auto& node) { return node.contains(point); });
|
return m_tree.evaluate([&point](const auto& node) { return node.contains(point); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto VersionSpec::is_explicitly_free() const -> bool
|
||||||
|
{
|
||||||
|
const auto free_pred = VersionPredicate::make_free();
|
||||||
|
const auto is_free_pred = [&free_pred](const auto& node) { return node == free_pred; };
|
||||||
|
return m_tree.empty() || ((m_tree.size() == 1) && m_tree.evaluate(is_free_pred));
|
||||||
|
}
|
||||||
|
|
||||||
auto VersionSpec::str() const -> std::string
|
auto VersionSpec::str() const -> std::string
|
||||||
{
|
{
|
||||||
return fmt::format("{}", *this);
|
return fmt::format("{}", *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto VersionSpec::str_conda_build() const -> std::string
|
||||||
|
{
|
||||||
|
return fmt::format("{:b}", *this);
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
template <typename Val, typename Range>
|
template <typename Val, typename Range>
|
||||||
|
@ -464,10 +488,10 @@ namespace mamba::specs
|
||||||
auto
|
auto
|
||||||
fmt::formatter<mamba::specs::VersionSpec>::parse(format_parse_context& ctx) -> decltype(ctx.begin())
|
fmt::formatter<mamba::specs::VersionSpec>::parse(format_parse_context& ctx) -> decltype(ctx.begin())
|
||||||
{
|
{
|
||||||
// make sure that range is empty
|
if (auto it = std::find(ctx.begin(), ctx.end(), 'b'); it < ctx.end())
|
||||||
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
|
|
||||||
{
|
{
|
||||||
throw fmt::format_error("Invalid format");
|
conda_build_form = true;
|
||||||
|
return ++it;
|
||||||
}
|
}
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
}
|
}
|
||||||
|
@ -511,7 +535,7 @@ fmt::formatter<mamba::specs::VersionSpec>::format(
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<Token, tree_type::variable_type>)
|
if constexpr (std::is_same_v<Token, tree_type::variable_type>)
|
||||||
{
|
{
|
||||||
out = fmt::format_to(out, "{}", token);
|
out = fmt::format_to(out, conda_build_form ? "{:b}" : "{}", token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,29 +67,29 @@ TEST_SUITE("specs::match_spec")
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("xtensor==0.12.3");
|
auto ms = MatchSpec::parse("xtensor==0.12.3");
|
||||||
CHECK_EQ(ms.version(), "0.12.3");
|
CHECK_EQ(ms.version().str(), "==0.12.3");
|
||||||
CHECK_EQ(ms.name(), "xtensor");
|
CHECK_EQ(ms.name(), "xtensor");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("");
|
auto ms = MatchSpec::parse("");
|
||||||
CHECK_EQ(ms.version(), "");
|
CHECK_EQ(ms.version().str(), "=*");
|
||||||
CHECK_EQ(ms.name(), "");
|
CHECK_EQ(ms.name(), "");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("ipykernel");
|
auto ms = MatchSpec::parse("ipykernel");
|
||||||
CHECK_EQ(ms.version(), "");
|
CHECK_EQ(ms.version().str(), "=*");
|
||||||
CHECK_EQ(ms.name(), "ipykernel");
|
CHECK_EQ(ms.name(), "ipykernel");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("ipykernel ");
|
auto ms = MatchSpec::parse("ipykernel ");
|
||||||
CHECK_EQ(ms.version(), "");
|
CHECK_EQ(ms.version().str(), "=*");
|
||||||
CHECK_EQ(ms.name(), "ipykernel");
|
CHECK_EQ(ms.name(), "ipykernel");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("numpy 1.7*");
|
auto ms = MatchSpec::parse("numpy 1.7*");
|
||||||
CHECK_EQ(ms.version(), "1.7*");
|
CHECK_EQ(ms.version().str(), "=1.7");
|
||||||
CHECK_EQ(ms.name(), "numpy");
|
CHECK_EQ(ms.name(), "numpy");
|
||||||
CHECK_EQ(ms.conda_build_form(), "numpy 1.7*");
|
CHECK_EQ(ms.conda_build_form(), "numpy 1.7.*");
|
||||||
CHECK_EQ(ms.str(), "numpy=1.7");
|
CHECK_EQ(ms.str(), "numpy=1.7");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -98,11 +98,11 @@ TEST_SUITE("specs::match_spec")
|
||||||
// CHECK_EQ(ms.version, "1.7|1.8");
|
// CHECK_EQ(ms.version, "1.7|1.8");
|
||||||
CHECK_EQ(ms.name(), "numpy");
|
CHECK_EQ(ms.name(), "numpy");
|
||||||
CHECK_EQ(ms.brackets["version"], "1.7|1.8");
|
CHECK_EQ(ms.brackets["version"], "1.7|1.8");
|
||||||
CHECK_EQ(ms.str(), "numpy[version='1.7|1.8']");
|
CHECK_EQ(ms.str(), "numpy[version='==1.7|==1.8']");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("conda-forge/linux-64::xtensor==0.12.3");
|
auto ms = MatchSpec::parse("conda-forge/linux-64::xtensor==0.12.3");
|
||||||
CHECK_EQ(ms.version(), "0.12.3");
|
CHECK_EQ(ms.version().str(), "==0.12.3");
|
||||||
CHECK_EQ(ms.name(), "xtensor");
|
CHECK_EQ(ms.name(), "xtensor");
|
||||||
REQUIRE(ms.channel().has_value());
|
REQUIRE(ms.channel().has_value());
|
||||||
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
||||||
|
@ -111,7 +111,7 @@ TEST_SUITE("specs::match_spec")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("conda-forge::foo[build=3](target=blarg,optional)");
|
auto ms = MatchSpec::parse("conda-forge::foo[build=3](target=blarg,optional)");
|
||||||
CHECK_EQ(ms.version(), "");
|
CHECK_EQ(ms.version().str(), "=*");
|
||||||
CHECK_EQ(ms.name(), "foo");
|
CHECK_EQ(ms.name(), "foo");
|
||||||
REQUIRE(ms.channel().has_value());
|
REQUIRE(ms.channel().has_value());
|
||||||
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
||||||
|
@ -136,7 +136,7 @@ TEST_SUITE("specs::match_spec")
|
||||||
"https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
"https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
||||||
);
|
);
|
||||||
CHECK_EQ(ms.name(), "_libgcc_mutex");
|
CHECK_EQ(ms.name(), "_libgcc_mutex");
|
||||||
CHECK_EQ(ms.version(), "0.1");
|
CHECK_EQ(ms.version().str(), "==0.1");
|
||||||
CHECK_EQ(ms.build_string(), "conda_forge");
|
CHECK_EQ(ms.build_string(), "conda_forge");
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
ms.url(),
|
ms.url(),
|
||||||
|
@ -149,7 +149,7 @@ TEST_SUITE("specs::match_spec")
|
||||||
"https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_13.tar.bz2"
|
"https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_13.tar.bz2"
|
||||||
);
|
);
|
||||||
CHECK_EQ(ms.name(), "libgcc-ng");
|
CHECK_EQ(ms.name(), "libgcc-ng");
|
||||||
CHECK_EQ(ms.version(), "11.2.0");
|
CHECK_EQ(ms.version().str(), "==11.2.0");
|
||||||
CHECK_EQ(ms.build_string(), "h1d223b6_13");
|
CHECK_EQ(ms.build_string(), "h1d223b6_13");
|
||||||
CHECK_EQ(
|
CHECK_EQ(
|
||||||
ms.url(),
|
ms.url(),
|
||||||
|
@ -162,7 +162,7 @@ TEST_SUITE("specs::match_spec")
|
||||||
"/home/randomguy/Downloads/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
"/home/randomguy/Downloads/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
||||||
);
|
);
|
||||||
CHECK_EQ(ms.name(), "_libgcc_mutex");
|
CHECK_EQ(ms.name(), "_libgcc_mutex");
|
||||||
CHECK_EQ(ms.version(), "0.1");
|
CHECK_EQ(ms.version().str(), "==0.1");
|
||||||
CHECK_EQ(ms.build_string(), "conda_forge");
|
CHECK_EQ(ms.build_string(), "conda_forge");
|
||||||
if (util::on_win)
|
if (util::on_win)
|
||||||
{
|
{
|
||||||
|
@ -199,21 +199,21 @@ TEST_SUITE("specs::match_spec")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("foo=1.0=2");
|
auto ms = MatchSpec::parse("foo=1.0=2");
|
||||||
CHECK_EQ(ms.conda_build_form(), "foo 1.0 2");
|
CHECK_EQ(ms.conda_build_form(), "foo 1.0.* 2");
|
||||||
CHECK_EQ(ms.str(), "foo==1.0=2");
|
CHECK_EQ(ms.str(), "foo=1.0=2");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("foo=1.0=2[md5=123123123, license=BSD-3, fn='test 123.tar.bz2']"
|
auto ms = MatchSpec::parse("foo=1.0=2[md5=123123123, license=BSD-3, fn='test 123.tar.bz2']"
|
||||||
);
|
);
|
||||||
CHECK_EQ(ms.conda_build_form(), "foo 1.0 2");
|
CHECK_EQ(ms.conda_build_form(), "foo 1.0.* 2");
|
||||||
CHECK_EQ(ms.str(), "foo==1.0=2[md5=123123123,license=BSD-3,fn='test 123.tar.bz2']");
|
CHECK_EQ(ms.str(), "foo=1.0=2[md5=123123123,license=BSD-3,fn='test 123.tar.bz2']");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse(
|
auto ms = MatchSpec::parse(
|
||||||
"foo=1.0=2[md5=123123123, license=BSD-3, fn='test 123.tar.bz2', url='abcdef']"
|
"foo=1.0=2[md5=123123123, license=BSD-3, fn='test 123.tar.bz2', url='abcdef']"
|
||||||
);
|
);
|
||||||
CHECK_EQ(ms.conda_build_form(), "foo 1.0 2");
|
CHECK_EQ(ms.conda_build_form(), "foo 1.0.* 2");
|
||||||
CHECK_EQ(ms.str(), "foo==1.0=2[url=abcdef,md5=123123123,license=BSD-3]");
|
CHECK_EQ(ms.str(), "foo=1.0=2[url=abcdef,md5=123123123,license=BSD-3]");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("libblas=*=*mkl");
|
auto ms = MatchSpec::parse("libblas=*=*mkl");
|
||||||
|
@ -222,12 +222,14 @@ TEST_SUITE("specs::match_spec")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("libblas=0.15*");
|
auto ms = MatchSpec::parse("libblas=0.15*");
|
||||||
CHECK_EQ(ms.conda_build_form(), "libblas 0.15*");
|
CHECK_EQ(ms.conda_build_form(), "libblas 0.15*.*");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
// '*' is part of the version, not the glob
|
||||||
auto ms = MatchSpec::parse("xtensor =0.15*");
|
auto ms = MatchSpec::parse("xtensor =0.15*");
|
||||||
CHECK_EQ(ms.conda_build_form(), "xtensor 0.15*");
|
CHECK_EQ(ms.conda_build_form(), "xtensor 0.15*.*");
|
||||||
CHECK_EQ(ms.str(), "xtensor=0.15");
|
CHECK_EQ(ms.str(), "xtensor=0.15*");
|
||||||
|
CHECK_EQ(ms.version().str(), "=0.15*");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto ms = MatchSpec::parse("numpy=1.20");
|
auto ms = MatchSpec::parse("numpy=1.20");
|
||||||
|
|
|
@ -80,6 +80,7 @@ TEST_SUITE("specs::version_spec")
|
||||||
CHECK_FALSE(sw.contains(v3));
|
CHECK_FALSE(sw.contains(v3));
|
||||||
CHECK_FALSE(sw.contains(v4));
|
CHECK_FALSE(sw.contains(v4));
|
||||||
CHECK_EQ(sw.str(), "=2.0");
|
CHECK_EQ(sw.str(), "=2.0");
|
||||||
|
CHECK_EQ(sw.str_conda_build(), "2.0.*");
|
||||||
|
|
||||||
const auto nsw = VersionPredicate::make_not_starts_with(v2);
|
const auto nsw = VersionPredicate::make_not_starts_with(v2);
|
||||||
CHECK(nsw.contains(v1));
|
CHECK(nsw.contains(v1));
|
||||||
|
@ -397,4 +398,42 @@ TEST_SUITE("specs::version_spec")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("VersionSpec::str")
|
||||||
|
{
|
||||||
|
SUBCASE("2.3")
|
||||||
|
{
|
||||||
|
auto vs = VersionSpec::parse("2.3");
|
||||||
|
CHECK_EQ(vs.str(), "==2.3");
|
||||||
|
CHECK_EQ(vs.str_conda_build(), "==2.3");
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("=2.3,<3.0")
|
||||||
|
{
|
||||||
|
auto vs = VersionSpec::parse("=2.3,<3.0");
|
||||||
|
CHECK_EQ(vs.str(), "=2.3,<3.0");
|
||||||
|
CHECK_EQ(vs.str_conda_build(), "2.3.*,<3.0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("VersionSpec::is_free")
|
||||||
|
{
|
||||||
|
{
|
||||||
|
using namespace mamba::util;
|
||||||
|
|
||||||
|
auto parser = InfixParser<VersionPredicate, BoolOperator>{};
|
||||||
|
parser.push_variable(VersionPredicate::make_free());
|
||||||
|
parser.finalize();
|
||||||
|
auto spec = VersionSpec(std::move(parser).tree());
|
||||||
|
|
||||||
|
CHECK(spec.is_explicitly_free());
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(VersionSpec().is_explicitly_free());
|
||||||
|
CHECK(VersionSpec::parse("*").is_explicitly_free());
|
||||||
|
CHECK(VersionSpec::parse("").is_explicitly_free());
|
||||||
|
|
||||||
|
CHECK_FALSE(VersionSpec::parse("==2.3|!=2.3").is_explicitly_free());
|
||||||
|
CHECK_FALSE(VersionSpec::parse("=2.3,<3.0").is_explicitly_free());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -666,4 +666,4 @@ def test_MatchSpec():
|
||||||
ms = MatchSpec.parse("conda-forge::python=3.7=*pypy")
|
ms = MatchSpec.parse("conda-forge::python=3.7=*pypy")
|
||||||
|
|
||||||
# str
|
# str
|
||||||
assert str(ms) == "conda-forge::python==3.7[build=*pypy]"
|
assert str(ms) == "conda-forge::python=3.7[build=*pypy]"
|
||||||
|
|
Loading…
Reference in New Issue