From 686ee868842609d4c3946dbe376eae8d34090819 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Thu, 14 Mar 2024 14:54:02 +0100 Subject: [PATCH] Add Matcher as namespace callback --- libmamba/CMakeLists.txt | 1 + libmamba/src/solver/libsolv/database.cpp | 25 +++++ libmamba/src/solver/libsolv/matcher.cpp | 99 +++++++++++++++++++ libmamba/src/solver/libsolv/matcher.hpp | 68 +++++++++++++ .../tests/src/solver/libsolv/test_solver.cpp | 2 +- 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 libmamba/src/solver/libsolv/matcher.cpp create mode 100644 libmamba/src/solver/libsolv/matcher.hpp diff --git a/libmamba/CMakeLists.txt b/libmamba/CMakeLists.txt index b5e4ac18d4..4050641382 100644 --- a/libmamba/CMakeLists.txt +++ b/libmamba/CMakeLists.txt @@ -181,6 +181,7 @@ set( # Solver libsolv implementation ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/database.cpp ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/helpers.cpp + ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/matcher.cpp ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/parameters.cpp ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/repo_info.cpp ${LIBMAMBA_SOURCE_DIR}/solver/libsolv/solver.cpp diff --git a/libmamba/src/solver/libsolv/database.cpp b/libmamba/src/solver/libsolv/database.cpp index 5e85af3445..5cd5b3f008 100644 --- a/libmamba/src/solver/libsolv/database.cpp +++ b/libmamba/src/solver/libsolv/database.cpp @@ -24,6 +24,7 @@ #include "solv-cpp/queue.hpp" #include "solver/libsolv/helpers.hpp" +#include "solver/libsolv/matcher.hpp" namespace mamba::solver::libsolv { @@ -36,6 +37,8 @@ namespace mamba::solver::libsolv specs::ChannelResolveParams channel_params; solv::ObjPool pool = {}; + Matcher matcher = {}; + std::exception_ptr error = nullptr; }; Database::Database(specs::ChannelResolveParams channel_params) @@ -45,6 +48,23 @@ namespace mamba::solver::libsolv // Ensure that debug logging never goes to stdout as to not interfere json output pool().raw()->debugmask |= SOLV_DEBUG_TO_STDERR; ::pool_setdebuglevel(pool().raw(), -1); // Off + pool().set_namespace_callback( + [&data = (*m_data + )](solv::ObjPoolView pool, + solv::StringId name, + solv::StringId /* version */) noexcept -> solv::OffsetId + { + try + { + return data.matcher.get_matching_packages(pool, name); + } + catch (...) + { + data.error = std::current_exception(); + } + return 0; + } + ); } Database::~Database() = default; @@ -55,6 +75,11 @@ namespace mamba::solver::libsolv auto Database::pool() -> solv::ObjPool& { + if (auto error = m_data->error; error != nullptr) + { + m_data->error = nullptr; + std::rethrow_exception(error); + } return m_data->pool; } diff --git a/libmamba/src/solver/libsolv/matcher.cpp b/libmamba/src/solver/libsolv/matcher.cpp new file mode 100644 index 0000000000..3026727902 --- /dev/null +++ b/libmamba/src/solver/libsolv/matcher.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2024, 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 + +#include "solver/libsolv/matcher.hpp" + +namespace mamba::solver::libsolv +{ + + auto Matcher::get_matching_packages( // + solv::ObjPoolView pool, + const specs::MatchSpec& ms + ) -> solv::OffsetId + { + m_packages.clear(); // Reuse the buffer + + auto add_pkg_if_matching = [&](solv::ObjSolvableViewConst s) + { + if (pkg_match(pool, s, ms)) + { + m_packages.push_back(s.id()); + } + }; + + if (ms.name().is_exact()) + { + // Name does not have glob so we can use it as index into packages with exact name. + auto name_id = pool.add_string(ms.name().str()); + pool.for_each_whatprovides(name_id, add_pkg_if_matching); + } + else + { + // Name is a Glob (e.g. ``py*``) so we have to loop through all packages. + pool.for_each_solvable(add_pkg_if_matching); + } + return pool.add_to_whatprovides_data(m_packages); + } + + auto Matcher::get_matching_packages( // + solv::ObjPoolView pool, + solv::StringId dep + ) -> solv::OffsetId + { + return specs::MatchSpec::parse(pool.get_string(dep)) + .transform([&](const specs::MatchSpec& ms) { return get_matching_packages(pool, ms); }) + .or_else( + [&](const auto& error) -> specs::expected_parse_t + { + pool.set_current_error(error.what()); + return pool.add_to_whatprovides_data({}); + } + ) + .value(); + } + + auto Matcher::get_pkg_attributes(solv::ObjPoolView pool, solv::ObjSolvableViewConst solv) + -> expected_t + { + auto version = specs::Version::parse(solv.version()); + if (!version.has_value()) + { + return make_unexpected(version.error().what(), mamba_error_code::invalid_spec); + } + + auto track_features = specs::MatchSpec::string_set(); + for (solv::StringId id : solv.track_features()) + { + track_features.insert(std::string(pool.get_string(id))); + } + + return { Pkg{ + /* .name= */ solv.name(), + /* .version= */ std::move(version).value(), + /* .build_string= */ solv.build_string(), + /* .build_number= */ solv.build_number(), + /* .md5= */ solv.md5(), + /* .sha256= */ solv.sha256(), + /* .license= */ solv.license(), + /* .platform= */ std::string(solv.platform()), + /* .track_features= */ std::move(track_features), + } }; + } + + auto Matcher::pkg_match( // + solv::ObjPoolView pool, + solv::ObjSolvableViewConst solv, + const specs::MatchSpec& ms + ) -> bool + { + return get_pkg_attributes(pool, solv) + .transform([&](const Pkg& pkg) -> bool { return ms.contains_except_channel(pkg); }) + .or_else([](const auto&) -> expected_t { return false; }) + .value(); + } +} diff --git a/libmamba/src/solver/libsolv/matcher.hpp b/libmamba/src/solver/libsolv/matcher.hpp new file mode 100644 index 0000000000..f357d459dc --- /dev/null +++ b/libmamba/src/solver/libsolv/matcher.hpp @@ -0,0 +1,68 @@ +// Copyright (c) 2024, 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_SOLVER_LIBSOLV_MATCHER +#define MAMBA_SOLVER_LIBSOLV_MATCHER + +#include +#include +#include + +#include "mamba/core/error_handling.hpp" +#include "mamba/specs/match_spec.hpp" +#include "mamba/specs/version.hpp" +#include "solv-cpp/pool.hpp" +#include "solv-cpp/solvable.hpp" + +namespace mamba::solver::libsolv +{ + class Matcher + { + public: + + auto get_matching_packages( // + solv::ObjPoolView pool, + const specs::MatchSpec& ms + ) -> solv::OffsetId; + + auto get_matching_packages( // + solv::ObjPoolView pool, + solv::StringId dep + ) -> solv::OffsetId; + + private: + + struct Pkg + { + std::string_view name; + specs::Version version; + std::string_view build_string; + std::size_t build_number; + std::string_view md5; + std::string_view sha256; + std::string_view license; + std::string platform; + specs::MatchSpec::string_set track_features; + }; + + auto get_pkg_attributes( // + solv::ObjPoolView pool, + solv::ObjSolvableViewConst solv + ) -> expected_t; + + auto pkg_match( // + solv::ObjPoolView pool, + solv::ObjSolvableViewConst solv, + const specs::MatchSpec& ms + ) -> bool; + + solv::ObjQueue m_packages = {}; + // TODO use matchspec cache? Or not since they would have the same string in libdolv... + // TODO use version cache + // TODO handle channels + }; +} +#endif diff --git a/libmamba/tests/src/solver/libsolv/test_solver.cpp b/libmamba/tests/src/solver/libsolv/test_solver.cpp index bff54dd82d..43e055d429 100644 --- a/libmamba/tests/src/solver/libsolv/test_solver.cpp +++ b/libmamba/tests/src/solver/libsolv/test_solver.cpp @@ -843,7 +843,7 @@ TEST_SUITE("solver::libsolv::solver") )); } - SUBCASE("conda-forge[subdir=linux-64]") + SUBCASE("conda-forge::numpy[subdir=linux-64]") { auto request = Request{ /* .flags= */ {},