Skip to content

Commit

Permalink
Add Matcher as namespace callback
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoinePrv committed Mar 14, 2024
1 parent 9c07a0e commit 686ee86
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
1 change: 1 addition & 0 deletions libmamba/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions libmamba/src/solver/libsolv/database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "solv-cpp/queue.hpp"

#include "solver/libsolv/helpers.hpp"
#include "solver/libsolv/matcher.hpp"

namespace mamba::solver::libsolv
{
Expand All @@ -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)
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down
99 changes: 99 additions & 0 deletions libmamba/src/solver/libsolv/matcher.cpp
Original file line number Diff line number Diff line change
@@ -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 <fmt/format.h>

#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<solv::OffsetId>
{
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<Pkg>
{
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<bool> { return false; })
.value();
}
}
68 changes: 68 additions & 0 deletions libmamba/src/solver/libsolv/matcher.hpp
Original file line number Diff line number Diff line change
@@ -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 <functional>
#include <string>
#include <string_view>

#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<Pkg>;

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
2 changes: 1 addition & 1 deletion libmamba/tests/src/solver/libsolv/test_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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= */ {},
Expand Down

0 comments on commit 686ee86

Please sign in to comment.