mirror of https://github.com/mamba-org/mamba.git
GlobSpec (#3094)
* Fix include guard * Add GlobSpec * Use GlobSpec for MatchSpec build_string * Make MatchSpec::name a GlobSpec
This commit is contained in:
parent
de69846617
commit
18b77d62be
|
@ -173,11 +173,12 @@ set(
|
|||
${LIBMAMBA_SOURCE_DIR}/specs/channel.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/channel_spec.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/conda_url.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/glob_spec.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/match_spec.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/platform.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/repo_data.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/version.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/version_spec.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/specs/match_spec.cpp
|
||||
# Artifacts validation
|
||||
${LIBMAMBA_SOURCE_DIR}/validation/tools.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/validation/errors.cpp
|
||||
|
@ -287,11 +288,12 @@ set(
|
|||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/channel.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/channel_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/conda_url.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/glob_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/match_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/platform.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/repo_data.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/version.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/version_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/match_spec.hpp
|
||||
# Artifacts validation
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/validation/tools.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/validation/errors.hpp
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2023, QuantStack and Mamba Contributors
|
||||
//
|
||||
// Distributed under the terms of the BSD 3-Clause License.
|
||||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#ifndef MAMBA_SPECS_GLOB_SPEC
|
||||
#define MAMBA_SPECS_GLOB_SPEC
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace mamba::specs
|
||||
{
|
||||
/**
|
||||
* A matcher for glob expression.
|
||||
*
|
||||
* Currently only support "*" wildcard for matching zero or more characters.
|
||||
*/
|
||||
class GlobSpec
|
||||
{
|
||||
public:
|
||||
|
||||
inline static constexpr std::string_view free_pattern = "*";
|
||||
|
||||
GlobSpec() = default;
|
||||
explicit GlobSpec(std::string pattern);
|
||||
|
||||
[[nodiscard]] auto contains(std::string_view str) const -> bool;
|
||||
|
||||
/**
|
||||
* Return true if the spec will match true on any input.
|
||||
*/
|
||||
[[nodiscard]] auto is_free() const -> bool;
|
||||
|
||||
/**
|
||||
* Return true if the spec will match exaclty one input.
|
||||
*/
|
||||
[[nodiscard]] auto is_exact() const -> bool;
|
||||
|
||||
[[nodiscard]] auto str() const -> const std::string&;
|
||||
|
||||
private:
|
||||
|
||||
std::string m_pattern = std::string(free_pattern);
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<mamba::specs::GlobSpec>
|
||||
{
|
||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());
|
||||
|
||||
auto format(const ::mamba::specs::GlobSpec& spec, format_context& ctx) -> decltype(ctx.out());
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,8 +4,8 @@
|
|||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#ifndef MAMBA_CORE_MATCH_SPEC
|
||||
#define MAMBA_CORE_MATCH_SPEC
|
||||
#ifndef MAMBA_SPECS_MATCH_SPEC
|
||||
#define MAMBA_SPECS_MATCH_SPEC
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
@ -14,6 +14,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "mamba/specs/channel_spec.hpp"
|
||||
#include "mamba/specs/glob_spec.hpp"
|
||||
#include "mamba/specs/version_spec.hpp"
|
||||
|
||||
namespace mamba::specs
|
||||
|
@ -22,6 +23,9 @@ namespace mamba::specs
|
|||
{
|
||||
public:
|
||||
|
||||
using NameSpec = GlobSpec;
|
||||
using BuildStringSpec = GlobSpec;
|
||||
|
||||
[[nodiscard]] static auto parse_version_and_build(std::string_view s)
|
||||
-> std::tuple<std::string, std::string>;
|
||||
|
||||
|
@ -35,8 +39,8 @@ namespace mamba::specs
|
|||
[[nodiscard]] auto name_space() const -> const std::string&;
|
||||
void set_name_space(std::string ns);
|
||||
|
||||
[[nodiscard]] auto name() const -> const std::string&;
|
||||
void set_name(std::string name);
|
||||
[[nodiscard]] auto name() const -> const NameSpec&;
|
||||
void set_name(NameSpec name);
|
||||
|
||||
[[nodiscard]] auto version() const -> const VersionSpec&;
|
||||
void set_version(VersionSpec ver);
|
||||
|
@ -44,8 +48,8 @@ namespace mamba::specs
|
|||
[[nodiscard]] auto build_number() const -> const std::string&;
|
||||
void set_build_number(std::string num);
|
||||
|
||||
[[nodiscard]] auto build_string() const -> const std::string&;
|
||||
void set_build_string(std::string bs);
|
||||
[[nodiscard]] auto build_string() const -> const BuildStringSpec&;
|
||||
void set_build_string(BuildStringSpec bs);
|
||||
|
||||
[[nodiscard]] auto optional() const -> bool;
|
||||
void set_optional(bool opt);
|
||||
|
@ -69,10 +73,10 @@ namespace mamba::specs
|
|||
|
||||
std::optional<ChannelSpec> m_channel;
|
||||
VersionSpec m_version;
|
||||
NameSpec m_name;
|
||||
BuildStringSpec m_build_string;
|
||||
std::string m_name_space;
|
||||
std::string m_name;
|
||||
std::string m_build_number;
|
||||
std::string m_build_string;
|
||||
// TODO can put inside channel spec
|
||||
std::string m_filename;
|
||||
std::string m_url;
|
||||
|
|
|
@ -355,9 +355,9 @@ namespace mamba
|
|||
}
|
||||
std::size_t hash = u.find_first_of('#');
|
||||
auto ms = specs::MatchSpec::parse(u.substr(0, hash));
|
||||
PackageInfo p(ms.name());
|
||||
PackageInfo p(ms.name().str());
|
||||
p.url = ms.url();
|
||||
p.build_string = ms.build_string();
|
||||
p.build_string = ms.build_string().str();
|
||||
p.version = ms.version().str();
|
||||
if (ms.channel().has_value())
|
||||
{
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace mamba
|
|||
std::vector<std::string> keep_specs;
|
||||
for (auto& it : hist_map)
|
||||
{
|
||||
keep_specs.push_back(it.second.name());
|
||||
keep_specs.push_back(it.second.name().str());
|
||||
}
|
||||
solver_flag |= SOLVER_SOLVABLE_ALL;
|
||||
if (prune_deps)
|
||||
|
@ -136,10 +136,10 @@ namespace mamba
|
|||
std::vector<std::string> remove_specs;
|
||||
for (auto& it : hist_map)
|
||||
{
|
||||
if (std::find(update_specs.begin(), update_specs.end(), it.second.name())
|
||||
if (std::find(update_specs.begin(), update_specs.end(), it.second.name().str())
|
||||
== update_specs.end())
|
||||
{
|
||||
remove_specs.push_back(it.second.name());
|
||||
remove_specs.push_back(it.second.name().str());
|
||||
}
|
||||
}
|
||||
solver.add_jobs(remove_specs, SOLVER_ERASE | SOLVER_CLEANDEPS);
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace mamba
|
|||
package.info.url = package_node["url"].as<std::string>();
|
||||
const auto spec = specs::MatchSpec::parse(package.info.url);
|
||||
package.info.fn = spec.filename();
|
||||
package.info.build_string = spec.build_string();
|
||||
package.info.build_string = spec.build_string().str();
|
||||
if (spec.channel().has_value())
|
||||
{
|
||||
package.info.channel = spec.channel()->location();
|
||||
|
|
|
@ -211,17 +211,17 @@ namespace mamba
|
|||
auto remove_specs = to_specs(request.remove);
|
||||
for (auto& spec : remove_specs)
|
||||
{
|
||||
map.erase(spec.name());
|
||||
map.erase(spec.name().str());
|
||||
}
|
||||
auto update_specs = to_specs(request.update);
|
||||
for (auto& spec : update_specs)
|
||||
{
|
||||
map[spec.name()] = spec;
|
||||
map[spec.name().str()] = spec;
|
||||
}
|
||||
auto neutered_specs = to_specs(request.neutered);
|
||||
for (auto& spec : neutered_specs)
|
||||
{
|
||||
map[spec.name()] = spec;
|
||||
map[spec.name().str()] = spec;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -996,7 +996,7 @@ namespace mamba
|
|||
specs::MatchSpec* requested_spec = nullptr;
|
||||
for (auto& ms : m_context->requested_specs)
|
||||
{
|
||||
if (ms.name() == m_pkg_info.name)
|
||||
if (ms.name().contains(m_pkg_info.name))
|
||||
{
|
||||
requested_spec = &ms;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace mamba
|
|||
|
||||
for (const auto& spec : specs)
|
||||
{
|
||||
if (specs::MatchSpec::parse(spec).name() == "python")
|
||||
if (specs::MatchSpec::parse(spec).name().contains("python"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace mamba
|
|||
auto ms = specs::MatchSpec::parse(dep);
|
||||
// Ignoring unmatched dependencies, the environment could be broken
|
||||
// or it could be a matchspec
|
||||
const auto from_iter = name_to_node_id.find(ms.name());
|
||||
const auto from_iter = name_to_node_id.find(ms.name().str());
|
||||
if (from_iter != name_to_node_id.cend())
|
||||
{
|
||||
dep_graph.add_edge(from_iter->second, to_id);
|
||||
|
|
|
@ -265,6 +265,54 @@ namespace mamba
|
|||
return CompressedProblemsGraph::NamedList<O>(tmp.begin(), tmp.end());
|
||||
}
|
||||
|
||||
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>
|
||||
auto invoke_build_string(T&& e) -> decltype(auto)
|
||||
{
|
||||
using TT = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
using Build = decltype(std::invoke(&TT::build_string, std::forward<T>(e)));
|
||||
Build bld = std::invoke(&TT::build_string, std::forward<T>(e));
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(bld)>, specs::GlobSpec>)
|
||||
{
|
||||
return std::forward<Build>(bld).str();
|
||||
}
|
||||
else
|
||||
{
|
||||
return bld;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto invoke_name(T&& e) -> decltype(auto)
|
||||
{
|
||||
using TT = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
using Name = decltype(std::invoke(&TT::name, std::forward<T>(e)));
|
||||
Name name = std::invoke(&TT::name, std::forward<T>(e));
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(name)>, specs::GlobSpec>)
|
||||
{
|
||||
return std::forward<Name>(name).str();
|
||||
}
|
||||
else
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if a type has a ``name`` member (function).
|
||||
*/
|
||||
|
@ -287,7 +335,7 @@ namespace mamba
|
|||
{
|
||||
if constexpr (has_name_v<T>)
|
||||
{
|
||||
return std::invoke(&T::name, obj);
|
||||
return invoke_name(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -538,32 +586,6 @@ namespace mamba
|
|||
* 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>
|
||||
auto CompressedProblemsGraph::RoughCompare<T>::operator()(const T& a, const T& b) const -> bool
|
||||
{
|
||||
|
@ -573,12 +595,12 @@ namespace mamba
|
|||
decltype(invoke_name(x)),
|
||||
decltype(invoke_version(x)),
|
||||
decltype(std::invoke(&T::build_number, x)),
|
||||
decltype(std::invoke(&T::build_string, x))>;
|
||||
decltype(invoke_build_string(x))>;
|
||||
return Attrs(
|
||||
invoke_name(x),
|
||||
invoke_version(x),
|
||||
std::invoke(&T::build_number, x),
|
||||
std::invoke(&T::build_string, x)
|
||||
invoke_build_string(x)
|
||||
);
|
||||
};
|
||||
return attrs(a) < attrs(b);
|
||||
|
@ -694,13 +716,13 @@ namespace mamba
|
|||
) const -> std::pair<std::string, std::size_t>
|
||||
{
|
||||
auto builds = std::vector<std::string>(size());
|
||||
auto invoke_build_string = [](auto&& v) -> decltype(auto)
|
||||
{
|
||||
using TT = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
|
||||
return std::invoke(&TT::build_string, std::forward<decltype(v)>(v));
|
||||
};
|
||||
// TODO(C++20) *this | std::ranges::transform(invoke_buid_string) | ranges::unique
|
||||
std::transform(begin(), end(), builds.begin(), invoke_build_string);
|
||||
std::transform(
|
||||
begin(),
|
||||
end(),
|
||||
builds.begin(),
|
||||
[](const auto& p) { return invoke_build_string(p); }
|
||||
);
|
||||
if (remove_duplicates)
|
||||
{
|
||||
builds.erase(std::unique(builds.begin(), builds.end()), builds.end());
|
||||
|
@ -718,14 +740,7 @@ namespace mamba
|
|||
{
|
||||
auto versions_builds = std::vector<std::string>(size());
|
||||
auto invoke_version_builds = [](auto&& v) -> decltype(auto)
|
||||
{
|
||||
using TT = std::remove_cv_t<std::remove_reference_t<decltype(v)>>;
|
||||
return fmt::format(
|
||||
"{} {}",
|
||||
std::invoke(&TT::version, std::forward<decltype(v)>(v)),
|
||||
std::invoke(&TT::build_string, std::forward<decltype(v)>(v))
|
||||
);
|
||||
};
|
||||
{ return fmt::format("{} {}", invoke_version(v), invoke_build_string(v)); };
|
||||
// TODO(C++20) *this | std::ranges::transform(invoke_version) | ranges::unique
|
||||
std::transform(begin(), end(), versions_builds.begin(), invoke_version_builds);
|
||||
if (remove_duplicates)
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace mamba
|
|||
m_pool.pool().for_each_installed_solvable(
|
||||
[&](solv::ObjSolvableViewConst s)
|
||||
{
|
||||
if (s.name() == ms.name())
|
||||
if (ms.name().contains(s.name()))
|
||||
{
|
||||
solvable = s;
|
||||
return solv::LoopControl::Break;
|
||||
|
@ -97,7 +97,7 @@ namespace mamba
|
|||
}
|
||||
|
||||
if (ms.channel().has_value() || !ms.version().is_explicitly_free()
|
||||
|| !ms.build_string().empty())
|
||||
|| !ms.build_string().is_free())
|
||||
{
|
||||
Console::stream() << ms.conda_build_form()
|
||||
<< ": overriding channel, version and build from "
|
||||
|
@ -107,7 +107,7 @@ namespace mamba
|
|||
auto ms_modified = ms;
|
||||
ms_modified.set_channel(specs::ChannelSpec::parse(solvable->channel()));
|
||||
ms_modified.set_version(specs::VersionSpec::parse(solvable->version()));
|
||||
ms_modified.set_build_string(std::string(solvable->build_string()));
|
||||
ms_modified.set_build_string(specs::GlobSpec(std::string(solvable->build_string())));
|
||||
|
||||
LOG_INFO << "Reinstall " << ms_modified.conda_build_form() << " from channel "
|
||||
<< ms_modified.channel()->str();
|
||||
|
@ -745,8 +745,8 @@ namespace mamba
|
|||
// dependency.
|
||||
auto edge = specs::MatchSpec::parse(source.value().name);
|
||||
// The package cannot exist without its name in the pool
|
||||
assert(m_pool.pool().find_string(edge.name()).has_value());
|
||||
const auto dep_id = m_pool.pool().find_string(edge.name()).value();
|
||||
assert(m_pool.pool().find_string(edge.name().str()).has_value());
|
||||
const auto dep_id = m_pool.pool().find_string(edge.name().str()).value();
|
||||
const bool added = add_expanded_deps_edges(m_root_node, dep_id, edge);
|
||||
if (!added)
|
||||
{
|
||||
|
|
|
@ -69,10 +69,10 @@ namespace mamba
|
|||
|
||||
for (auto& ms : specs)
|
||||
{
|
||||
out.emplace_back(ms.name());
|
||||
out.emplace_back(ms.name().str());
|
||||
auto& p = out.back();
|
||||
p.url = ms.url();
|
||||
p.build_string = ms.build_string();
|
||||
p.build_string = ms.build_string().str();
|
||||
p.version = ms.version().str_conda_build();
|
||||
if (ms.channel().has_value())
|
||||
{
|
||||
|
@ -110,7 +110,7 @@ namespace mamba
|
|||
to_install_specs.cbegin(),
|
||||
to_install_specs.cend(),
|
||||
std::back_inserter(to_install_names),
|
||||
[](const auto& spec) { return spec.name(); }
|
||||
[](const auto& spec) { return spec.name().str(); }
|
||||
);
|
||||
|
||||
const auto& to_remove_specs = solver.remove_specs();
|
||||
|
@ -120,7 +120,7 @@ namespace mamba
|
|||
to_remove_specs.cbegin(),
|
||||
to_remove_specs.cend(),
|
||||
std::back_inserter(to_remove_names),
|
||||
[](const auto& spec) { return spec.name(); }
|
||||
[](const auto& spec) { return spec.name().str(); }
|
||||
);
|
||||
|
||||
auto specs = util::flat_set<std::string>{};
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) 2023, QuantStack and Mamba Contributors
|
||||
//
|
||||
// Distributed under the terms of the BSD 3-Clause License.
|
||||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mamba/specs/glob_spec.hpp"
|
||||
#include "mamba/util/parsers.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
namespace mamba::specs
|
||||
{
|
||||
GlobSpec::GlobSpec(std::string pattern)
|
||||
: m_pattern(std::move(pattern))
|
||||
{
|
||||
// Not sure what to make of empty patterns... A "match nothing"?
|
||||
if (m_pattern.empty())
|
||||
{
|
||||
m_pattern = free_pattern;
|
||||
}
|
||||
}
|
||||
|
||||
auto GlobSpec::contains(std::string_view str) const -> bool
|
||||
{
|
||||
// is_free is not required but returns faster in the default case.
|
||||
return is_free() || util::glob_match(m_pattern, str);
|
||||
}
|
||||
|
||||
auto GlobSpec::is_free() const -> bool
|
||||
{
|
||||
return m_pattern == free_pattern;
|
||||
}
|
||||
|
||||
auto GlobSpec::is_exact() const -> bool
|
||||
{
|
||||
return !util::contains(m_pattern, '*');
|
||||
}
|
||||
|
||||
auto GlobSpec::str() const -> const std::string&
|
||||
{
|
||||
return m_pattern;
|
||||
}
|
||||
}
|
||||
|
||||
auto
|
||||
fmt::formatter<mamba::specs::GlobSpec>::parse(format_parse_context& ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
// make sure that range is empty
|
||||
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
|
||||
{
|
||||
throw fmt::format_error("Invalid format");
|
||||
}
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
auto
|
||||
fmt::formatter<mamba::specs::GlobSpec>::format(const ::mamba::specs::GlobSpec& spec, format_context& ctx)
|
||||
-> decltype(ctx.out())
|
||||
{
|
||||
return fmt::format_to(ctx.out(), "{}", spec.str());
|
||||
}
|
|
@ -69,7 +69,7 @@ namespace mamba::specs
|
|||
|
||||
// Build string
|
||||
auto [head, tail] = util::rsplit_once(strip_archive_extension(pkg), '-');
|
||||
out.m_build_string = tail;
|
||||
out.m_build_string = BuildStringSpec(std::string(tail));
|
||||
if (!head.has_value())
|
||||
{
|
||||
fail_parse();
|
||||
|
@ -84,7 +84,7 @@ namespace mamba::specs
|
|||
}
|
||||
|
||||
// Name
|
||||
out.m_name = head.value(); // There may be '-' in the name
|
||||
out.m_name = NameSpec(std::string(head.value())); // There may be '-' in the name
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -198,12 +198,8 @@ namespace mamba::specs
|
|||
std::smatch vb_match;
|
||||
if (std::regex_match(spec_str, vb_match, version_build_re))
|
||||
{
|
||||
out.m_name = vb_match[1].str();
|
||||
out.m_name = NameSpec(vb_match[1].str());
|
||||
version_and_build = util::strip(vb_match[2].str());
|
||||
if (out.m_name.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("Invalid spec, no package name found: " + spec_str);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -226,7 +222,7 @@ namespace mamba::specs
|
|||
auto [pv, pb] = parse_version_and_build(version_and_build);
|
||||
|
||||
out.m_version = VersionSpec::parse(pv);
|
||||
out.m_build_string = pb;
|
||||
out.m_build_string = BuildStringSpec(std::string(pb));
|
||||
}
|
||||
else // no-op
|
||||
{
|
||||
|
@ -245,7 +241,7 @@ namespace mamba::specs
|
|||
}
|
||||
else if (k == "build")
|
||||
{
|
||||
out.m_build_string = v;
|
||||
out.m_build_string = MatchSpec::BuildStringSpec(std::string(v));
|
||||
}
|
||||
else if (k == "version")
|
||||
{
|
||||
|
@ -320,12 +316,12 @@ namespace mamba::specs
|
|||
m_name_space = std::move(ns);
|
||||
}
|
||||
|
||||
auto MatchSpec::name() const -> const std::string&
|
||||
auto MatchSpec::name() const -> const NameSpec&
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void MatchSpec::set_name(std::string name)
|
||||
void MatchSpec::set_name(NameSpec name)
|
||||
{
|
||||
m_name = std::move(name);
|
||||
}
|
||||
|
@ -350,7 +346,7 @@ namespace mamba::specs
|
|||
m_build_number = std::move(bn);
|
||||
}
|
||||
|
||||
auto MatchSpec::build_string() const -> const std::string&
|
||||
auto MatchSpec::build_string() const -> const BuildStringSpec&
|
||||
{
|
||||
return m_build_string;
|
||||
}
|
||||
|
@ -365,7 +361,7 @@ namespace mamba::specs
|
|||
m_optional = opt;
|
||||
}
|
||||
|
||||
void MatchSpec::set_build_string(std::string bs)
|
||||
void MatchSpec::set_build_string(BuildStringSpec bs)
|
||||
{
|
||||
m_build_string = std::move(bs);
|
||||
}
|
||||
|
@ -382,21 +378,24 @@ namespace mamba::specs
|
|||
|
||||
auto MatchSpec::conda_build_form() const -> std::string
|
||||
{
|
||||
std::stringstream res;
|
||||
res << m_name;
|
||||
if (!m_version.is_explicitly_free())
|
||||
const bool has_version = !m_version.is_explicitly_free();
|
||||
const bool has_build_str = !m_build_string.is_free();
|
||||
if (has_version)
|
||||
{
|
||||
res << " " << m_version.str_conda_build();
|
||||
if (has_build_str)
|
||||
{
|
||||
return fmt::format("{} {:b} {}", m_name, m_version, m_build_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fmt::format("{} {:b}", m_name, m_version);
|
||||
}
|
||||
}
|
||||
else if (!m_build_string.empty())
|
||||
if (has_build_str)
|
||||
{
|
||||
res << " *";
|
||||
return fmt::format("{} * {}", m_name, m_build_string);
|
||||
}
|
||||
if (!m_build_string.empty())
|
||||
{
|
||||
res << " " << m_build_string;
|
||||
}
|
||||
return res.str();
|
||||
return fmt::format("{}", m_name);
|
||||
}
|
||||
|
||||
auto MatchSpec::str() const -> std::string
|
||||
|
@ -428,9 +427,8 @@ namespace mamba::specs
|
|||
// res << ns;
|
||||
// res << ":";
|
||||
// }
|
||||
res << (!m_name.empty() ? m_name : "*");
|
||||
res << m_name.str();
|
||||
std::vector<std::string> formatted_brackets;
|
||||
bool version_exact = false;
|
||||
|
||||
auto is_complex_relation = [](const std::string& s)
|
||||
{ return s.find_first_of("><$^|,") != s.npos; };
|
||||
|
@ -445,27 +443,19 @@ namespace mamba::specs
|
|||
else
|
||||
{
|
||||
res << ver;
|
||||
version_exact = true;
|
||||
// version_exact = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_build_string.empty())
|
||||
if (!m_build_string.is_free())
|
||||
{
|
||||
if (is_complex_relation(m_build_string))
|
||||
if (m_build_string.is_exact())
|
||||
{
|
||||
formatted_brackets.push_back(util::concat("build='", m_build_string, '\''));
|
||||
}
|
||||
else if (m_build_string.find('*') != m_build_string.npos)
|
||||
{
|
||||
formatted_brackets.push_back(util::concat("build=", m_build_string));
|
||||
}
|
||||
else if (version_exact)
|
||||
{
|
||||
res << "=" << m_build_string;
|
||||
res << "=" << m_build_string.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
formatted_brackets.push_back(util::concat("build=", m_build_string));
|
||||
formatted_brackets.push_back(util::concat("build='", m_build_string.str(), '\''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,7 +504,7 @@ namespace mamba::specs
|
|||
|
||||
auto MatchSpec::is_simple() const -> bool
|
||||
{
|
||||
return m_version.is_explicitly_free() && m_build_string.empty() && m_build_number.empty();
|
||||
return m_version.is_explicitly_free() && m_build_string.is_free() && m_build_number.empty();
|
||||
}
|
||||
|
||||
auto MatchSpec::is_file() const -> bool
|
||||
|
|
|
@ -46,11 +46,12 @@ set(
|
|||
src/specs/test_channel.cpp
|
||||
src/specs/test_channel_spec.cpp
|
||||
src/specs/test_conda_url.cpp
|
||||
src/specs/test_glob_spec.cpp
|
||||
src/specs/test_match_spec.cpp
|
||||
src/specs/test_platform.cpp
|
||||
src/specs/test_repo_data.cpp
|
||||
src/specs/test_version.cpp
|
||||
src/specs/test_version_spec.cpp
|
||||
src/specs/test_match_spec.cpp
|
||||
# Artifacts validation
|
||||
src/validation/test_tools.cpp
|
||||
src/validation/test_update_framework_v0_6.cpp
|
||||
|
|
|
@ -479,12 +479,19 @@ namespace
|
|||
return std::visit(
|
||||
[](const auto& n) -> bool
|
||||
{
|
||||
using Node = std::remove_const_t<std::remove_reference_t<decltype(n)>>;
|
||||
if constexpr (!std::is_same_v<Node, ProblemsGraph::RootNode>)
|
||||
using Node = std::decay_t<decltype(n)>;
|
||||
if constexpr (std::is_same_v<Node, ProblemsGraph::RootNode>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if constexpr (std::is_same_v<Node, ProblemsGraph::UnresolvedDependencyNode> || std::is_same_v<Node, ProblemsGraph::ConstraintNode>)
|
||||
{
|
||||
return util::starts_with(std::invoke(&Node::name, n).str(), "__");
|
||||
}
|
||||
else
|
||||
{
|
||||
return util::starts_with(std::invoke(&Node::name, n), "__");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
node
|
||||
);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2023, QuantStack and Mamba Contributors
|
||||
//
|
||||
// Distributed under the terms of the BSD 3-Clause License.
|
||||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include "mamba/specs/glob_spec.hpp"
|
||||
|
||||
using namespace mamba::specs;
|
||||
|
||||
TEST_SUITE("specs::glob_spec")
|
||||
{
|
||||
// See also test_parser for Glob matcher tests
|
||||
|
||||
TEST_CASE("Free")
|
||||
{
|
||||
auto spec = GlobSpec();
|
||||
|
||||
CHECK(spec.contains(""));
|
||||
CHECK(spec.contains("hello"));
|
||||
|
||||
CHECK_EQ(spec.str(), "*");
|
||||
CHECK(spec.is_free());
|
||||
CHECK_FALSE(spec.is_exact());
|
||||
}
|
||||
|
||||
TEST_CASE("*mkl*")
|
||||
{
|
||||
auto spec = GlobSpec("mkl");
|
||||
|
||||
CHECK(spec.contains("mkl"));
|
||||
CHECK_FALSE(spec.contains(""));
|
||||
CHECK_FALSE(spec.contains("nomkl"));
|
||||
CHECK_FALSE(spec.contains("hello"));
|
||||
|
||||
CHECK_EQ(spec.str(), "mkl");
|
||||
CHECK_FALSE(spec.is_free());
|
||||
CHECK(spec.is_exact());
|
||||
}
|
||||
|
||||
TEST_CASE("*py*")
|
||||
{
|
||||
// See also test_parser for Glob matcher tests
|
||||
auto spec = GlobSpec("*py*");
|
||||
|
||||
CHECK(spec.contains("py"));
|
||||
CHECK(spec.contains("pypy"));
|
||||
CHECK(spec.contains("cpython-linux-64"));
|
||||
CHECK_FALSE(spec.contains("rust"));
|
||||
CHECK_FALSE(spec.contains("hello"));
|
||||
|
||||
CHECK_EQ(spec.str(), "*py*");
|
||||
CHECK_FALSE(spec.is_free());
|
||||
CHECK_FALSE(spec.is_exact());
|
||||
}
|
||||
}
|
|
@ -68,27 +68,27 @@ TEST_SUITE("specs::match_spec")
|
|||
{
|
||||
auto ms = MatchSpec::parse("xtensor==0.12.3");
|
||||
CHECK_EQ(ms.version().str(), "==0.12.3");
|
||||
CHECK_EQ(ms.name(), "xtensor");
|
||||
CHECK_EQ(ms.name().str(), "xtensor");
|
||||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("");
|
||||
CHECK_EQ(ms.version().str(), "=*");
|
||||
CHECK_EQ(ms.name(), "");
|
||||
CHECK_EQ(ms.name().str(), "*");
|
||||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("ipykernel");
|
||||
CHECK_EQ(ms.version().str(), "=*");
|
||||
CHECK_EQ(ms.name(), "ipykernel");
|
||||
CHECK_EQ(ms.name().str(), "ipykernel");
|
||||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("ipykernel ");
|
||||
CHECK_EQ(ms.version().str(), "=*");
|
||||
CHECK_EQ(ms.name(), "ipykernel");
|
||||
CHECK_EQ(ms.name().str(), "ipykernel");
|
||||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("numpy 1.7*");
|
||||
CHECK_EQ(ms.version().str(), "=1.7");
|
||||
CHECK_EQ(ms.name(), "numpy");
|
||||
CHECK_EQ(ms.name().str(), "numpy");
|
||||
CHECK_EQ(ms.conda_build_form(), "numpy 1.7.*");
|
||||
CHECK_EQ(ms.str(), "numpy=1.7");
|
||||
}
|
||||
|
@ -96,14 +96,14 @@ TEST_SUITE("specs::match_spec")
|
|||
auto ms = MatchSpec::parse("numpy[version='1.7|1.8']");
|
||||
// TODO!
|
||||
// CHECK_EQ(ms.version, "1.7|1.8");
|
||||
CHECK_EQ(ms.name(), "numpy");
|
||||
CHECK_EQ(ms.name().str(), "numpy");
|
||||
CHECK_EQ(ms.brackets["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");
|
||||
CHECK_EQ(ms.version().str(), "==0.12.3");
|
||||
CHECK_EQ(ms.name(), "xtensor");
|
||||
CHECK_EQ(ms.name().str(), "xtensor");
|
||||
REQUIRE(ms.channel().has_value());
|
||||
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
||||
CHECK_EQ(ms.channel()->platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
@ -112,7 +112,7 @@ TEST_SUITE("specs::match_spec")
|
|||
{
|
||||
auto ms = MatchSpec::parse("conda-forge::foo[build=3](target=blarg,optional)");
|
||||
CHECK_EQ(ms.version().str(), "=*");
|
||||
CHECK_EQ(ms.name(), "foo");
|
||||
CHECK_EQ(ms.name().str(), "foo");
|
||||
REQUIRE(ms.channel().has_value());
|
||||
CHECK_EQ(ms.channel()->location(), "conda-forge");
|
||||
CHECK_EQ(ms.brackets["build"], "3");
|
||||
|
@ -121,13 +121,13 @@ TEST_SUITE("specs::match_spec")
|
|||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("python[build_number=3]");
|
||||
CHECK_EQ(ms.name(), "python");
|
||||
CHECK_EQ(ms.name().str(), "python");
|
||||
CHECK_EQ(ms.brackets["build_number"], "3");
|
||||
CHECK_EQ(ms.build_number(), "3");
|
||||
}
|
||||
{
|
||||
auto ms = MatchSpec::parse("python[build_number='<=3']");
|
||||
CHECK_EQ(ms.name(), "python");
|
||||
CHECK_EQ(ms.name().str(), "python");
|
||||
CHECK_EQ(ms.brackets["build_number"], "<=3");
|
||||
CHECK_EQ(ms.build_number(), "<=3");
|
||||
}
|
||||
|
@ -135,9 +135,9 @@ TEST_SUITE("specs::match_spec")
|
|||
auto ms = MatchSpec::parse(
|
||||
"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().str(), "_libgcc_mutex");
|
||||
CHECK_EQ(ms.version().str(), "==0.1");
|
||||
CHECK_EQ(ms.build_string(), "conda_forge");
|
||||
CHECK_EQ(ms.build_string().str(), "conda_forge");
|
||||
CHECK_EQ(
|
||||
ms.url(),
|
||||
"https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
||||
|
@ -148,9 +148,9 @@ TEST_SUITE("specs::match_spec")
|
|||
auto ms = MatchSpec::parse(
|
||||
"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().str(), "libgcc-ng");
|
||||
CHECK_EQ(ms.version().str(), "==11.2.0");
|
||||
CHECK_EQ(ms.build_string(), "h1d223b6_13");
|
||||
CHECK_EQ(ms.build_string().str(), "h1d223b6_13");
|
||||
CHECK_EQ(
|
||||
ms.url(),
|
||||
"https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_13.tar.bz2"
|
||||
|
@ -161,9 +161,9 @@ TEST_SUITE("specs::match_spec")
|
|||
auto ms = MatchSpec::parse(
|
||||
"/home/randomguy/Downloads/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"
|
||||
);
|
||||
CHECK_EQ(ms.name(), "_libgcc_mutex");
|
||||
CHECK_EQ(ms.name().str(), "_libgcc_mutex");
|
||||
CHECK_EQ(ms.version().str(), "==0.1");
|
||||
CHECK_EQ(ms.build_string(), "conda_forge");
|
||||
CHECK_EQ(ms.build_string().str(), "conda_forge");
|
||||
if (util::on_win)
|
||||
{
|
||||
std::string driveletter = fs::absolute(fs::u8path("/")).string().substr(0, 1);
|
||||
|
@ -190,7 +190,7 @@ TEST_SUITE("specs::match_spec")
|
|||
auto ms = MatchSpec::parse(
|
||||
"xtensor[url=file:///home/wolfv/Downloads/xtensor-0.21.4-hc9558a2_0.tar.bz2]"
|
||||
);
|
||||
CHECK_EQ(ms.name(), "xtensor");
|
||||
CHECK_EQ(ms.name().str(), "xtensor");
|
||||
CHECK_EQ(
|
||||
ms.brackets["url"],
|
||||
"file:///home/wolfv/Downloads/xtensor-0.21.4-hc9558a2_0.tar.bz2"
|
||||
|
|
|
@ -666,4 +666,4 @@ def test_MatchSpec():
|
|||
ms = MatchSpec.parse("conda-forge::python=3.7=*pypy")
|
||||
|
||||
# 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