From 15e805602953fcec839dbb04aa264bf21d05710b Mon Sep 17 00:00:00 2001 From: Antoine Prouvost Date: Tue, 26 Dec 2023 17:23:33 +0100 Subject: [PATCH] Some future proofing MatchSpec (#3082) * Private MatchSpec::name * Private MatchSpec::filename * Private MatchSpec::url * Make MatchSpec::is_file a function * Private MatchSpec::namespace * Private MatchSpec::build_number * Remove dead affectation * Private MatchSpec::optional * Remove dead code * Clean MatchSpec dist parsing * Fix MatchSpec dist parse * Private MatchSpec::version * Private MatchSpec::build_string * Private MatchSpec::channel * Add missing MatchSpec setters * Put MatchSpec in specs:: --- libmamba/CMakeLists.txt | 4 +- libmamba/include/mamba/api/install.hpp | 4 +- libmamba/include/mamba/core/history.hpp | 6 +- libmamba/include/mamba/core/link.hpp | 4 +- libmamba/include/mamba/core/match_spec.hpp | 48 --- libmamba/include/mamba/core/pool.hpp | 8 +- .../mamba/core/satisfiability_error.hpp | 12 +- libmamba/include/mamba/core/solver.hpp | 21 +- libmamba/include/mamba/core/transaction.hpp | 8 +- .../mamba/core/transaction_context.hpp | 13 +- libmamba/include/mamba/specs/match_spec.hpp | 81 +++++ libmamba/src/api/install.cpp | 30 +- libmamba/src/api/remove.cpp | 4 +- libmamba/src/api/update.cpp | 10 +- libmamba/src/core/env_lockfile.cpp | 25 +- libmamba/src/core/history.cpp | 14 +- libmamba/src/core/link.cpp | 6 +- libmamba/src/core/pinning.cpp | 3 +- libmamba/src/core/pool.cpp | 18 +- libmamba/src/core/prefix_data.cpp | 4 +- libmamba/src/core/satisfiability_error.cpp | 4 +- libmamba/src/core/solver.cpp | 69 ++-- libmamba/src/core/transaction.cpp | 43 +-- libmamba/src/core/transaction_context.cpp | 4 +- libmamba/src/{core => specs}/match_spec.cpp | 314 +++++++++++------- libmamba/tests/CMakeLists.txt | 2 +- .../src/{core => specs}/test_match_spec.cpp | 96 +++--- libmambapy/src/libmambapy/bindings/legacy.cpp | 23 +- libmambapy/src/libmambapy/bindings/specs.cpp | 20 ++ libmambapy/tests/test_specs.py | 9 + micromamba/src/update.cpp | 7 +- micromamba/tests/test_install.py | 2 +- 32 files changed, 534 insertions(+), 382 deletions(-) delete mode 100644 libmamba/include/mamba/core/match_spec.hpp create mode 100644 libmamba/include/mamba/specs/match_spec.hpp rename libmamba/src/{core => specs}/match_spec.cpp (63%) rename libmamba/tests/src/{core => specs}/test_match_spec.cpp (75%) diff --git a/libmamba/CMakeLists.txt b/libmamba/CMakeLists.txt index 700f1eb427..cc3f85c80d 100644 --- a/libmamba/CMakeLists.txt +++ b/libmamba/CMakeLists.txt @@ -176,6 +176,7 @@ set( ${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 @@ -200,7 +201,6 @@ set( ${LIBMAMBA_SOURCE_DIR}/core/transaction_context.cpp ${LIBMAMBA_SOURCE_DIR}/core/link.cpp ${LIBMAMBA_SOURCE_DIR}/core/history.cpp - ${LIBMAMBA_SOURCE_DIR}/core/match_spec.cpp ${LIBMAMBA_SOURCE_DIR}/core/menuinst.cpp ${LIBMAMBA_SOURCE_DIR}/core/mirror.cpp ${LIBMAMBA_SOURCE_DIR}/core/output.cpp @@ -288,6 +288,7 @@ set( ${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 @@ -308,7 +309,6 @@ set( ${LIBMAMBA_INCLUDE_DIR}/mamba/core/satisfiability_error.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/core/history.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/core/link.hpp - ${LIBMAMBA_INCLUDE_DIR}/mamba/core/match_spec.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/core/menuinst.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/core/mirror.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/core/output.hpp diff --git a/libmamba/include/mamba/api/install.hpp b/libmamba/include/mamba/api/install.hpp index 69d4f86d07..4d6e2a6ff0 100644 --- a/libmamba/include/mamba/api/install.hpp +++ b/libmamba/include/mamba/api/install.hpp @@ -14,12 +14,12 @@ #include #include -#include "mamba/core/match_spec.hpp" #include "mamba/core/package_cache.hpp" #include "mamba/core/package_info.hpp" #include "mamba/core/pool.hpp" #include "mamba/core/repo.hpp" #include "mamba/fs/filesystem.hpp" +#include "mamba/specs/match_spec.hpp" namespace mamba { @@ -88,7 +88,7 @@ namespace mamba yaml_file_contents read_yaml_file(fs::u8path yaml_file, const std::string platform); - std::tuple, std::vector> + std::tuple, std::vector> parse_urls_to_package_info(const std::vector& urls); inline void to_json(nlohmann::json&, const other_pkg_mgr_spec&) diff --git a/libmamba/include/mamba/core/history.hpp b/libmamba/include/mamba/core/history.hpp index b205a9769b..e786671281 100644 --- a/libmamba/include/mamba/core/history.hpp +++ b/libmamba/include/mamba/core/history.hpp @@ -12,9 +12,9 @@ #include #include +#include "mamba/core/channel_context.hpp" #include "mamba/fs/filesystem.hpp" - -#include "match_spec.hpp" +#include "mamba/specs/match_spec.hpp" namespace mamba { @@ -52,7 +52,7 @@ namespace mamba std::vector parse(); bool parse_comment_line(const std::string& line, UserRequest& req); std::vector get_user_requests(); - std::unordered_map get_requested_specs_map(); + std::unordered_map get_requested_specs_map(); void add_entry(const History::UserRequest& entry); fs::u8path m_prefix; diff --git a/libmamba/include/mamba/core/link.hpp b/libmamba/include/mamba/core/link.hpp index 201ca0f440..0d8b8c8486 100644 --- a/libmamba/include/mamba/core/link.hpp +++ b/libmamba/include/mamba/core/link.hpp @@ -8,17 +8,15 @@ #define MAMBA_CORE_LINK #include -#include #include #include #include +#include "mamba/core/package_info.hpp" #include "mamba/fs/filesystem.hpp" #include "mamba/util/build.hpp" -#include "match_spec.hpp" #include "package_paths.hpp" -#include "transaction.hpp" #include "transaction_context.hpp" namespace mamba diff --git a/libmamba/include/mamba/core/match_spec.hpp b/libmamba/include/mamba/core/match_spec.hpp deleted file mode 100644 index ba5f3607cd..0000000000 --- a/libmamba/include/mamba/core/match_spec.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2019, 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_CORE_MATCH_SPEC -#define MAMBA_CORE_MATCH_SPEC - -#include -#include -#include -#include -#include - -#include "mamba/specs/channel_spec.hpp" - -namespace mamba -{ - class MatchSpec - { - public: - - [[nodiscard]] static auto parse_version_and_build(std::string_view s) - -> std::tuple; - [[nodiscard]] static auto parse(std::string_view spec) -> MatchSpec; - - [[nodiscard]] auto conda_build_form() const -> std::string; - [[nodiscard]] auto str() const -> std::string; - - [[nodiscard]] auto is_simple() const -> bool; - - std::optional channel; - std::string name; - std::string version; - std::string ns; - std::string build_string; - std::string fn; - std::string url; - std::string build_number; - - bool is_file = false; - bool optional = false; - std::unordered_map brackets; - std::unordered_map parens; - }; -} -#endif diff --git a/libmamba/include/mamba/core/pool.hpp b/libmamba/include/mamba/core/pool.hpp index bcc4247b51..4b5ba0eaa9 100644 --- a/libmamba/include/mamba/core/pool.hpp +++ b/libmamba/include/mamba/core/pool.hpp @@ -16,7 +16,6 @@ namespace mamba { - class MatchSpec; class ChannelContext; class Context; @@ -25,6 +24,11 @@ namespace mamba class ObjPool; } + namespace specs + { + class MatchSpec; + } + /** * Pool of solvable involved in resolving en environment. * @@ -44,7 +48,7 @@ namespace mamba void create_whatprovides(); std::vector select_solvables(Id id, bool sorted = false) const; - Id matchspec2id(const MatchSpec& ms); + Id matchspec2id(const specs::MatchSpec& ms); std::optional id2pkginfo(Id solv_id) const; std::optional dep2str(Id dep_id) const; diff --git a/libmamba/include/mamba/core/satisfiability_error.hpp b/libmamba/include/mamba/core/satisfiability_error.hpp index 728e02c000..975a4e21c3 100644 --- a/libmamba/include/mamba/core/satisfiability_error.hpp +++ b/libmamba/include/mamba/core/satisfiability_error.hpp @@ -10,19 +10,17 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include -#include "mamba/core/match_spec.hpp" #include "mamba/core/package_info.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/flat_set.hpp" #include "mamba/util/graph.hpp" @@ -81,17 +79,17 @@ namespace mamba { }; - struct UnresolvedDependencyNode : MatchSpec + struct UnresolvedDependencyNode : specs::MatchSpec { }; - struct ConstraintNode : MatchSpec + struct ConstraintNode : specs::MatchSpec { }; using node_t = std::variant; - using edge_t = MatchSpec; + using edge_t = specs::MatchSpec; using graph_t = util::DiGraph; using node_id = graph_t::node_id; @@ -213,7 +211,7 @@ namespace mamba UnresolvedDependencyListNode, ConstraintListNode>; - using edge_t = NamedList; + using edge_t = NamedList; using graph_t = util::DiGraph; using node_id = graph_t::node_id; diff --git a/libmamba/include/mamba/core/solver.hpp b/libmamba/include/mamba/core/solver.hpp index 103bec719b..155c423c9a 100644 --- a/libmamba/include/mamba/core/solver.hpp +++ b/libmamba/include/mamba/core/solver.hpp @@ -22,8 +22,7 @@ #include "mamba/core/package_info.hpp" #include "mamba/core/pool.hpp" #include "mamba/core/satisfiability_error.hpp" - -#include "match_spec.hpp" +#include "mamba/specs/match_spec.hpp" #define PY_MAMBA_NO_DEPS 0b0001 #define PY_MAMBA_ONLY_DEPS 0b0010 @@ -105,10 +104,10 @@ namespace mamba [[nodiscard]] MPool& pool() &; [[nodiscard]] MPool&& pool() &&; - [[nodiscard]] const std::vector& install_specs() const; - [[nodiscard]] const std::vector& remove_specs() const; - [[nodiscard]] const std::vector& neuter_specs() const; - [[nodiscard]] const std::vector& pinned_specs() const; + [[nodiscard]] const std::vector& install_specs() const; + [[nodiscard]] const std::vector& remove_specs() const; + [[nodiscard]] const std::vector& neuter_specs() const; + [[nodiscard]] const std::vector& pinned_specs() const; operator const Solver*() const; operator Solver*(); @@ -118,10 +117,10 @@ namespace mamba private: std::vector> m_libsolv_flags; - std::vector m_install_specs; - std::vector m_remove_specs; - std::vector m_neuter_specs; - std::vector m_pinned_specs; + std::vector m_install_specs; + std::vector m_remove_specs; + std::vector m_neuter_specs; + std::vector m_pinned_specs; // Order of m_pool and m_solver is critical since m_pool must outlive m_solver. MPool m_pool; // Temporary Pimpl all libsolv to keep it private @@ -130,7 +129,7 @@ namespace mamba Flags m_flags = {}; bool m_is_solved; - void add_reinstall_job(MatchSpec& ms, int job_flag); + void add_reinstall_job(const specs::MatchSpec& ms, int job_flag); void apply_libsolv_flags(); }; } // namespace mamba diff --git a/libmamba/include/mamba/core/transaction.hpp b/libmamba/include/mamba/core/transaction.hpp index 0aee7a207e..519d4d40db 100644 --- a/libmamba/include/mamba/core/transaction.hpp +++ b/libmamba/include/mamba/core/transaction.hpp @@ -13,8 +13,8 @@ #include "mamba/api/install.hpp" #include "mamba/fs/filesystem.hpp" +#include "mamba/specs/match_spec.hpp" -#include "match_spec.hpp" #include "package_cache.hpp" #include "package_info.hpp" #include "pool.hpp" @@ -33,8 +33,8 @@ namespace mamba MTransaction( MPool& pool, - const std::vector& specs_to_remove, - const std::vector& specs_to_install, + const std::vector& specs_to_remove, + const std::vector& specs_to_install, MultiPackageCache& caches ); MTransaction(MPool& pool, MSolver& solver, MultiPackageCache& caches); @@ -72,7 +72,7 @@ namespace mamba History::UserRequest m_history_entry; - std::vector m_requested_specs; + std::vector m_requested_specs; MTransaction(MPool&, MultiPackageCache&); }; diff --git a/libmamba/include/mamba/core/transaction_context.hpp b/libmamba/include/mamba/core/transaction_context.hpp index 66891d5891..734b4cb306 100644 --- a/libmamba/include/mamba/core/transaction_context.hpp +++ b/libmamba/include/mamba/core/transaction_context.hpp @@ -11,11 +11,10 @@ #include +#include "mamba/core/context.hpp" +#include "mamba/core/util.hpp" #include "mamba/fs/filesystem.hpp" - -#include "context.hpp" -#include "match_spec.hpp" -#include "util.hpp" +#include "mamba/specs/match_spec.hpp" namespace mamba { @@ -42,7 +41,7 @@ namespace mamba const Context& context, const fs::u8path& target_prefix, const std::pair& py_versions, - const std::vector& requested_specs + const std::vector& requested_specs ); TransactionContext( @@ -50,7 +49,7 @@ namespace mamba const fs::u8path& target_prefix, const fs::u8path& relocate_prefix, const std::pair& py_versions, - const std::vector& requested_specs + const std::vector& requested_specs ); ~TransactionContext(); bool try_pyc_compilation(const std::vector& py_files); @@ -70,7 +69,7 @@ namespace mamba bool compile_pyc = true; // this needs to be done when python version changes bool relink_noarch = false; - std::vector requested_specs; + std::vector requested_specs; const Context& context() const { diff --git a/libmamba/include/mamba/specs/match_spec.hpp b/libmamba/include/mamba/specs/match_spec.hpp new file mode 100644 index 0000000000..a7269d162d --- /dev/null +++ b/libmamba/include/mamba/specs/match_spec.hpp @@ -0,0 +1,81 @@ +// Copyright (c) 2019, 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_CORE_MATCH_SPEC +#define MAMBA_CORE_MATCH_SPEC + +#include +#include +#include +#include +#include + +#include "mamba/specs/channel_spec.hpp" + +namespace mamba::specs +{ + class MatchSpec + { + public: + + [[nodiscard]] static auto parse_version_and_build(std::string_view s) + -> std::tuple; + + [[nodiscard]] static auto parse(std::string_view spec) -> MatchSpec; + + [[nodiscard]] static auto parse_url(std::string_view spec) -> MatchSpec; + + [[nodiscard]] auto channel() const -> const std::optional&; + void set_channel(std::optional chan); + + [[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 version() const -> const std::string&; + void set_version(std::string ver); + + [[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 optional() const -> bool; + void set_optional(bool opt); + + [[nodiscard]] auto filename() const -> const std::string&; + + [[nodiscard]] auto url() const -> const std::string&; + + [[nodiscard]] auto conda_build_form() const -> std::string; + [[nodiscard]] auto str() const -> std::string; + + [[nodiscard]] auto is_simple() const -> bool; + + [[nodiscard]] auto is_file() const -> bool; + + std::unordered_map brackets; + + std::unordered_map parens; + + private: + + std::optional m_channel; + std::string m_name_space; + std::string m_name; + std::string m_version; + std::string m_build_number; + std::string m_build_string; + // TODO can put inside channel spec + std::string m_filename; + std::string m_url; + bool m_optional = false; + }; +} +#endif diff --git a/libmamba/src/api/install.cpp b/libmamba/src/api/install.cpp index 4d96336528..4e97e0e40c 100644 --- a/libmamba/src/api/install.cpp +++ b/libmamba/src/api/install.cpp @@ -342,11 +342,11 @@ namespace mamba return result; } - std::tuple, std::vector> + std::tuple, std::vector> parse_urls_to_package_info(const std::vector& urls) { std::vector pi_result; - std::vector ms_result; + std::vector ms_result; for (auto& u : urls) { if (util::strip(u).size() == 0) @@ -354,22 +354,22 @@ namespace mamba continue; } std::size_t hash = u.find_first_of('#'); - auto ms = MatchSpec::parse(u.substr(0, hash)); - PackageInfo p(ms.name); - p.url = ms.url; - p.build_string = ms.build_string; - p.version = ms.version; - if (ms.channel.has_value()) + auto ms = specs::MatchSpec::parse(u.substr(0, hash)); + PackageInfo p(ms.name()); + p.url = ms.url(); + p.build_string = ms.build_string(); + p.version = ms.version(); + if (ms.channel().has_value()) { - p.channel = ms.channel->location(); - if (!ms.channel->platform_filters().empty()) + p.channel = ms.channel()->location(); + if (!ms.channel()->platform_filters().empty()) { // There must be only one since we are expecting URLs - assert(ms.channel->platform_filters().size() == 1); - p.subdir = ms.channel->platform_filters().front(); + assert(ms.channel()->platform_filters().size() == 1); + p.subdir = ms.channel()->platform_filters().front(); } } - p.fn = ms.fn; + p.fn = ms.filename(); if (hash != std::string::npos) { @@ -476,9 +476,9 @@ namespace mamba // add channels from specs for (const auto& s : specs) { - if (auto ms = MatchSpec::parse(s); ms.channel.has_value()) + if (auto ms = specs::MatchSpec::parse(s); ms.channel().has_value()) { - ctx.channels.push_back(ms.channel->str()); + ctx.channels.push_back(ms.channel()->str()); } } diff --git a/libmamba/src/api/remove.cpp b/libmamba/src/api/remove.cpp index 94a99fc040..1fd9c02a81 100644 --- a/libmamba/src/api/remove.cpp +++ b/libmamba/src/api/remove.cpp @@ -107,13 +107,13 @@ namespace mamba if (force) { - std::vector mspecs; + std::vector mspecs; mspecs.reserve(specs.size()); std::transform( specs.begin(), specs.end(), std::back_inserter(mspecs), - [&](const auto& spec_str) { return MatchSpec::parse(spec_str); } + [&](const auto& spec_str) { return specs::MatchSpec::parse(spec_str); } ); auto transaction = MTransaction(pool, mspecs, {}, package_caches); execute_transaction(transaction); diff --git a/libmamba/src/api/update.cpp b/libmamba/src/api/update.cpp index 263a56ed35..e4397515d5 100644 --- a/libmamba/src/api/update.cpp +++ b/libmamba/src/api/update.cpp @@ -38,9 +38,9 @@ namespace mamba // add channels from specs for (const auto& s : update_specs) { - if (auto m = MatchSpec::parse(s); m.channel.has_value()) + if (auto m = specs::MatchSpec::parse(s); m.channel().has_value()) { - ctx.channels.push_back(m.channel->str()); + ctx.channels.push_back(m.channel()->str()); } } @@ -118,7 +118,7 @@ namespace mamba std::vector keep_specs; for (auto& it : hist_map) { - keep_specs.push_back(it.second.name); + keep_specs.push_back(it.second.name()); } solver_flag |= SOLVER_SOLVABLE_ALL; if (prune_deps) @@ -136,10 +136,10 @@ namespace mamba std::vector 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()) == update_specs.end()) { - remove_specs.push_back(it.second.name); + remove_specs.push_back(it.second.name()); } } solver.add_jobs(remove_specs, SOLVER_ERASE | SOLVER_CLEANDEPS); diff --git a/libmamba/src/core/env_lockfile.cpp b/libmamba/src/core/env_lockfile.cpp index 68588a78b9..251b2a0893 100644 --- a/libmamba/src/core/env_lockfile.cpp +++ b/libmamba/src/core/env_lockfile.cpp @@ -8,8 +8,8 @@ #include #include "mamba/core/env_lockfile.hpp" -#include "mamba/core/match_spec.hpp" #include "mamba/fs/filesystem.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/string.hpp" namespace mamba @@ -56,17 +56,17 @@ namespace mamba } package.info.url = package_node["url"].as(); - const auto spec = MatchSpec::parse(package.info.url); - package.info.fn = spec.fn; - package.info.build_string = spec.build_string; - if (spec.channel.has_value()) + const auto spec = specs::MatchSpec::parse(package.info.url); + package.info.fn = spec.filename(); + package.info.build_string = spec.build_string(); + if (spec.channel().has_value()) { - package.info.channel = spec.channel->location(); - if (!spec.channel->platform_filters().empty()) + package.info.channel = spec.channel()->location(); + if (!spec.channel()->platform_filters().empty()) { // There must be only one since we are expecting URLs - assert(spec.channel->platform_filters().size() == 1); - package.info.subdir = spec.channel->platform_filters().front(); + assert(spec.channel()->platform_filters().size() == 1); + package.info.subdir = spec.channel()->platform_filters().front(); } } @@ -219,13 +219,14 @@ namespace mamba std::type_index{ typeid(err) } )); } - catch (...) + catch (const std::exception& e) { return tl::unexpected(EnvLockFileError::make_error( file_parsing_error_code::parsing_failure, fmt::format( - "unknown error while reading environment lockfile located at '{}'", - file_path.string() + "Error while reading environment lockfile located at '{}': {}", + file_path.string(), + e.what() ) )); } diff --git a/libmamba/src/core/history.cpp b/libmamba/src/core/history.cpp index a5713921ae..f9fc42f51b 100644 --- a/libmamba/src/core/history.cpp +++ b/libmamba/src/core/history.cpp @@ -191,17 +191,17 @@ namespace mamba return res; } - std::unordered_map History::get_requested_specs_map() + std::unordered_map History::get_requested_specs_map() { - std::unordered_map map; + std::unordered_map map; auto to_specs = [&](const std::vector& sv) { - std::vector v; + std::vector v; v.reserve(sv.size()); for (const auto& el : sv) { - v.emplace_back(MatchSpec::parse(el)); + v.emplace_back(specs::MatchSpec::parse(el)); } return v; }; @@ -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()); } auto update_specs = to_specs(request.update); for (auto& spec : update_specs) { - map[spec.name] = spec; + map[spec.name()] = spec; } auto neutered_specs = to_specs(request.neutered); for (auto& spec : neutered_specs) { - map[spec.name] = spec; + map[spec.name()] = spec; } } diff --git a/libmamba/src/core/link.cpp b/libmamba/src/core/link.cpp index 9f021b6e1c..ffb6207003 100644 --- a/libmamba/src/core/link.cpp +++ b/libmamba/src/core/link.cpp @@ -14,10 +14,10 @@ #include #include "mamba/core/link.hpp" -#include "mamba/core/match_spec.hpp" #include "mamba/core/menuinst.hpp" #include "mamba/core/output.hpp" #include "mamba/core/transaction_context.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/build.hpp" #include "mamba/util/environment.hpp" #include "mamba/util/string.hpp" @@ -993,10 +993,10 @@ namespace mamba out_json["paths_data"] = paths_json; out_json["files"] = files_record; - MatchSpec* requested_spec = nullptr; + specs::MatchSpec* requested_spec = nullptr; for (auto& ms : m_context->requested_specs) { - if (ms.name == m_pkg_info.name) + if (ms.name() == m_pkg_info.name) { requested_spec = &ms; } diff --git a/libmamba/src/core/pinning.cpp b/libmamba/src/core/pinning.cpp index fe71340a92..adaf2fa7ee 100644 --- a/libmamba/src/core/pinning.cpp +++ b/libmamba/src/core/pinning.cpp @@ -9,6 +9,7 @@ #include "mamba/core/output.hpp" #include "mamba/core/pinning.hpp" #include "mamba/core/prefix_data.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/string.hpp" namespace mamba @@ -30,7 +31,7 @@ namespace mamba for (const auto& spec : specs) { - if (MatchSpec::parse(spec).name == "python") + if (specs::MatchSpec::parse(spec).name() == "python") { return ""; } diff --git a/libmamba/src/core/pool.cpp b/libmamba/src/core/pool.cpp index c98b149b06..b14e0ddba4 100644 --- a/libmamba/src/core/pool.cpp +++ b/libmamba/src/core/pool.cpp @@ -19,9 +19,9 @@ extern "C" // Incomplete header #include "mamba/core/channel_context.hpp" #include "mamba/core/context.hpp" -#include "mamba/core/match_spec.hpp" #include "mamba/core/output.hpp" #include "mamba/core/pool.hpp" +#include "mamba/specs/match_spec.hpp" #include "solv-cpp/pool.hpp" #include "solv-cpp/queue.hpp" @@ -179,12 +179,12 @@ namespace mamba auto add_channel_specific_matchspec( ChannelContext& channel_context, solv::ObjPool& pool, - const MatchSpec& ms + const specs::MatchSpec& ms ) -> solv::DependencyId { - assert(ms.channel.has_value()); + assert(ms.channel().has_value()); // Poor man's ms repr to match waht the user provided - const std::string repr = fmt::format("{}::{}", *ms.channel, ms.conda_build_form()); + const std::string repr = fmt::format("{}::{}", *ms.channel(), ms.conda_build_form()); // Already added, return that id if (const auto maybe_id = pool.find_string(repr)) @@ -198,7 +198,7 @@ namespace mamba ms.conda_build_form().c_str() ); - auto ms_channel = channel_context.make_channel(*ms.channel); + auto ms_channel = channel_context.make_channel(*ms.channel()); solv::ObjQueue selected_pkgs = {}; auto other_subdir_match = std::string(); @@ -206,7 +206,7 @@ namespace mamba match, [&](solv::ObjSolvableViewConst s) { - assert(ms.channel.has_value()); + assert(ms.channel().has_value()); // TODO this does not work with s.url(), we need to proper channel class // to properly manage this. auto repo = solv::ObjRepoView(*s.raw()->repo); @@ -237,7 +237,7 @@ namespace mamba { if (!other_subdir_match.empty()) { - const auto& filters = ms.channel->platform_filters(); + const auto& filters = ms.channel()->platform_filters(); throw std::runtime_error(fmt::format( R"(The package "{}" is not available for the specified platform{} ({}))" R"( but is available on {}.)", @@ -266,10 +266,10 @@ namespace mamba } } - ::Id MPool::matchspec2id(const MatchSpec& ms) + ::Id MPool::matchspec2id(const specs::MatchSpec& ms) { ::Id id = 0; - if (!ms.channel.has_value()) + if (!ms.channel().has_value()) { id = pool_conda_matchspec(pool().raw(), ms.conda_build_form().c_str()); } diff --git a/libmamba/src/core/prefix_data.cpp b/libmamba/src/core/prefix_data.cpp index ed54b488a2..a790006fd2 100644 --- a/libmamba/src/core/prefix_data.cpp +++ b/libmamba/src/core/prefix_data.cpp @@ -101,10 +101,10 @@ namespace mamba for (const auto& dep : record->depends) { // Creating a matchspec to parse the name (there may be a channel) - auto ms = MatchSpec::parse(dep); + 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()); if (from_iter != name_to_node_id.cend()) { dep_graph.add_edge(from_iter->second, to_id); diff --git a/libmamba/src/core/satisfiability_error.cpp b/libmamba/src/core/satisfiability_error.cpp index aa1d4f642d..ddbbf2cd5d 100644 --- a/libmamba/src/core/satisfiability_error.cpp +++ b/libmamba/src/core/satisfiability_error.cpp @@ -556,7 +556,7 @@ namespace mamba template struct CompressedProblemsGraph::RoughCompare; template struct CompressedProblemsGraph::RoughCompare; template struct CompressedProblemsGraph::RoughCompare; - template struct CompressedProblemsGraph::RoughCompare; + template struct CompressedProblemsGraph::RoughCompare; /********************************************************** * Implementation of CompressedProblemsGraph::NamedList * @@ -746,7 +746,7 @@ namespace mamba template class CompressedProblemsGraph::NamedList; template class CompressedProblemsGraph::NamedList; template class CompressedProblemsGraph::NamedList; - template class CompressedProblemsGraph::NamedList; + template class CompressedProblemsGraph::NamedList; /*********************************** * Implementation of summary_msg * diff --git a/libmamba/src/core/solver.cpp b/libmamba/src/core/solver.cpp index e4e3d0aa58..1b83247e80 100644 --- a/libmamba/src/core/solver.cpp +++ b/libmamba/src/core/solver.cpp @@ -16,12 +16,12 @@ #include "mamba/core/channel_context.hpp" #include "mamba/core/context.hpp" #include "mamba/core/error_handling.hpp" -#include "mamba/core/match_spec.hpp" #include "mamba/core/output.hpp" #include "mamba/core/package_info.hpp" #include "mamba/core/pool.hpp" #include "mamba/core/satisfiability_error.hpp" #include "mamba/core/solver.hpp" +#include "mamba/specs/match_spec.hpp" #include "solv-cpp/pool.hpp" #include "solv-cpp/queue.hpp" #include "solv-cpp/solver.hpp" @@ -70,7 +70,7 @@ namespace mamba m_jobs->push_back(job_flag, 0); } - void MSolver::add_reinstall_job(MatchSpec& ms, int job_flag) + void MSolver::add_reinstall_job(const specs::MatchSpec& ms, int job_flag) { auto solvable = std::optional{}; @@ -78,7 +78,7 @@ namespace mamba m_pool.pool().for_each_installed_solvable( [&](solv::ObjSolvableViewConst s) { - if (s.name() == ms.name) + if (s.name() == ms.name()) { solvable = s; return solv::LoopControl::Break; @@ -96,34 +96,30 @@ namespace mamba 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().empty() || !ms.build_string().empty()) { Console::stream() << ms.conda_build_form() << ": overriding channel, version and build from " "installed packages due to --force-reinstall."; - ms.channel = std::nullopt; - ms.version = ""; - ms.build_string = ""; } - MatchSpec modified_spec(ms); - modified_spec.channel = specs::ChannelSpec::parse(solvable->channel()); + auto ms_modified = ms; + ms_modified.set_channel(specs::ChannelSpec::parse(solvable->channel())); + ms_modified.set_version(std::string(solvable->version())); + ms_modified.set_build_string(std::string(solvable->build_string())); - modified_spec.version = solvable->version(); - modified_spec.build_string = solvable->build_string(); - - LOG_INFO << "Reinstall " << modified_spec.conda_build_form() << " from channel " - << modified_spec.channel->str(); + LOG_INFO << "Reinstall " << ms_modified.conda_build_form() << " from channel " + << ms_modified.channel()->str(); // TODO Fragile! The only reason why this works is that with a channel specific matchspec // the job will always be reinstalled. - m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id(modified_spec)); + m_jobs->push_back(job_flag | SOLVER_SOLVABLE_PROVIDES, m_pool.matchspec2id(ms_modified)); } void MSolver::add_jobs(const std::vector& jobs, int job_flag) { for (const auto& job : jobs) { - auto ms = MatchSpec::parse(job); + auto ms = specs::MatchSpec::parse(job); int job_type = job_flag & SOLVER_JOBMASK; if (ms.conda_build_form().empty()) @@ -133,15 +129,16 @@ namespace mamba if (job_type & SOLVER_INSTALL) { - m_install_specs.emplace_back(MatchSpec::parse(job)); + m_install_specs.emplace_back(specs::MatchSpec::parse(job)); } else if (job_type == SOLVER_ERASE) { - m_remove_specs.emplace_back(MatchSpec::parse(job)); + m_remove_specs.emplace_back(specs::MatchSpec::parse(job)); } else if (job_type == SOLVER_LOCK) { - m_neuter_specs.emplace_back(MatchSpec::parse(job)); // not used for the moment + m_neuter_specs.emplace_back(specs::MatchSpec::parse(job)); // not used for the + // moment } const ::Id job_id = m_pool.matchspec2id(ms); @@ -173,7 +170,7 @@ namespace mamba { m_jobs->push_back( SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, - m_pool.matchspec2id(MatchSpec::parse(job)) + m_pool.matchspec2id(specs::MatchSpec::parse(job)) ); } @@ -205,7 +202,7 @@ namespace mamba // as one of its constrains. // Then we lock this solvable and force the re-checking of its dependencies. - const auto pin_ms = MatchSpec::parse(pin); + const auto pin_ms = specs::MatchSpec::parse(pin); m_pinned_specs.push_back(pin_ms); auto& pool = m_pool.pool(); @@ -317,22 +314,22 @@ namespace mamba return std::move(m_pool); } - const std::vector& MSolver::install_specs() const + const std::vector& MSolver::install_specs() const { return m_install_specs; } - const std::vector& MSolver::remove_specs() const + const std::vector& MSolver::remove_specs() const { return m_remove_specs; } - const std::vector& MSolver::neuter_specs() const + const std::vector& MSolver::neuter_specs() const { return m_neuter_specs; } - const std::vector& MSolver::pinned_specs() const + const std::vector& MSolver::pinned_specs() const { return m_pinned_specs; } @@ -616,9 +613,9 @@ namespace mamba ); node_id cons_id = add_solvable( problem.dep_id, - ConstraintNode{ MatchSpec::parse(dep.value()) } + ConstraintNode{ specs::MatchSpec::parse(dep.value()) } ); - MatchSpec edge(MatchSpec::parse(dep.value())); + auto edge = specs::MatchSpec::parse(dep.value()); m_graph.add_edge(src_id, cons_id, std::move(edge)); add_conflict(cons_id, tgt_id); break; @@ -638,7 +635,7 @@ namespace mamba problem.source_id, PackageNode{ std::move(source).value() } ); - MatchSpec edge(MatchSpec::parse(dep.value())); + auto edge = specs::MatchSpec::parse(dep.value()); bool added = add_expanded_deps_edges(src_id, problem.dep_id, edge); if (!added) { @@ -657,7 +654,7 @@ namespace mamba warn_unexpected_problem(problem); break; } - MatchSpec edge(MatchSpec::parse(dep.value())); + auto edge = specs::MatchSpec::parse(dep.value()); bool added = add_expanded_deps_edges(m_root_node, problem.dep_id, edge); if (!added) { @@ -676,10 +673,10 @@ namespace mamba warn_unexpected_problem(problem); break; } - MatchSpec edge(MatchSpec::parse(dep.value())); + auto edge = specs::MatchSpec::parse(dep.value()); node_id dep_id = add_solvable( problem.dep_id, - UnresolvedDependencyNode{ MatchSpec::parse(dep.value()) } + UnresolvedDependencyNode{ specs::MatchSpec::parse(dep.value()) } ); m_graph.add_edge(m_root_node, dep_id, std::move(edge)); break; @@ -695,14 +692,14 @@ namespace mamba warn_unexpected_problem(problem); break; } - MatchSpec edge(MatchSpec::parse(dep.value())); + auto edge = specs::MatchSpec::parse(dep.value()); node_id src_id = add_solvable( problem.source_id, PackageNode{ std::move(source).value() } ); node_id dep_id = add_solvable( problem.dep_id, - UnresolvedDependencyNode{ MatchSpec::parse(dep.value()) } + UnresolvedDependencyNode{ specs::MatchSpec::parse(dep.value()) } ); m_graph.add_edge(src_id, dep_id, std::move(edge)); break; @@ -745,10 +742,10 @@ namespace mamba // how the solver is handling this package, as this is resolved in term of // installed packages and solver flags (allow downgrade...) rather than a // dependency. - MatchSpec edge(MatchSpec::parse(source.value().name)); + 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()).has_value()); + const auto dep_id = m_pool.pool().find_string(edge.name()).value(); const bool added = add_expanded_deps_edges(m_root_node, dep_id, edge); if (!added) { diff --git a/libmamba/src/core/transaction.cpp b/libmamba/src/core/transaction.cpp index 59553fcf04..65d426607a 100644 --- a/libmamba/src/core/transaction.cpp +++ b/libmamba/src/core/transaction.cpp @@ -27,13 +27,13 @@ extern "C" // Incomplete header #include "mamba/core/env_lockfile.hpp" #include "mamba/core/execution.hpp" #include "mamba/core/link.hpp" -#include "mamba/core/match_spec.hpp" #include "mamba/core/output.hpp" #include "mamba/core/package_fetcher.hpp" #include "mamba/core/pool.hpp" #include "mamba/core/solver.hpp" #include "mamba/core/thread_utils.hpp" #include "mamba/core/transaction.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/flat_set.hpp" #include "mamba/util/string.hpp" #include "solv-cpp/pool.hpp" @@ -69,22 +69,22 @@ namespace mamba for (auto& ms : specs) { - out.emplace_back(ms.name); + out.emplace_back(ms.name()); auto& p = out.back(); - p.url = ms.url; - p.build_string = ms.build_string; - p.version = ms.version; - if (ms.channel.has_value()) + p.url = ms.url(); + p.build_string = ms.build_string(); + p.version = ms.version(); + if (ms.channel().has_value()) { - p.channel = ms.channel->location(); - if (!ms.channel->platform_filters().empty()) + p.channel = ms.channel()->location(); + if (!ms.channel()->platform_filters().empty()) { // There must be only one since we are expecting URLs - assert(ms.channel->platform_filters().size() == 1); - p.subdir = ms.channel->platform_filters().front(); + assert(ms.channel()->platform_filters().size() == 1); + p.subdir = ms.channel()->platform_filters().front(); } } - p.fn = ms.fn; + p.fn = ms.filename(); if (ms.brackets.find("md5") != ms.brackets.end()) { p.md5 = ms.brackets.at("md5"); @@ -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(); } ); 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(); } ); auto specs = util::flat_set{}; @@ -298,8 +298,8 @@ namespace mamba MTransaction::MTransaction( MPool& pool, - const std::vector& specs_to_remove, - const std::vector& specs_to_install, + const std::vector& specs_to_remove, + const std::vector& specs_to_install, MultiPackageCache& caches ) : MTransaction(pool, caches) @@ -414,7 +414,8 @@ namespace mamba if (solver.flags().keep_specs) { - auto to_string_vec = [](const std::vector& vec) -> std::vector + auto to_string_vec = [](const std::vector& vec + ) -> std::vector { std::vector res = {}; res.reserve(vec.size()); @@ -579,10 +580,10 @@ namespace mamba m_solution = transaction_to_solution(m_pool, trans); - std::vector specs_to_install; + std::vector specs_to_install; for (const auto& pkginfo : packages) { - specs_to_install.push_back(MatchSpec::parse( + specs_to_install.push_back(specs::MatchSpec::parse( fmt::format("{}=={}={}", pkginfo.name, pkginfo.version, pkginfo.build_string) )); } @@ -1370,7 +1371,7 @@ namespace mamba MTransaction create_explicit_transaction_from_urls(MPool& pool, const std::vector& urls, MultiPackageCache& package_caches, std::vector&) { - std::vector specs_to_install = {}; + std::vector specs_to_install = {}; specs_to_install.reserve(urls.size()); for (auto& raw_url : urls) { @@ -1381,8 +1382,8 @@ namespace mamba } const auto hash_idx = url.find_first_of('#'); - specs_to_install.emplace_back(MatchSpec::parse(url.substr(0, hash_idx))); - MatchSpec& ms = specs_to_install.back(); + specs_to_install.emplace_back(specs::MatchSpec::parse(url.substr(0, hash_idx))); + specs::MatchSpec& ms = specs_to_install.back(); if (hash_idx != std::string::npos) { diff --git a/libmamba/src/core/transaction_context.cpp b/libmamba/src/core/transaction_context.cpp index f51b120feb..795edd44f0 100644 --- a/libmamba/src/core/transaction_context.cpp +++ b/libmamba/src/core/transaction_context.cpp @@ -102,7 +102,7 @@ namespace mamba const Context& context, const fs::u8path& ltarget_prefix, const std::pair& py_versions, - const std::vector& lrequested_specs + const std::vector& lrequested_specs ) : has_python(py_versions.first.size() != 0) , target_prefix(ltarget_prefix) @@ -145,7 +145,7 @@ namespace mamba const fs::u8path& ltarget_prefix, const fs::u8path& lrelocate_prefix, const std::pair& py_versions, - const std::vector& lrequested_specs + const std::vector& lrequested_specs ) : TransactionContext(context, ltarget_prefix, py_versions, lrequested_specs) { diff --git a/libmamba/src/core/match_spec.cpp b/libmamba/src/specs/match_spec.cpp similarity index 63% rename from libmamba/src/core/match_spec.cpp rename to libmamba/src/specs/match_spec.cpp index a1ed8e4a9f..b452011b16 100644 --- a/libmamba/src/core/match_spec.cpp +++ b/libmamba/src/specs/match_spec.cpp @@ -10,30 +10,13 @@ #include -#include "mamba/core/match_spec.hpp" -#include "mamba/core/output.hpp" #include "mamba/specs/archive.hpp" -#include "mamba/specs/platform.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/string.hpp" #include "mamba/util/url_manip.hpp" -namespace mamba +namespace mamba::specs { - namespace - { - auto parse_legacy_dist(std::string_view dist) -> std::vector - { - auto dist_str = std::string(specs::strip_archive_extension(dist)); - auto split_str = util::rsplit(dist_str, "-", 2); - if (split_str.size() != 3) - { - LOG_ERROR << "dist_str " << dist_str << " did not split into a correct version info."; - throw std::runtime_error("Invalid package filename"); - } - return split_str; - } - } - auto MatchSpec::parse_version_and_build(std::string_view s) -> std::tuple { @@ -68,6 +51,43 @@ namespace mamba } } + auto MatchSpec::parse_url(std::string_view spec) -> MatchSpec + { + auto fail_parse = [&]() + { + throw std::invalid_argument( + util::concat(R"(Fail to parse MatchSpec ditribution ")", spec, '"') + ); + }; + + auto out = MatchSpec(); + out.m_channel = ChannelSpec::parse(spec); + auto [_, pkg] = util::rsplit_once(out.m_channel->location(), '/'); + out.m_filename = std::string(pkg); + out.m_url = util::path_or_url_to_url(spec); + + // Build string + auto [head, tail] = util::rsplit_once(strip_archive_extension(pkg), '-'); + out.m_build_string = tail; + if (!head.has_value()) + { + fail_parse(); + } + + // Version + std::tie(head, tail) = util::rsplit_once(head.value(), '-'); + out.m_version = tail; + if (!head.has_value()) + { + fail_parse(); + } + + // Name + out.m_name = head.value(); // There may be '-' in the name + + return out; + } + auto MatchSpec::parse(std::string_view spec) -> MatchSpec { auto spec_str = std::string(spec); @@ -83,18 +103,9 @@ namespace mamba } spec_str = util::strip(spec_str); - if (specs::has_archive_extension(spec_str)) + if (has_archive_extension(spec_str)) { - out.channel = specs::ChannelSpec::parse(spec_str); - auto [path, pkg] = util::rsplit_once(out.channel->location(), '/'); - auto dist = parse_legacy_dist(pkg); - out.name = dist[0]; - out.version = dist[1]; - out.build_string = dist[2]; - out.fn = std::string(pkg); - out.url = util::path_or_url_to_url(spec_str); - out.is_file = true; - return out; + return MatchSpec::parse_url(spec_str); } auto extract_kv = [&spec_str](const std::string& kv_string, auto& map) @@ -142,7 +153,7 @@ namespace mamba extract_kv(parens_str, out.parens); if (parens_str.find("optional") != parens_str.npos) { - out.optional = true; + out.m_optional = true; } spec_str.erase( static_cast(match.position(1)), @@ -155,13 +166,13 @@ namespace mamba std::string channel_str; if (m5_len == 3) { - out.channel = specs::ChannelSpec::parse(m5[0]); - out.ns = m5[1]; + out.m_channel = ChannelSpec::parse(m5[0]); + out.m_name_space = m5[1]; spec_str = m5[2]; } else if (m5_len == 2) { - out.ns = m5[0]; + out.m_name_space = m5[0]; spec_str = m5[1]; } else if (m5_len == 1) @@ -173,12 +184,6 @@ namespace mamba throw std::runtime_error("Parsing of channel / namespace / subdir failed."); } - auto get_known_platforms = []() -> std::vector - { - auto plats = specs::known_platform_names(); - return { plats.begin(), plats.end() }; - }; - // support faulty conda matchspecs such as `libblas=[build=*mkl]`, which is // the repr of `libblas=*=*mkl` if (spec_str.back() == '=') @@ -190,9 +195,9 @@ namespace mamba std::smatch vb_match; if (std::regex_match(spec_str, vb_match, version_build_re)) { - out.name = vb_match[1].str(); - out.version = util::strip(vb_match[2].str()); - if (out.name.size() == 0) + out.m_name = vb_match[1].str(); + out.m_version = util::strip(vb_match[2].str()); + if (out.m_name.size() == 0) { throw std::runtime_error("Invalid spec, no package name found: " + spec_str); } @@ -204,9 +209,9 @@ namespace mamba // # Step 7. otherwise sort out version + build // spec_str = spec_str and spec_str.strip() - if (!out.version.empty()) + if (!out.m_version.empty()) { - if (out.version.find('[') != out.version.npos) + if (out.m_version.find('[') != out.m_version.npos) { throw std::runtime_error(util::concat( R"(Invalid match spec: multiple bracket sections not allowed ")", @@ -215,38 +220,38 @@ namespace mamba )); } - out.version = std::string(util::strip(out.version)); - auto [pv, pb] = parse_version_and_build(std::string(util::strip(out.version))); + out.m_version = std::string(util::strip(out.m_version)); + auto [pv, pb] = parse_version_and_build(std::string(util::strip(out.m_version))); - out.version = pv; - out.build_string = pb; + out.m_version = pv; + 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.version.size() >= 2 && out.version[0] == '=') + if (out.m_version.size() >= 2 && out.m_version[0] == '=') { - auto rest = out.version.substr(1); - if (out.version[1] == '=' && out.build_string.empty()) + auto rest = out.m_version.substr(1); + if (out.m_version[1] == '=' && out.m_build_string.empty()) { - out.version = out.version.substr(2); + out.m_version = out.m_version.substr(2); } else if (rest.find_first_of("=,|") == rest.npos) { - if (out.build_string.empty() && out.version.back() != '*') + if (out.m_build_string.empty() && out.m_version.back() != '*') { - out.version = util::concat(out.version, "*"); + out.m_version = util::concat(out.m_version, "*"); } else { - out.version = rest; + out.m_version = rest; } } } } else { - out.version = ""; - out.build_string = ""; + out.m_version = ""; + out.m_build_string = ""; } // TODO think about using a hash function here, (and elsewhere), like: @@ -256,78 +261,156 @@ namespace mamba { if (k == "build_number") { - out.build_number = v; + out.m_build_number = v; } else if (k == "build") { - out.build_string = v; + out.m_build_string = v; } else if (k == "version") { - out.version = v; + out.m_version = v; } else if (k == "channel") { - if (!out.channel.has_value()) + if (!out.m_channel.has_value()) { - out.channel = specs::ChannelSpec::parse(v); + out.m_channel = ChannelSpec::parse(v); } else { // Subdirs might have been set with a previous subdir key - auto subdirs = out.channel->clear_platform_filters(); - out.channel = specs::ChannelSpec::parse(v); + auto subdirs = out.m_channel->clear_platform_filters(); + out.m_channel = ChannelSpec::parse(v); if (!subdirs.empty()) { - out.channel = specs::ChannelSpec( - out.channel->clear_location(), + out.m_channel = ChannelSpec( + out.m_channel->clear_location(), std::move(subdirs), - out.channel->type() + out.m_channel->type() ); } } } else if (k == "subdir") { - if (!out.channel.has_value()) + if (!out.m_channel.has_value()) { - out.channel = specs::ChannelSpec("", { v }, specs::ChannelSpec::Type::Unknown); + out.m_channel = ChannelSpec("", { v }, ChannelSpec::Type::Unknown); } // Subdirs specified in the channel part have higher precedence - else if (out.channel->platform_filters().empty()) + else if (out.m_channel->platform_filters().empty()) { - out.channel = specs::ChannelSpec( - out.channel->clear_location(), + out.m_channel = ChannelSpec( + out.m_channel->clear_location(), { v }, - out.channel->type() + out.m_channel->type() ); } } else if (k == "url") { - out.is_file = true; - out.url = v; + out.m_url = v; } else if (k == "fn") { - out.is_file = true; - out.fn = v; + out.m_filename = v; } } return out; } + auto MatchSpec::channel() const -> const std::optional& + { + return m_channel; + } + + void MatchSpec::set_channel(std::optional chan) + { + m_channel = std::move(chan); + } + + auto MatchSpec::name_space() const -> const std::string& + { + return m_name_space; + } + + void MatchSpec::set_name_space(std::string ns) + { + m_name_space = std::move(ns); + } + + auto MatchSpec::name() const -> const std::string& + { + return m_name; + } + + void MatchSpec::set_name(std::string name) + { + m_name = std::move(name); + } + + auto MatchSpec::version() const -> const std::string& + { + return m_version; + } + + void MatchSpec::set_version(std::string ver) + { + m_version = std::move(ver); + } + + auto MatchSpec::build_number() const -> const std::string& + { + return m_build_number; + } + + void MatchSpec::set_build_number(std::string bn) + { + m_build_number = std::move(bn); + } + + auto MatchSpec::build_string() const -> const std::string& + { + return m_build_string; + } + + auto MatchSpec::optional() const -> bool + { + return m_optional; + } + + void MatchSpec::set_optional(bool opt) + { + m_optional = opt; + } + + void MatchSpec::set_build_string(std::string bs) + { + m_build_string = std::move(bs); + } + + auto MatchSpec::filename() const -> const std::string& + { + return m_filename; + } + + auto MatchSpec::url() const -> const std::string& + { + return m_url; + } + auto MatchSpec::conda_build_form() const -> std::string { std::stringstream res; - res << name; - if (!version.empty()) + res << m_name; + if (!m_version.empty()) { - res << " " << version; + res << " " << m_version; // if (!build.empty() && (build != "*")) - if (!build_string.empty()) + if (!m_build_string.empty()) { - res << " " << build_string; + res << " " << m_build_string; } } return res.str(); @@ -352,9 +435,9 @@ namespace mamba // else: // brackets.append("subdir=%s" % subdir_matcher) - if (channel.has_value()) + if (m_channel.has_value()) { - res << fmt::format("{}::", *channel); + res << fmt::format("{}::", *m_channel); } // TODO when namespaces are implemented! // if (!ns.empty()) @@ -362,78 +445,78 @@ namespace mamba // res << ns; // res << ":"; // } - res << (!name.empty() ? name : "*"); + res << (!m_name.empty() ? m_name : "*"); std::vector formatted_brackets; bool version_exact = false; auto is_complex_relation = [](const std::string& s) { return s.find_first_of("><$^|,") != s.npos; }; - if (!version.empty()) + if (!m_version.empty()) { - if (is_complex_relation(version)) + if (is_complex_relation(m_version)) { - formatted_brackets.push_back(util::concat("version='", version, "'")); + formatted_brackets.push_back(util::concat("version='", m_version, "'")); } - else if (util::starts_with(version, "!=") || util::starts_with(version, "~=")) + else if (util::starts_with(m_version, "!=") || util::starts_with(m_version, "~=")) { - if (!build_string.empty()) + if (!m_build_string.empty()) { - formatted_brackets.push_back(util::concat("version='", version, "'")); + formatted_brackets.push_back(util::concat("version='", m_version, "'")); } else { - res << " " << version; + res << " " << m_version; } } - else if (util::ends_with(version, ".*")) + else if (util::ends_with(m_version, ".*")) { - res << "=" + version.substr(0, version.size() - 2); + res << "=" + m_version.substr(0, m_version.size() - 2); } - else if (version.back() == '*') + else if (m_version.back() == '*') { - if (version.size() == 1) + if (m_version.size() == 1) { res << "=*"; } - else if (util::starts_with(version, "=")) + else if (util::starts_with(m_version, "=")) { - res << version.substr(0, version.size() - 1); + res << m_version.substr(0, m_version.size() - 1); } else { - res << "=" + version.substr(0, version.size() - 1); + res << "=" + m_version.substr(0, m_version.size() - 1); } } - else if (util::starts_with(version, "==")) + else if (util::starts_with(m_version, "==")) { - res << version; + res << m_version; version_exact = true; } else { - res << "==" << version; + res << "==" << m_version; version_exact = true; } } - if (!build_string.empty()) + if (!m_build_string.empty()) { - if (is_complex_relation(build_string)) + if (is_complex_relation(m_build_string)) { - formatted_brackets.push_back(util::concat("build='", build_string, '\'')); + formatted_brackets.push_back(util::concat("build='", m_build_string, '\'')); } - else if (build_string.find('*') != build_string.npos) + else if (m_build_string.find('*') != m_build_string.npos) { - formatted_brackets.push_back(util::concat("build=", build_string)); + formatted_brackets.push_back(util::concat("build=", m_build_string)); } else if (version_exact) { - res << "=" << build_string; + res << "=" << m_build_string; } else { - formatted_brackets.push_back(util::concat("build=", build_string)); + formatted_brackets.push_back(util::concat("build=", m_build_string)); } } @@ -442,7 +525,7 @@ namespace mamba "md5", "license", "license_family", "fn" }; - if (!url.empty()) + if (!m_url.empty()) { // erase "fn" when we have a URL check.pop_back(); @@ -482,6 +565,11 @@ namespace mamba auto MatchSpec::is_simple() const -> bool { - return version.empty() && build_string.empty() && build_number.empty(); + return m_version.empty() && m_build_string.empty() && m_build_number.empty(); + } + + auto MatchSpec::is_file() const -> bool + { + return (!m_filename.empty()) || (!m_url.empty()); } -} // namespace mamba +} diff --git a/libmamba/tests/CMakeLists.txt b/libmamba/tests/CMakeLists.txt index 4ae099eb19..e15692e7f5 100644 --- a/libmamba/tests/CMakeLists.txt +++ b/libmamba/tests/CMakeLists.txt @@ -49,6 +49,7 @@ set( 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 @@ -58,7 +59,6 @@ set( src/core/test_activation.cpp src/core/test_channel_context.cpp src/core/test_configuration.cpp - src/core/test_match_spec.cpp src/core/test_cpp.cpp src/core/test_downloader.cpp src/core/test_env_file_reading.cpp diff --git a/libmamba/tests/src/core/test_match_spec.cpp b/libmamba/tests/src/specs/test_match_spec.cpp similarity index 75% rename from libmamba/tests/src/core/test_match_spec.cpp rename to libmamba/tests/src/specs/test_match_spec.cpp index 2eaecd0f79..02a3dc73c7 100644 --- a/libmamba/tests/src/core/test_match_spec.cpp +++ b/libmamba/tests/src/specs/test_match_spec.cpp @@ -6,14 +6,15 @@ #include -#include "mamba/core/match_spec.hpp" #include "mamba/fs/filesystem.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/util/build.hpp" #include "mamba/util/string.hpp" using namespace mamba; +using namespace mamba::specs; -TEST_SUITE("MatchSpec") +TEST_SUITE("specs::match_spec") { using PlatformSet = typename util::flat_set; @@ -66,28 +67,28 @@ TEST_SUITE("MatchSpec") { { auto ms = MatchSpec::parse("xtensor==0.12.3"); - CHECK_EQ(ms.version, "0.12.3"); - CHECK_EQ(ms.name, "xtensor"); + CHECK_EQ(ms.version(), "0.12.3"); + CHECK_EQ(ms.name(), "xtensor"); } { auto ms = MatchSpec::parse(""); - CHECK_EQ(ms.version, ""); - CHECK_EQ(ms.name, ""); + CHECK_EQ(ms.version(), ""); + CHECK_EQ(ms.name(), ""); } { auto ms = MatchSpec::parse("ipykernel"); - CHECK_EQ(ms.version, ""); - CHECK_EQ(ms.name, "ipykernel"); + CHECK_EQ(ms.version(), ""); + CHECK_EQ(ms.name(), "ipykernel"); } { auto ms = MatchSpec::parse("ipykernel "); - CHECK_EQ(ms.version, ""); - CHECK_EQ(ms.name, "ipykernel"); + CHECK_EQ(ms.version(), ""); + CHECK_EQ(ms.name(), "ipykernel"); } { auto ms = MatchSpec::parse("numpy 1.7*"); - CHECK_EQ(ms.version, "1.7*"); - CHECK_EQ(ms.name, "numpy"); + CHECK_EQ(ms.version(), "1.7*"); + CHECK_EQ(ms.name(), "numpy"); CHECK_EQ(ms.conda_build_form(), "numpy 1.7*"); CHECK_EQ(ms.str(), "numpy=1.7"); } @@ -95,66 +96,79 @@ TEST_SUITE("MatchSpec") 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(), "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, "0.12.3"); - CHECK_EQ(ms.name, "xtensor"); - REQUIRE(ms.channel.has_value()); - CHECK_EQ(ms.channel->location(), "conda-forge"); - CHECK_EQ(ms.channel->platform_filters(), PlatformSet{ "linux-64" }); - CHECK_EQ(ms.optional, false); + CHECK_EQ(ms.version(), "0.12.3"); + CHECK_EQ(ms.name(), "xtensor"); + REQUIRE(ms.channel().has_value()); + CHECK_EQ(ms.channel()->location(), "conda-forge"); + CHECK_EQ(ms.channel()->platform_filters(), PlatformSet{ "linux-64" }); + CHECK_EQ(ms.optional(), false); } { auto ms = MatchSpec::parse("conda-forge::foo[build=3](target=blarg,optional)"); - CHECK_EQ(ms.version, ""); - CHECK_EQ(ms.name, "foo"); - REQUIRE(ms.channel.has_value()); - CHECK_EQ(ms.channel->location(), "conda-forge"); + CHECK_EQ(ms.version(), ""); + CHECK_EQ(ms.name(), "foo"); + REQUIRE(ms.channel().has_value()); + CHECK_EQ(ms.channel()->location(), "conda-forge"); CHECK_EQ(ms.brackets["build"], "3"); CHECK_EQ(ms.parens["target"], "blarg"); - CHECK_EQ(ms.optional, true); + CHECK_EQ(ms.optional(), true); } { auto ms = MatchSpec::parse("python[build_number=3]"); - CHECK_EQ(ms.name, "python"); + CHECK_EQ(ms.name(), "python"); CHECK_EQ(ms.brackets["build_number"], "3"); - CHECK_EQ(ms.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(), "python"); CHECK_EQ(ms.brackets["build_number"], "<=3"); - CHECK_EQ(ms.build_number, "<=3"); + CHECK_EQ(ms.build_number(), "<=3"); } { 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.version, "0.1"); - CHECK_EQ(ms.build_string, "conda_forge"); + CHECK_EQ(ms.name(), "_libgcc_mutex"); + CHECK_EQ(ms.version(), "0.1"); + CHECK_EQ(ms.build_string(), "conda_forge"); CHECK_EQ( - ms.url, + ms.url(), "https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2" ); - CHECK_EQ(ms.fn, "_libgcc_mutex-0.1-conda_forge.tar.bz2"); + CHECK_EQ(ms.filename(), "_libgcc_mutex-0.1-conda_forge.tar.bz2"); + } + { + 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.version(), "11.2.0"); + CHECK_EQ(ms.build_string(), "h1d223b6_13"); + CHECK_EQ( + ms.url(), + "https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_13.tar.bz2" + ); + CHECK_EQ(ms.filename(), "libgcc-ng-11.2.0-h1d223b6_13.tar.bz2"); } { 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.version, "0.1"); - CHECK_EQ(ms.build_string, "conda_forge"); + CHECK_EQ(ms.name(), "_libgcc_mutex"); + CHECK_EQ(ms.version(), "0.1"); + CHECK_EQ(ms.build_string(), "conda_forge"); if (util::on_win) { std::string driveletter = fs::absolute(fs::u8path("/")).string().substr(0, 1); CHECK_EQ( - ms.url, + ms.url(), util::concat( "file://", driveletter, @@ -165,23 +179,23 @@ TEST_SUITE("MatchSpec") else { CHECK_EQ( - ms.url, + ms.url(), "file:///home/randomguy/Downloads/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2" ); } - CHECK_EQ(ms.fn, "_libgcc_mutex-0.1-conda_forge.tar.bz2"); + CHECK_EQ(ms.filename(), "_libgcc_mutex-0.1-conda_forge.tar.bz2"); } { 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(), "xtensor"); CHECK_EQ( ms.brackets["url"], "file:///home/wolfv/Downloads/xtensor-0.21.4-hc9558a2_0.tar.bz2" ); - CHECK_EQ(ms.url, "file:///home/wolfv/Downloads/xtensor-0.21.4-hc9558a2_0.tar.bz2"); + CHECK_EQ(ms.url(), "file:///home/wolfv/Downloads/xtensor-0.21.4-hc9558a2_0.tar.bz2"); } { auto ms = MatchSpec::parse("foo=1.0=2"); diff --git a/libmambapy/src/libmambapy/bindings/legacy.cpp b/libmambapy/src/libmambapy/bindings/legacy.cpp index 4802d492df..5f92bac045 100644 --- a/libmambapy/src/libmambapy/bindings/legacy.cpp +++ b/libmambapy/src/libmambapy/bindings/legacy.cpp @@ -34,7 +34,6 @@ #include "mamba/core/transaction.hpp" #include "mamba/core/util_os.hpp" #include "mamba/core/virtual_packages.hpp" -#include "mamba/specs/version.hpp" #include "mamba/util/string.hpp" #include "mamba/validation/tools.hpp" #include "mamba/validation/update_framework_v0_6.hpp" @@ -287,21 +286,6 @@ bind_submodule_impl(pybind11::module_ m) py::add_ostream_redirect(m, "ostream_redirect"); - py::class_(m, "MatchSpec") - .def_static("parse", &MatchSpec::parse) - .def(py::init<>()) - .def( - // Deprecating would lead to confusing error. Better to make sure people stop using it. - py::init( - [](std::string_view) -> MatchSpec { - throw std::invalid_argument( - "Use 'MatchSpec.parse' to create a new object from a string" - ); - } - ), - py::arg("spec") - ) - .def("conda_build_form", &MatchSpec::conda_build_form); py::class_(m, "Pool") .def( @@ -406,8 +390,11 @@ bind_submodule_impl(pybind11::module_ m) py::class_(pyPbGraph, "RootNode").def(py::init<>()); py::class_(pyPbGraph, "PackageNode"); - py::class_(pyPbGraph, "UnresolvedDependencyNode"); - py::class_(pyPbGraph, "ConstraintNode"); + py::class_( + pyPbGraph, + "UnresolvedDependencyNode" + ); + py::class_(pyPbGraph, "ConstraintNode"); py::class_(pyPbGraph, "ConflictMap") .def(py::init([]() { return PbGraph::conflicts_t(); })) diff --git a/libmambapy/src/libmambapy/bindings/specs.cpp b/libmambapy/src/libmambapy/bindings/specs.cpp index 43a483475a..90de742b59 100644 --- a/libmambapy/src/libmambapy/bindings/specs.cpp +++ b/libmambapy/src/libmambapy/bindings/specs.cpp @@ -13,6 +13,7 @@ #include "mamba/specs/channel.hpp" #include "mamba/specs/channel_spec.hpp" #include "mamba/specs/conda_url.hpp" +#include "mamba/specs/match_spec.hpp" #include "mamba/specs/platform.hpp" #include "mamba/specs/version.hpp" #include "mamba/specs/version_spec.hpp" @@ -564,5 +565,24 @@ namespace mambapy .def("__str__", &VersionSpec::str) .def("__copy__", ©) .def("__deepcopy__", &deepcopy, py::arg("memo")); + + // WIP MatchSpec class + py::class_(m, "MatchSpec") + .def_static("parse", &MatchSpec::parse) + .def( + // Hard deperecation since errors would be hard to track. + py::init( + [](std::string_view) -> MatchSpec { + throw std::invalid_argument( + "Use 'MatchSpec.parse' to create a new object from a string" + ); + } + ), + py::arg("spec") + ) + .def("conda_build_form", &MatchSpec::conda_build_form) + .def("__str__", &MatchSpec::str) + .def("__copy__", ©) + .def("__deepcopy__", &deepcopy, py::arg("memo")); } } diff --git a/libmambapy/tests/test_specs.py b/libmambapy/tests/test_specs.py index 4438c42cd1..05f76eb665 100644 --- a/libmambapy/tests/test_specs.py +++ b/libmambapy/tests/test_specs.py @@ -656,3 +656,12 @@ def test_VersionSpec(): # Copy, no easy comparison, this may not work for all specs assert str(copy.deepcopy(vs)) == str(vs) + + +def test_MatchSpec(): + MatchSpec = libmambapy.specs.MatchSpec + + ms = MatchSpec.parse("conda-forge::python=3.7=*pypy") + + # str + assert str(ms) == "conda-forge::python==3.7[build=*pypy]" diff --git a/micromamba/src/update.cpp b/micromamba/src/update.cpp index d7a86b6b17..37cc448ab7 100644 --- a/micromamba/src/update.cpp +++ b/micromamba/src/update.cpp @@ -51,11 +51,14 @@ update_self(Configuration& config, const std::optional& version) std::string matchspec = version ? fmt::format("micromamba={}", version.value()) : fmt::format("micromamba>{}", umamba::version()); - auto solvable_ids = pool.select_solvables(pool.matchspec2id(MatchSpec::parse(matchspec)), true); + auto solvable_ids = pool.select_solvables( + pool.matchspec2id(specs::MatchSpec::parse(matchspec)), + true + ); if (solvable_ids.empty()) { - if (pool.select_solvables(pool.matchspec2id(MatchSpec::parse("micromamba"))).empty()) + if (pool.select_solvables(pool.matchspec2id(specs::MatchSpec::parse("micromamba"))).empty()) { throw mamba::mamba_error( "No micromamba found in the loaded channels. Add 'conda-forge' to your config file.", diff --git a/micromamba/tests/test_install.py b/micromamba/tests/test_install.py index a0c813ee55..bb7f887e6f 100644 --- a/micromamba/tests/test_install.py +++ b/micromamba/tests/test_install.py @@ -533,7 +533,7 @@ def test_broken_package_name(self): try: helpers.install(non_existing_url, default_channel=False) except subprocess.CalledProcessError as e: - assert "Invalid package filename" in e.stderr.decode("utf-8") + assert " Fail to parse MatchSpec" in e.stderr.decode("utf-8") def test_no_reinstall(self, existing_cache): """Reinstalling is a no op."""