mirror of https://github.com/mamba-org/mamba.git
Internally add flag for switching MatchSpec parser (#3905)
This commit is contained in:
parent
939ed2f038
commit
306e4542a1
|
@ -8,7 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
|||
|
||||
add_library(
|
||||
solv-cpp OBJECT
|
||||
src/pool.cpp src/queue.cpp src/repo.cpp src/solvable.cpp src/solver.cpp src/transaction.cpp
|
||||
src/pool.cpp
|
||||
src/queue.cpp
|
||||
src/repo.cpp
|
||||
src/solvable.cpp
|
||||
src/solver.cpp
|
||||
src/transaction.cpp
|
||||
src/dependency.cpp
|
||||
)
|
||||
target_include_directories(
|
||||
solv-cpp
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2025, 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_SOLV_DEPENDENCY_HPP
|
||||
#define MAMBA_SOLV_DEPENDENCY_HPP
|
||||
|
||||
#include <solv/poolid.h>
|
||||
|
||||
#include "solv-cpp/ids.hpp"
|
||||
|
||||
namespace solv
|
||||
{
|
||||
class ObjDependencyViewConst
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ObjDependencyViewConst(const ::Reldep& reldep) noexcept;
|
||||
~ObjDependencyViewConst() noexcept;
|
||||
|
||||
[[nodiscard]] auto raw() const -> const ::Reldep*;
|
||||
|
||||
/**
|
||||
* The name field of the dependency.
|
||||
*
|
||||
* Can be a string id for simple dependencies, or another dependency id for
|
||||
* complex depndencies with boolean expressions.
|
||||
*/
|
||||
[[nodiscard]] auto name() const -> StringId /* OR DependencyId */;
|
||||
|
||||
/**
|
||||
* The version range field of the dependency.
|
||||
*
|
||||
* Can be a string id for simple dependencies, or another dependency id for
|
||||
* complex depndencies with boolean expressions.
|
||||
*/
|
||||
[[nodiscard]] auto version_range() const -> StringId /* OR DependencyId */;
|
||||
|
||||
/** The flags of the dependency, such as types. */
|
||||
[[nodiscard]] auto flags() const -> RelationFlag;
|
||||
|
||||
private:
|
||||
|
||||
const ::Reldep* m_reldep = nullptr;
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <solv/pool.h>
|
||||
|
||||
#include "solv-cpp/dependency.hpp"
|
||||
#include "solv-cpp/ids.hpp"
|
||||
#include "solv-cpp/queue.hpp"
|
||||
#include "solv-cpp/repo.hpp"
|
||||
|
@ -107,10 +108,23 @@ namespace solv
|
|||
auto add_dependency(StringId name_id, RelationFlag flag, StringId version_id) -> DependencyId;
|
||||
|
||||
/**
|
||||
* Parse a dependency from string and add it to the pool.
|
||||
* Parse a conda dependency from string and add it to the pool.
|
||||
*
|
||||
* This is currently the most efficient and stable way of adding dependencies.
|
||||
* We do not control the MatchSpec parser with this method so it may not be complete.
|
||||
*/
|
||||
auto add_conda_dependency(raw_str_view dep) -> DependencyId;
|
||||
auto add_conda_dependency(const std::string& dep) -> DependencyId;
|
||||
auto add_legacy_conda_dependency(raw_str_view dep) -> DependencyId;
|
||||
auto add_legacy_conda_dependency(const std::string& dep) -> DependencyId;
|
||||
|
||||
/**
|
||||
* Get the dependency object associated with the dependency id.
|
||||
*
|
||||
* Return nothing if not given a dependency id, which can be the case when string
|
||||
* ids are used as dependencies.
|
||||
* Can also be used to check if an id is a dependency id or not.
|
||||
*/
|
||||
auto get_dependency(DependencyId /* OR StringId */ id) const
|
||||
-> std::optional<ObjDependencyViewConst>;
|
||||
|
||||
/** Get the registered name of a dependency. */
|
||||
auto get_dependency_name(DependencyId id) const -> std::string_view;
|
||||
|
@ -323,6 +337,8 @@ namespace solv
|
|||
ObjPool();
|
||||
~ObjPool();
|
||||
|
||||
[[nodiscard]] auto view() const -> ObjPoolView;
|
||||
|
||||
using ObjPoolView::raw;
|
||||
using ObjPoolView::current_error;
|
||||
using ObjPoolView::set_current_error;
|
||||
|
@ -333,7 +349,7 @@ namespace solv
|
|||
using ObjPoolView::get_string;
|
||||
using ObjPoolView::find_dependency;
|
||||
using ObjPoolView::add_dependency;
|
||||
using ObjPoolView::add_conda_dependency;
|
||||
using ObjPoolView::add_legacy_conda_dependency;
|
||||
using ObjPoolView::get_dependency_name;
|
||||
using ObjPoolView::get_dependency_version;
|
||||
using ObjPoolView::get_dependency_relation;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#include "solv-cpp/dependency.hpp"
|
||||
|
||||
namespace solv
|
||||
{
|
||||
ObjDependencyViewConst::ObjDependencyViewConst(const ::Reldep& reldep) noexcept
|
||||
: m_reldep(&reldep)
|
||||
{
|
||||
}
|
||||
|
||||
ObjDependencyViewConst::~ObjDependencyViewConst() noexcept
|
||||
{
|
||||
m_reldep = nullptr;
|
||||
}
|
||||
|
||||
auto ObjDependencyViewConst::raw() const -> const ::Reldep*
|
||||
{
|
||||
return m_reldep;
|
||||
}
|
||||
|
||||
auto ObjDependencyViewConst::name() const -> StringId
|
||||
{
|
||||
return m_reldep->name;
|
||||
}
|
||||
|
||||
auto ObjDependencyViewConst::version_range() const -> StringId
|
||||
{
|
||||
return m_reldep->evr;
|
||||
}
|
||||
|
||||
auto ObjDependencyViewConst::flags() const -> RelationFlag
|
||||
{
|
||||
return m_reldep->flags;
|
||||
}
|
||||
}
|
|
@ -93,21 +93,15 @@ namespace solv
|
|||
|
||||
namespace
|
||||
{
|
||||
// This function is only used in `assert()` expressions
|
||||
// That's why it might get reported as unused in Release builds
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
auto is_reldep(::Id id) -> bool
|
||||
[[nodiscard]] auto is_reldep(::Id id) -> bool
|
||||
{
|
||||
return ISRELDEP(static_cast<std::make_unsigned_t<::Id>>(id)) != 0;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
[[nodiscard]] auto get_reldep(const ::Pool* pool, ::Id id) -> const ::Reldep*
|
||||
{
|
||||
return GETRELDEP(pool, static_cast<std::make_unsigned_t<::Id>>(id));
|
||||
}
|
||||
}
|
||||
|
||||
auto ObjPoolView::get_string(StringId id) const -> std::string_view
|
||||
|
@ -145,14 +139,25 @@ namespace solv
|
|||
return id;
|
||||
}
|
||||
|
||||
auto ObjPoolView::add_conda_dependency(raw_str_view dep) -> DependencyId
|
||||
auto ObjPoolView::add_legacy_conda_dependency(raw_str_view dep) -> DependencyId
|
||||
{
|
||||
return ::pool_conda_matchspec(raw(), dep);
|
||||
}
|
||||
|
||||
auto ObjPoolView::add_conda_dependency(const std::string& dep) -> DependencyId
|
||||
auto ObjPoolView::add_legacy_conda_dependency(const std::string& dep) -> DependencyId
|
||||
{
|
||||
return add_conda_dependency(dep.c_str());
|
||||
return add_legacy_conda_dependency(dep.c_str());
|
||||
}
|
||||
|
||||
auto ObjPoolView::get_dependency(DependencyId id) const -> std::optional<ObjDependencyViewConst>
|
||||
{
|
||||
if (!is_reldep(id))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
const auto rel = get_reldep(raw(), id);
|
||||
assert(rel != nullptr);
|
||||
return { ObjDependencyViewConst(*rel) };
|
||||
}
|
||||
|
||||
auto ObjPoolView::get_dependency_name(DependencyId id) const -> std::string_view
|
||||
|
@ -389,6 +394,11 @@ namespace solv
|
|||
|
||||
ObjPool::~ObjPool() = default;
|
||||
|
||||
auto ObjPool::view() const -> ObjPoolView
|
||||
{
|
||||
return *static_cast<const ObjPoolView*>(this);
|
||||
}
|
||||
|
||||
void ObjPool::set_namespace_callback(UserCallback&& callback)
|
||||
{
|
||||
m_user_namespace_callback = std::make_unique<NamespaceCallbackWrapper>();
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace solv::test
|
|||
solv.set_version(pkg.version);
|
||||
for (const auto& dep : pkg.dependencies)
|
||||
{
|
||||
solv.add_dependency(pool.add_conda_dependency(dep));
|
||||
solv.add_dependency(pool.add_legacy_conda_dependency(dep));
|
||||
}
|
||||
solv.add_self_provide();
|
||||
return solv_id;
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace
|
|||
|
||||
SECTION("Parse a conda dependency")
|
||||
{
|
||||
const auto id_conda = pool.add_conda_dependency("rattler < 0.1");
|
||||
const auto id_conda = pool.add_legacy_conda_dependency("rattler < 0.1");
|
||||
REQUIRE(pool.get_dependency_name(id_conda) == "rattler");
|
||||
REQUIRE(pool.get_dependency_version(id_conda) == "<0.1");
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("a"),
|
||||
pool.add_legacy_conda_dependency("a"),
|
||||
};
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
auto trans = ObjTransaction::from_solver(pool, solver);
|
||||
|
@ -58,7 +58,7 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("b==1.0"),
|
||||
pool.add_legacy_conda_dependency("b==1.0"),
|
||||
};
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
auto trans = ObjTransaction::from_solver(pool, solver);
|
||||
|
@ -70,7 +70,7 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("b==2.0"),
|
||||
pool.add_legacy_conda_dependency("b==2.0"),
|
||||
};
|
||||
solver.set_flag(SOLVER_FLAG_ALLOW_UNINSTALL, true);
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
|
@ -83,7 +83,7 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("c==1.0"),
|
||||
pool.add_legacy_conda_dependency("c==1.0"),
|
||||
};
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
auto trans = ObjTransaction::from_solver(pool, solver);
|
||||
|
@ -97,9 +97,9 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_LOCK | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("a"),
|
||||
pool.add_legacy_conda_dependency("a"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("c==1.0"),
|
||||
pool.add_legacy_conda_dependency("c==1.0"),
|
||||
};
|
||||
solver.set_flag(SOLVER_FLAG_ALLOW_UNINSTALL, true);
|
||||
REQUIRE_FALSE(solver.solve(pool, jobs));
|
||||
|
@ -117,7 +117,7 @@ namespace
|
|||
{
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("c==2.0"),
|
||||
pool.add_legacy_conda_dependency("c==2.0"),
|
||||
};
|
||||
REQUIRE_FALSE(solver.solve(pool, jobs));
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ namespace
|
|||
solver.set_flag(flag, true);
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("c==2.0"),
|
||||
pool.add_legacy_conda_dependency("c==2.0"),
|
||||
};
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
auto trans = ObjTransaction::from_solver(pool, solver);
|
||||
|
|
|
@ -44,9 +44,9 @@ namespace
|
|||
// The job is matched with the ``provides`` field of the solvable
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("menu"),
|
||||
pool.add_legacy_conda_dependency("menu"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("icons=2.*"),
|
||||
pool.add_legacy_conda_dependency("icons=2.*"),
|
||||
};
|
||||
REQUIRE(solver.solve(pool, jobs));
|
||||
REQUIRE(solver.problem_count() == 0);
|
||||
|
@ -56,9 +56,12 @@ namespace
|
|||
{
|
||||
// The job is matched with the ``provides`` field of the solvable
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, pool.add_conda_dependency("menu"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, pool.add_conda_dependency("icons=1.*"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES, pool.add_conda_dependency("intl=5.*"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_legacy_conda_dependency("menu"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_legacy_conda_dependency("icons=1.*"),
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_legacy_conda_dependency("intl=5.*"),
|
||||
};
|
||||
|
||||
REQUIRE_FALSE(solver.solve(pool, jobs));
|
||||
|
@ -80,7 +83,7 @@ namespace
|
|||
// The job is matched with the ``provides`` field of the solvable
|
||||
auto jobs = ObjQueue{
|
||||
SOLVER_INSTALL | SOLVER_SOLVABLE_PROVIDES,
|
||||
pool.add_conda_dependency("does-not-exists"),
|
||||
pool.add_legacy_conda_dependency("does-not-exists"),
|
||||
};
|
||||
|
||||
REQUIRE_FALSE(solver.solve(pool, jobs));
|
||||
|
|
|
@ -108,7 +108,9 @@ namespace
|
|||
pool.create_whatprovides();
|
||||
|
||||
auto solver = ObjSolver(pool);
|
||||
REQUIRE(solver.solve(pool, { SOLVER_INSTALL, pool.add_conda_dependency("menu>=1.4") }));
|
||||
REQUIRE(
|
||||
solver.solve(pool, { SOLVER_INSTALL, pool.add_legacy_conda_dependency("menu>=1.4") })
|
||||
);
|
||||
auto trans = ObjTransaction::from_solver(pool, solver);
|
||||
REQUIRE_FALSE(trans.empty());
|
||||
REQUIRE(trans.size() == 4);
|
||||
|
|
|
@ -19,6 +19,13 @@ namespace mamba::solver::libsolv
|
|||
Libsolv,
|
||||
};
|
||||
|
||||
enum class MatchSpecParser
|
||||
{
|
||||
Mixed,
|
||||
Mamba,
|
||||
Libsolv,
|
||||
};
|
||||
|
||||
enum class PipAsPythonDependency : bool
|
||||
{
|
||||
No = false,
|
||||
|
|
|
@ -117,6 +117,11 @@ namespace mamba::specs
|
|||
*/
|
||||
[[nodiscard]] auto is_only_package_name() const -> bool;
|
||||
|
||||
/**
|
||||
* Make a new MatchSpec that matches only on the name part.
|
||||
*/
|
||||
[[nodiscard]] auto to_named_spec() const -> MatchSpec;
|
||||
|
||||
/**
|
||||
* Check if the MatchSpec matches the given package.
|
||||
*
|
||||
|
|
|
@ -170,6 +170,7 @@ namespace mamba::solver::libsolv
|
|||
std::string(url),
|
||||
channel_id,
|
||||
package_types,
|
||||
MatchSpecParser::Libsolv, // Backward compatibility
|
||||
verify_artifacts
|
||||
);
|
||||
}
|
||||
|
@ -240,7 +241,12 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
auto s_repo = solv::ObjRepoView(*repo.m_ptr);
|
||||
auto [id, solv] = s_repo.add_solvable();
|
||||
set_solvable(pool(), solv, pkg);
|
||||
set_solvable(
|
||||
pool(),
|
||||
solv,
|
||||
pkg,
|
||||
MatchSpecParser::Libsolv // Backward compatibility
|
||||
);
|
||||
}
|
||||
|
||||
void Database::add_repo_from_packages_impl_post(const RepoInfo& repo, PipAsPythonDependency add)
|
||||
|
@ -324,9 +330,10 @@ namespace mamba::solver::libsolv
|
|||
|
||||
namespace
|
||||
{
|
||||
auto matchspec2id(solv::ObjPool& pool, const specs::MatchSpec& ms) -> solv::DependencyId
|
||||
auto pool_add_matchspec_throwing(solv::ObjPool& pool, const specs::MatchSpec& ms)
|
||||
-> solv::DependencyId
|
||||
{
|
||||
return pool_add_matchspec(pool, ms)
|
||||
return pool_add_matchspec(pool, ms, MatchSpecParser::Mixed)
|
||||
.or_else([](mamba_error&& error) { throw std::move(error); })
|
||||
.value_or(0);
|
||||
}
|
||||
|
@ -337,7 +344,7 @@ namespace mamba::solver::libsolv
|
|||
static_assert(std::is_same_v<std::underlying_type_t<PackageId>, solv::SolvableId>);
|
||||
|
||||
pool().ensure_whatprovides();
|
||||
const auto ms_id = matchspec2id(pool(), ms);
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms);
|
||||
auto solvables = pool().select_solvables({ SOLVER_SOLVABLE_PROVIDES, ms_id });
|
||||
auto out = std::vector<PackageId>(solvables.size());
|
||||
std::transform(
|
||||
|
@ -354,7 +361,7 @@ namespace mamba::solver::libsolv
|
|||
static_assert(std::is_same_v<std::underlying_type_t<PackageId>, solv::SolvableId>);
|
||||
|
||||
pool().ensure_whatprovides();
|
||||
const auto ms_id = matchspec2id(pool(), ms);
|
||||
const auto ms_id = pool_add_matchspec_throwing(pool(), ms);
|
||||
auto solvables = pool().what_matches_dep(SOLVABLE_REQUIRES, ms_id);
|
||||
auto out = std::vector<PackageId>(solvables.size());
|
||||
std::transform(
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <simdjson.h>
|
||||
#include <solv/conda.h>
|
||||
#include <solv/repo.h>
|
||||
|
@ -20,8 +21,10 @@
|
|||
|
||||
#include "mamba/core/output.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
#include "mamba/solver/libsolv/parameters.hpp"
|
||||
#include "mamba/specs/archive.hpp"
|
||||
#include "mamba/specs/conda_url.hpp"
|
||||
#include "mamba/specs/match_spec.hpp"
|
||||
#include "mamba/util/cfile.hpp"
|
||||
#include "mamba/util/random.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
@ -41,7 +44,12 @@ namespace mamba::solver::libsolv
|
|||
// to seconds.
|
||||
inline constexpr auto MAX_CONDA_TIMESTAMP = 253402300799ULL;
|
||||
|
||||
void set_solvable(solv::ObjPool& pool, solv::ObjSolvableView solv, const specs::PackageInfo& pkg)
|
||||
void set_solvable(
|
||||
solv::ObjPool& pool,
|
||||
solv::ObjSolvableView solv,
|
||||
const specs::PackageInfo& pkg,
|
||||
MatchSpecParser parser
|
||||
)
|
||||
{
|
||||
solv.set_name(pkg.name);
|
||||
solv.set_version(pkg.version);
|
||||
|
@ -72,16 +80,20 @@ namespace mamba::solver::libsolv
|
|||
|
||||
for (const auto& dep : pkg.dependencies)
|
||||
{
|
||||
// TODO pool's matchspec2id
|
||||
const solv::DependencyId dep_id = pool.add_conda_dependency(dep);
|
||||
const solv::DependencyId dep_id = //
|
||||
pool_add_matchspec(pool, dep.c_str(), parser)
|
||||
.or_else([](mamba_error&& err) { throw std::move(err); })
|
||||
.value();
|
||||
assert(dep_id);
|
||||
solv.add_dependency(dep_id);
|
||||
}
|
||||
|
||||
for (const auto& cons : pkg.constrains)
|
||||
{
|
||||
// TODO pool's matchspec2id
|
||||
const solv::DependencyId dep_id = pool.add_conda_dependency(cons);
|
||||
const solv::DependencyId dep_id = //
|
||||
pool_add_matchspec(pool, cons.c_str(), parser)
|
||||
.or_else([](mamba_error&& err) { throw std::move(err); })
|
||||
.value();
|
||||
assert(dep_id);
|
||||
solv.add_constraint(dep_id);
|
||||
}
|
||||
|
@ -193,7 +205,8 @@ namespace mamba::solver::libsolv
|
|||
const std::string& filename,
|
||||
JSONObject&& pkg,
|
||||
const std::optional<nlohmann::json>& signatures,
|
||||
const std::string& default_subdir
|
||||
const std::string& default_subdir,
|
||||
MatchSpecParser parser
|
||||
) -> bool
|
||||
{
|
||||
// Not available from RepoDataPackage
|
||||
|
@ -307,11 +320,20 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
if (!elem.error() && elem.is_string())
|
||||
{
|
||||
if (const auto dep_id = pool.add_conda_dependency(
|
||||
std::string(elem.get_string().value_unsafe())
|
||||
))
|
||||
const auto ms = std::string(elem.get_string().value_unsafe());
|
||||
const auto maybe_dep_id = pool_add_matchspec(pool, ms.c_str(), parser);
|
||||
if (maybe_dep_id)
|
||||
{
|
||||
solv.add_dependency(dep_id);
|
||||
solv.add_dependency(*maybe_dep_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::print(
|
||||
LOG_WARNING,
|
||||
R"(Found invalid MatchSpec "{}" in "{}")",
|
||||
ms,
|
||||
filename
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,11 +345,20 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
if (!elem.error() && elem.is_string())
|
||||
{
|
||||
if (const auto dep_id = pool.add_conda_dependency(
|
||||
std::string(elem.get_string().value_unsafe())
|
||||
))
|
||||
const auto ms = std::string(elem.get_string().value_unsafe());
|
||||
const auto maybe_dep_id = pool_add_matchspec(pool, ms.c_str(), parser);
|
||||
if (maybe_dep_id)
|
||||
{
|
||||
solv.add_constraint(dep_id);
|
||||
solv.add_constraint(*maybe_dep_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::print(
|
||||
LOG_WARNING,
|
||||
R"(Found invalid MatchSpec "{}" in "{}")",
|
||||
ms,
|
||||
filename
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +406,8 @@ namespace mamba::solver::libsolv
|
|||
JSONObject& packages,
|
||||
const std::optional<nlohmann::json>& signatures,
|
||||
Filter&& filter,
|
||||
OnParsed&& on_parsed
|
||||
OnParsed&& on_parsed,
|
||||
MatchSpecParser parser
|
||||
)
|
||||
{
|
||||
auto packages_as_object = packages.get_object();
|
||||
|
@ -393,7 +425,8 @@ namespace mamba::solver::libsolv
|
|||
filename,
|
||||
pkg_field.value(),
|
||||
signatures,
|
||||
default_subdir
|
||||
default_subdir,
|
||||
parser
|
||||
);
|
||||
if (parsed)
|
||||
{
|
||||
|
@ -416,7 +449,8 @@ namespace mamba::solver::libsolv
|
|||
const std::string& channel_id,
|
||||
const std::string& default_subdir,
|
||||
JSONObject& packages,
|
||||
const std::optional<nlohmann::json>& signatures
|
||||
const std::optional<nlohmann::json>& signatures,
|
||||
MatchSpecParser parser
|
||||
)
|
||||
{
|
||||
return set_repo_solvables_impl(
|
||||
|
@ -428,7 +462,8 @@ namespace mamba::solver::libsolv
|
|||
packages,
|
||||
signatures,
|
||||
/* filter= */ [](const auto&) { return true; },
|
||||
/* on_parsed= */ [](const auto&) {}
|
||||
/* on_parsed= */ [](const auto&) {},
|
||||
parser
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -440,7 +475,8 @@ namespace mamba::solver::libsolv
|
|||
const std::string& channel_id,
|
||||
const std::string& default_subdir,
|
||||
JSONObject& packages,
|
||||
const std::optional<nlohmann::json>& signatures
|
||||
const std::optional<nlohmann::json>& signatures,
|
||||
MatchSpecParser parser
|
||||
) -> util::flat_set<std::string>
|
||||
{
|
||||
auto filenames = util::flat_set<std::string>();
|
||||
|
@ -453,8 +489,10 @@ namespace mamba::solver::libsolv
|
|||
packages,
|
||||
signatures,
|
||||
/* filter= */ [](const auto&) { return true; },
|
||||
/* on_parsed= */ [&](const auto& fn)
|
||||
{ filenames.insert(std::string(specs::strip_archive_extension(fn))); }
|
||||
/* on_parsed= */
|
||||
[&](const auto& fn)
|
||||
{ filenames.insert(std::string(specs::strip_archive_extension(fn))); },
|
||||
parser
|
||||
);
|
||||
// Sort only once
|
||||
return filenames;
|
||||
|
@ -469,7 +507,8 @@ namespace mamba::solver::libsolv
|
|||
const std::string& default_subdir,
|
||||
JSONObject& packages,
|
||||
const std::optional<nlohmann::json>& signatures,
|
||||
const SortedStringRange& added
|
||||
const SortedStringRange& added,
|
||||
MatchSpecParser parser
|
||||
)
|
||||
{
|
||||
return set_repo_solvables_impl(
|
||||
|
@ -480,9 +519,10 @@ namespace mamba::solver::libsolv
|
|||
default_subdir,
|
||||
packages,
|
||||
signatures,
|
||||
/* filter= */ [&](const auto& fn)
|
||||
{ return !added.contains(specs::strip_archive_extension(fn)); },
|
||||
/* on_parsed= */ [&](const auto&) {}
|
||||
/* filter= */
|
||||
[&](const auto& fn) { return !added.contains(specs::strip_archive_extension(fn)); },
|
||||
/* on_parsed= */ [&](const auto&) {},
|
||||
parser
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -542,6 +582,7 @@ namespace mamba::solver::libsolv
|
|||
const std::string& repo_url,
|
||||
const std::string& channel_id,
|
||||
PackageTypes package_types,
|
||||
MatchSpecParser ms_parser,
|
||||
bool verify_artifacts
|
||||
) -> expected_t<solv::ObjRepoView>
|
||||
{
|
||||
|
@ -657,7 +698,8 @@ namespace mamba::solver::libsolv
|
|||
channel_id,
|
||||
default_subdir,
|
||||
pkgs,
|
||||
json_signatures
|
||||
json_signatures,
|
||||
ms_parser
|
||||
);
|
||||
}
|
||||
if (auto pkgs = repodata_doc["packages"]; !pkgs.error())
|
||||
|
@ -670,7 +712,8 @@ namespace mamba::solver::libsolv
|
|||
default_subdir,
|
||||
pkgs,
|
||||
json_signatures,
|
||||
added
|
||||
added,
|
||||
ms_parser
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -686,7 +729,8 @@ namespace mamba::solver::libsolv
|
|||
channel_id,
|
||||
default_subdir,
|
||||
pkgs,
|
||||
json_signatures
|
||||
json_signatures,
|
||||
ms_parser
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -700,7 +744,8 @@ namespace mamba::solver::libsolv
|
|||
channel_id,
|
||||
default_subdir,
|
||||
pkgs,
|
||||
json_signatures
|
||||
json_signatures,
|
||||
ms_parser
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -870,8 +915,12 @@ namespace mamba::solver::libsolv
|
|||
|
||||
void add_pip_as_python_dependency(solv::ObjPool& pool, solv::ObjRepoView repo)
|
||||
{
|
||||
const solv::DependencyId python_id = pool.add_conda_dependency("python");
|
||||
const solv::DependencyId pip_id = pool.add_conda_dependency("pip");
|
||||
// These matchspecs are so simple that there should be no surprises in using
|
||||
// the libsolv parser, or in getting back an error.
|
||||
const solv::DependencyId python_id = //
|
||||
pool_add_matchspec(pool, "python", MatchSpecParser::Libsolv).value();
|
||||
const solv::DependencyId pip_id = //
|
||||
pool_add_matchspec(pool, "pip", MatchSpecParser::Libsolv).value();
|
||||
repo.for_each_solvable(
|
||||
[&](solv::ObjSolvableView s)
|
||||
{
|
||||
|
@ -899,7 +948,7 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
|
||||
auto get_abused_namespace_callback_args( //
|
||||
solv::ObjPoolView& pool,
|
||||
solv::ObjPoolView pool,
|
||||
solv::StringId name,
|
||||
solv::StringId ver
|
||||
) -> std::pair<std::string_view, MatchFlags>
|
||||
|
@ -910,34 +959,77 @@ namespace mamba::solver::libsolv
|
|||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] auto pool_add_matchspec( //
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& ms
|
||||
) -> expected_t<solv::DependencyId>
|
||||
namespace
|
||||
{
|
||||
auto check_not_zero = [&](solv::DependencyId id) -> expected_t<solv::DependencyId>
|
||||
|
||||
template <typename Func>
|
||||
[[nodiscard]] auto check_dep_error(solv::DependencyId id, Func get_str)
|
||||
-> expected_t<solv::DependencyId>
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
return make_unexpected(
|
||||
fmt::format(R"(Invalid MatchSpec "{}")", ms.str()),
|
||||
fmt::format(R"(Invalid MatchSpec "{}")", get_str()),
|
||||
mamba_error_code::invalid_spec
|
||||
);
|
||||
}
|
||||
return id;
|
||||
};
|
||||
}
|
||||
|
||||
if (ms.is_simple())
|
||||
[[nodiscard]] auto pool_add_matchspec( //
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& ms,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::DependencyId>
|
||||
{
|
||||
if (parser == MatchSpecParser::Mixed)
|
||||
{
|
||||
return check_not_zero(pool.add_conda_dependency(ms.conda_build_form()));
|
||||
parser = ms.is_simple() ? MatchSpecParser::Libsolv : MatchSpecParser::Mamba;
|
||||
}
|
||||
const auto [first, second] = make_abused_namespace_dep_args(pool, ms.str());
|
||||
return check_not_zero(pool.add_dependency(first, REL_NAMESPACE, second));
|
||||
|
||||
if (parser == MatchSpecParser::Libsolv)
|
||||
{
|
||||
return check_dep_error(
|
||||
pool.add_legacy_conda_dependency(ms.conda_build_form()),
|
||||
[&]() { return ms.str(); }
|
||||
);
|
||||
}
|
||||
else if (parser == MatchSpecParser::Mamba)
|
||||
{
|
||||
const auto [first, second] = make_abused_namespace_dep_args(pool, ms.str());
|
||||
return check_dep_error(
|
||||
pool.add_dependency(first, REL_NAMESPACE, second),
|
||||
[&]() { return ms.str(); }
|
||||
);
|
||||
}
|
||||
|
||||
return make_unexpected("Invalid parser enum", mamba_error_code::incorrect_usage);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto pool_add_matchspec( //
|
||||
solv::ObjPool& pool,
|
||||
const char* ms_str,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::DependencyId>
|
||||
{
|
||||
// Avoid at all parsing Matchspecs when using Libsolv
|
||||
if (parser == MatchSpecParser::Libsolv)
|
||||
{
|
||||
return check_dep_error(pool.add_legacy_conda_dependency(ms_str), [&]() { return ms_str; });
|
||||
}
|
||||
|
||||
return specs::MatchSpec::parse(ms_str)
|
||||
.transform_error( //
|
||||
[](auto&& err) { return mamba_error(err.what(), mamba_error_code::invalid_spec); }
|
||||
)
|
||||
.and_then([&](specs::MatchSpec&& ms) { return pool_add_matchspec(pool, ms, parser); });
|
||||
}
|
||||
|
||||
auto pool_add_pin( //
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& pin
|
||||
const specs::MatchSpec& pin,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::ObjSolvableView>
|
||||
{
|
||||
// In libsolv, locking means that a package keeps the same state: if it is installed,
|
||||
|
@ -990,35 +1082,78 @@ namespace mamba::solver::libsolv
|
|||
return repo;
|
||||
}();
|
||||
|
||||
return pool_add_matchspec(pool, pin).transform(
|
||||
[&](solv::DependencyId cons)
|
||||
return pool_add_matchspec(pool, pin, parser)
|
||||
.transform(
|
||||
[&](solv::DependencyId cons)
|
||||
{
|
||||
// Add dummy solvable with a constraint on the pin (not installed if not
|
||||
// present)
|
||||
auto [cons_solv_id, cons_solv] = installed.add_solvable();
|
||||
const std::string cons_solv_name = fmt::format(
|
||||
"pin-{}",
|
||||
util::generate_random_alphanumeric_string(10)
|
||||
);
|
||||
cons_solv.set_name(cons_solv_name);
|
||||
cons_solv.set_version("1");
|
||||
|
||||
cons_solv.add_constraint(cons);
|
||||
|
||||
// Solvable need to provide itself
|
||||
cons_solv.add_self_provide();
|
||||
|
||||
// Even if we lock it, libsolv may still try to remove it with
|
||||
// `SOLVER_FLAG_ALLOW_UNINSTALL`, so we flag it as not a real package to filter
|
||||
// it out in the transaction
|
||||
cons_solv.set_type(solv::SolvableType::Pin);
|
||||
|
||||
// Necessary for attributes to be properly stored
|
||||
// TODO move this at the end of all job requests
|
||||
installed.internalize();
|
||||
|
||||
return cons_solv;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto pool_get_matchspec( //
|
||||
solv::ObjPoolView pool,
|
||||
solv::DependencyId dep
|
||||
) -> expected_t<specs::MatchSpec>
|
||||
{
|
||||
constexpr auto make_ms = [](const auto& str) -> expected_t<specs::MatchSpec>
|
||||
{
|
||||
return specs::MatchSpec::parse(str).transform_error(
|
||||
[](auto&& err) -> mamba_error
|
||||
{ return mamba_error(err.what(), mamba_error_code::invalid_spec); }
|
||||
);
|
||||
};
|
||||
|
||||
const auto dependency = pool.get_dependency(dep);
|
||||
|
||||
if (!dependency.has_value())
|
||||
{
|
||||
return make_ms(pool.get_string(dep));
|
||||
}
|
||||
|
||||
switch (dependency->flags())
|
||||
{
|
||||
case REL_CONDA:
|
||||
{
|
||||
// Add dummy solvable with a constraint on the pin (not installed if not
|
||||
// present)
|
||||
auto [cons_solv_id, cons_solv] = installed.add_solvable();
|
||||
const std::string cons_solv_name = fmt::format(
|
||||
"pin-{}",
|
||||
util::generate_random_alphanumeric_string(10)
|
||||
);
|
||||
cons_solv.set_name(cons_solv_name);
|
||||
cons_solv.set_version("1");
|
||||
|
||||
cons_solv.add_constraint(cons);
|
||||
|
||||
// Solvable need to provide itself
|
||||
cons_solv.add_self_provide();
|
||||
|
||||
// Even if we lock it, libsolv may still try to remove it with
|
||||
// `SOLVER_FLAG_ALLOW_UNINSTALL`, so we flag it as not a real package to filter
|
||||
// it out in the transaction
|
||||
cons_solv.set_type(solv::SolvableType::Pin);
|
||||
|
||||
// Necessary for attributes to be properly stored
|
||||
// TODO move this at the end of all job requests
|
||||
installed.internalize();
|
||||
|
||||
return cons_solv;
|
||||
return make_ms(pool.dependency_to_string(dep));
|
||||
}
|
||||
case REL_NAMESPACE:
|
||||
{
|
||||
auto [str, _flags] = get_abused_namespace_callback_args(
|
||||
pool,
|
||||
dependency->name(),
|
||||
dependency->version_range()
|
||||
);
|
||||
return make_ms(str);
|
||||
}
|
||||
}
|
||||
return make_unexpected(
|
||||
fmt::format("An unknown relation ({}) was added to libsolv", dependency->flags()),
|
||||
mamba_error_code::incorrect_usage
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1362,9 +1497,12 @@ namespace mamba::solver::libsolv
|
|||
return ms;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
add_reinstall_job(solv::ObjQueue& jobs, solv::ObjPool& pool, const specs::MatchSpec& ms)
|
||||
-> expected_t<void>
|
||||
[[nodiscard]] auto add_reinstall_job(
|
||||
solv::ObjQueue& jobs,
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& ms,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<void>
|
||||
{
|
||||
auto solvable = std::optional<solv::ObjSolvableViewConst>{};
|
||||
|
||||
|
@ -1397,8 +1535,8 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
|
||||
// We are not reinstalling but simply installing.
|
||||
return pool_add_matchspec(pool, ms).transform([&](auto id)
|
||||
{ jobs.push_back(SOLVER_INSTALL, id); });
|
||||
return pool_add_matchspec(pool, ms, parser)
|
||||
.transform([&](auto id) { jobs.push_back(SOLVER_INSTALL, id); });
|
||||
}
|
||||
|
||||
[[nodiscard]] auto has_installed_package( //
|
||||
|
@ -1422,25 +1560,29 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
|
||||
template <typename Job>
|
||||
[[nodiscard]] auto
|
||||
add_job(const Job& job, solv::ObjQueue& raw_jobs, solv::ObjPool& pool, bool force_reinstall)
|
||||
-> expected_t<void>
|
||||
[[nodiscard]] auto add_job(
|
||||
const Job& job,
|
||||
solv::ObjQueue& raw_jobs,
|
||||
solv::ObjPool& pool,
|
||||
bool force_reinstall,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<void>
|
||||
{
|
||||
if constexpr (std::is_same_v<Job, Request::Install>)
|
||||
{
|
||||
if (force_reinstall)
|
||||
{
|
||||
return add_reinstall_job(raw_jobs, pool, job.spec);
|
||||
return add_reinstall_job(raw_jobs, pool, job.spec, parser);
|
||||
}
|
||||
else
|
||||
{
|
||||
return pool_add_matchspec(pool, job.spec)
|
||||
return pool_add_matchspec(pool, job.spec, parser)
|
||||
.transform([&](auto id) { raw_jobs.push_back(SOLVER_INSTALL, id); });
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<Job, Request::Remove>)
|
||||
{
|
||||
return pool_add_matchspec(pool, job.spec)
|
||||
return pool_add_matchspec(pool, job.spec, parser)
|
||||
.transform(
|
||||
[&](auto id)
|
||||
{
|
||||
|
@ -1453,7 +1595,7 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
if constexpr (std::is_same_v<Job, Request::Update>)
|
||||
{
|
||||
return pool_add_matchspec(pool, job.spec)
|
||||
return pool_add_matchspec(pool, job.spec, parser)
|
||||
.transform(
|
||||
[&](auto id)
|
||||
{
|
||||
|
@ -1504,17 +1646,18 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
if constexpr (std::is_same_v<Job, Request::Freeze>)
|
||||
{
|
||||
return pool_add_matchspec(pool, job.spec)
|
||||
return pool_add_matchspec(pool, job.spec, parser)
|
||||
.transform([&](auto id) { raw_jobs.push_back(SOLVER_LOCK, id); });
|
||||
}
|
||||
if constexpr (std::is_same_v<Job, Request::Keep>)
|
||||
{
|
||||
raw_jobs.push_back(SOLVER_USERINSTALLED, pool_add_matchspec(pool, job.spec).value());
|
||||
return {};
|
||||
return pool_add_matchspec(pool, job.spec, parser)
|
||||
.transform([&](auto id) { raw_jobs.push_back(SOLVER_USERINSTALLED, id); });
|
||||
}
|
||||
if constexpr (std::is_same_v<Job, Request::Pin>)
|
||||
{
|
||||
return pool_add_pin(pool, job.spec)
|
||||
// WARNING pins are not working with namespace dependencies so far
|
||||
return pool_add_pin(pool, job.spec, MatchSpecParser::Libsolv)
|
||||
.transform(
|
||||
[&](solv::ObjSolvableView pin_solv)
|
||||
{
|
||||
|
@ -1536,7 +1679,8 @@ namespace mamba::solver::libsolv
|
|||
auto request_to_decision_queue( //
|
||||
const Request& request,
|
||||
solv::ObjPool& pool,
|
||||
bool force_reinstall
|
||||
bool force_reinstall,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::ObjQueue>
|
||||
{
|
||||
auto solv_jobs = solv::ObjQueue();
|
||||
|
@ -1549,7 +1693,7 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(job)>, Request::Pin>)
|
||||
{
|
||||
return add_job(job, solv_jobs, pool, force_reinstall);
|
||||
return add_job(job, solv_jobs, pool, force_reinstall, parser);
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
@ -1570,7 +1714,7 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
if constexpr (!std::is_same_v<std::decay_t<decltype(job)>, Request::Pin>)
|
||||
{
|
||||
return add_job(job, solv_jobs, pool, force_reinstall);
|
||||
return add_job(job, solv_jobs, pool, force_reinstall, parser);
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
|
|
@ -36,7 +36,12 @@ namespace mamba::fs
|
|||
|
||||
namespace mamba::solver::libsolv
|
||||
{
|
||||
void set_solvable(solv::ObjPool& pool, solv::ObjSolvableView solv, const specs::PackageInfo& pkg);
|
||||
void set_solvable(
|
||||
solv::ObjPool& pool,
|
||||
solv::ObjSolvableView solv,
|
||||
const specs::PackageInfo& pkg,
|
||||
MatchSpecParser parser
|
||||
);
|
||||
|
||||
auto make_package_info(const solv::ObjPool& pool, solv::ObjSolvableViewConst s)
|
||||
-> specs::PackageInfo;
|
||||
|
@ -55,6 +60,7 @@ namespace mamba::solver::libsolv
|
|||
const std::string& repo_url,
|
||||
const std::string& channel_id,
|
||||
PackageTypes types,
|
||||
MatchSpecParser parser,
|
||||
bool verify_artifacts
|
||||
) -> expected_t<solv::ObjRepoView>;
|
||||
|
||||
|
@ -96,21 +102,34 @@ namespace mamba::solver::libsolv
|
|||
* callback to pass our own information.
|
||||
*/
|
||||
[[nodiscard]] auto get_abused_namespace_callback_args( //
|
||||
solv::ObjPoolView& pool,
|
||||
solv::ObjPoolView pool,
|
||||
solv::StringId first,
|
||||
solv::StringId second
|
||||
) -> std::pair<std::string_view, MatchFlags>;
|
||||
|
||||
[[nodiscard]] auto pool_add_matchspec( //
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& ms
|
||||
const specs::MatchSpec& ms,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::DependencyId>;
|
||||
|
||||
[[nodiscard]] auto pool_add_matchspec( //
|
||||
solv::ObjPool& pool,
|
||||
const char* ms,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::DependencyId>;
|
||||
|
||||
[[nodiscard]] auto pool_add_pin( //
|
||||
solv::ObjPool& pool,
|
||||
const specs::MatchSpec& pin_ms
|
||||
const specs::MatchSpec& pin_ms,
|
||||
MatchSpecParser parser
|
||||
) -> expected_t<solv::ObjSolvableView>;
|
||||
|
||||
[[nodiscard]] auto pool_get_matchspec( //
|
||||
solv::ObjPoolView pool,
|
||||
solv::DependencyId dep
|
||||
) -> expected_t<specs::MatchSpec>;
|
||||
|
||||
[[nodiscard]] auto transaction_to_solution_all( //
|
||||
const solv::ObjPool& pool,
|
||||
const solv::ObjTransaction& trans
|
||||
|
@ -149,8 +168,12 @@ namespace mamba::solver::libsolv
|
|||
std::string_view noarch_type
|
||||
) -> Solution;
|
||||
|
||||
[[nodiscard]] auto
|
||||
request_to_decision_queue(const Request& request, solv::ObjPool& pool, bool force_reinstall)
|
||||
-> expected_t<solv::ObjQueue>;
|
||||
[[nodiscard]] auto request_to_decision_queue(
|
||||
const Request& request,
|
||||
solv::ObjPool& pool,
|
||||
bool force_reinstall,
|
||||
MatchSpecParser parser
|
||||
|
||||
) -> expected_t<solv::ObjQueue>;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,12 @@ namespace mamba::solver::libsolv
|
|||
auto& pool = Database::Impl::get(mpool);
|
||||
const auto& flags = request.flags;
|
||||
|
||||
return solver::libsolv::request_to_decision_queue(request, pool, flags.force_reinstall)
|
||||
return solver::libsolv::request_to_decision_queue(
|
||||
request,
|
||||
pool,
|
||||
flags.force_reinstall,
|
||||
MatchSpecParser::Mixed
|
||||
)
|
||||
.transform(
|
||||
[&](auto&& jobs) -> Outcome
|
||||
{
|
||||
|
|
|
@ -338,7 +338,15 @@ namespace mamba::solver::libsolv
|
|||
void ProblemsGraphCreator::parse_problems()
|
||||
{
|
||||
// TODO Throwing error for now but we should use expected in UnSolvable API
|
||||
auto make_match_spec = [&](std::string_view str) -> specs::MatchSpec
|
||||
auto make_match_spec = [&](solv::DependencyId dep) -> specs::MatchSpec
|
||||
{
|
||||
return pool_get_matchspec(m_pool.view(), dep)
|
||||
.or_else([](auto&& err) { throw std::move(err); })
|
||||
.value();
|
||||
};
|
||||
// Re-create a MatchSpec from string. This is not the prefer way, as libsolv
|
||||
// representation may not be the same as ours.
|
||||
auto make_match_spec_str = [&](std::string_view str) -> specs::MatchSpec
|
||||
{
|
||||
return specs::MatchSpec::parse(str)
|
||||
.or_else([](specs::ParseError&& err) { throw std::move(err); })
|
||||
|
@ -392,9 +400,9 @@ namespace mamba::solver::libsolv
|
|||
);
|
||||
node_id cons_id = add_solvable(
|
||||
problem.dep_id,
|
||||
ConstraintNode{ make_match_spec(dep.value()) }
|
||||
ConstraintNode{ make_match_spec(problem.dep_id) }
|
||||
);
|
||||
auto edge = make_match_spec(dep.value());
|
||||
auto edge = make_match_spec(problem.dep_id);
|
||||
m_graph.add_edge(src_id, cons_id, std::move(edge));
|
||||
add_conflict(cons_id, tgt_id);
|
||||
break;
|
||||
|
@ -414,7 +422,7 @@ namespace mamba::solver::libsolv
|
|||
problem.source_id,
|
||||
PackageNode{ fixup_pkg(std::move(source).value()) }
|
||||
);
|
||||
auto edge = make_match_spec(dep.value());
|
||||
auto edge = make_match_spec(problem.dep_id);
|
||||
bool added = add_expanded_deps_edges(src_id, problem.dep_id, edge);
|
||||
if (!added)
|
||||
{
|
||||
|
@ -433,7 +441,7 @@ namespace mamba::solver::libsolv
|
|||
warn_unexpected_problem(problem);
|
||||
break;
|
||||
}
|
||||
auto edge = make_match_spec(dep.value());
|
||||
auto edge = make_match_spec(problem.dep_id);
|
||||
bool added = add_expanded_deps_edges(m_root_node, problem.dep_id, edge);
|
||||
if (!added)
|
||||
{
|
||||
|
@ -453,10 +461,10 @@ namespace mamba::solver::libsolv
|
|||
warn_unexpected_problem(problem);
|
||||
break;
|
||||
}
|
||||
auto edge = make_match_spec(dep.value());
|
||||
auto edge = make_match_spec(problem.dep_id);
|
||||
node_id dep_id = add_solvable(
|
||||
problem.dep_id,
|
||||
UnresolvedDependencyNode{ make_match_spec(dep.value()) }
|
||||
UnresolvedDependencyNode{ make_match_spec(problem.dep_id) }
|
||||
);
|
||||
m_graph.add_edge(m_root_node, dep_id, std::move(edge));
|
||||
break;
|
||||
|
@ -472,14 +480,14 @@ namespace mamba::solver::libsolv
|
|||
warn_unexpected_problem(problem);
|
||||
break;
|
||||
}
|
||||
auto edge = make_match_spec(dep.value());
|
||||
auto edge = make_match_spec(problem.dep_id);
|
||||
node_id src_id = add_solvable(
|
||||
problem.source_id,
|
||||
PackageNode{ fixup_pkg(std::move(source).value()) }
|
||||
);
|
||||
node_id dep_id = add_solvable(
|
||||
problem.dep_id,
|
||||
UnresolvedDependencyNode{ make_match_spec(dep.value()) }
|
||||
UnresolvedDependencyNode{ make_match_spec(problem.dep_id) }
|
||||
);
|
||||
m_graph.add_edge(src_id, dep_id, std::move(edge));
|
||||
break;
|
||||
|
@ -522,7 +530,7 @@ namespace mamba::solver::libsolv
|
|||
// 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.
|
||||
auto edge = make_match_spec(source.value().name);
|
||||
auto edge = make_match_spec_str(source.value().name);
|
||||
// The package cannot exist without its name in the pool
|
||||
assert(m_pool.find_string(edge.name().str()).has_value());
|
||||
const auto dep_id = m_pool.find_string(edge.name().str()).value();
|
||||
|
|
|
@ -1038,7 +1038,7 @@ namespace mamba::specs
|
|||
&& !track_features().has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MatchSpec::is_only_package_name() const -> bool
|
||||
auto MatchSpec::is_only_package_name() const -> bool
|
||||
{
|
||||
return name().is_exact() //
|
||||
&& version().is_explicitly_free() //
|
||||
|
@ -1046,6 +1046,13 @@ namespace mamba::specs
|
|||
&& is_simple();
|
||||
}
|
||||
|
||||
auto MatchSpec::to_named_spec() const -> MatchSpec
|
||||
{
|
||||
auto out = MatchSpec();
|
||||
out.m_name = this->m_name;
|
||||
return out;
|
||||
}
|
||||
|
||||
auto MatchSpec::contains_except_channel(const PackageInfo& pkg) const -> bool
|
||||
{
|
||||
struct Pkg
|
||||
|
|
|
@ -1006,6 +1006,56 @@ namespace
|
|||
REQUIRE(std::holds_alternative<Solution::Install>(bar_actions.front()));
|
||||
REQUIRE(std::get<Solution::Install>(bar_actions.front()).install.version == "1.0");
|
||||
}
|
||||
|
||||
SECTION("Unneeded pins are not installed")
|
||||
{
|
||||
auto pkg1 = PackageInfo("foo");
|
||||
pkg1.version = "1.0";
|
||||
auto pkg2 = PackageInfo("bar");
|
||||
pkg2.version = "1.0";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg1, pkg2 });
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "bar"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
const auto& solution = std::get<Solution>(outcome.value());
|
||||
|
||||
const auto foo_actions = find_actions_with_name(solution, "foo");
|
||||
REQUIRE(foo_actions.empty());
|
||||
|
||||
const auto bar_actions = find_actions_with_name(solution, "bar");
|
||||
REQUIRE(bar_actions.size() == 1);
|
||||
}
|
||||
|
||||
SECTION("Invalid pins are not an error")
|
||||
{
|
||||
auto pkg = PackageInfo("bar");
|
||||
pkg.version = "1.0";
|
||||
|
||||
db.add_repo_from_packages(std::array{ pkg });
|
||||
|
||||
auto request = Request{
|
||||
/* .flags= */ {},
|
||||
/* .jobs= */ { Request::Pin{ "foo=1.0"_ms }, Request::Install{ "bar"_ms } },
|
||||
};
|
||||
const auto outcome = libsolv::Solver().solve(db, request);
|
||||
|
||||
REQUIRE(outcome.has_value());
|
||||
REQUIRE(std::holds_alternative<Solution>(outcome.value()));
|
||||
const auto& solution = std::get<Solution>(outcome.value());
|
||||
|
||||
const auto foo_actions = find_actions_with_name(solution, "foo");
|
||||
REQUIRE(foo_actions.empty());
|
||||
|
||||
const auto bar_actions = find_actions_with_name(solution, "bar");
|
||||
REQUIRE(bar_actions.size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Handle complex matchspecs")
|
||||
|
|
|
@ -32,6 +32,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "*");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("xtensor==0.12.3")
|
||||
|
@ -40,6 +41,7 @@ namespace
|
|||
REQUIRE(ms.name().str() == "xtensor");
|
||||
REQUIRE(ms.version().str() == "==0.12.3");
|
||||
REQUIRE(ms.str() == "xtensor==0.12.3");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("xtensor >= 0.12.3")
|
||||
|
@ -50,6 +52,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "xtensor>=0.12.3");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("python > 3.11")
|
||||
|
@ -60,6 +63,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "python>3.11");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("numpy < 2.0")
|
||||
|
@ -70,6 +74,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "numpy<2.0");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("pytorch-cpu = 1.13.0")
|
||||
|
@ -80,6 +85,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "pytorch-cpu=1.13.0");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("scipy >= 1.5.0, < 2.0.0")
|
||||
|
@ -90,6 +96,7 @@ namespace
|
|||
REQUIRE(ms.build_string().is_explicitly_free());
|
||||
REQUIRE(ms.build_number().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "scipy[version=\">=1.5.0,<2.0.0\"]");
|
||||
REQUIRE_FALSE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("scikit-learn >1.0.0")
|
||||
|
@ -194,6 +201,7 @@ namespace
|
|||
REQUIRE(ms.name().str() == "ipykernel");
|
||||
REQUIRE(ms.version().is_explicitly_free());
|
||||
REQUIRE(ms.str() == "ipykernel");
|
||||
REQUIRE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("ipykernel ")
|
||||
|
@ -201,6 +209,7 @@ namespace
|
|||
auto ms = MatchSpec::parse("ipykernel ").value();
|
||||
REQUIRE(ms.name().str() == "ipykernel");
|
||||
REQUIRE(ms.version().is_explicitly_free());
|
||||
REQUIRE(ms.is_only_package_name());
|
||||
}
|
||||
|
||||
SECTION("disperse=v0.9.24")
|
||||
|
@ -793,6 +802,28 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MatchSpec::to_named_spec", "[mamba::specs][mamba::specs::MatchSpec]")
|
||||
{
|
||||
SECTION("Alrealy name only")
|
||||
{
|
||||
const auto str = GENERATE("foo", "foo ");
|
||||
const auto ms = MatchSpec::parse(str).value();
|
||||
const auto ms_named = ms.to_named_spec();
|
||||
REQUIRE(ms == ms_named);
|
||||
}
|
||||
|
||||
SECTION("With more restrictions")
|
||||
{
|
||||
const auto str = GENERATE("foo>1.0", "foo =*", "foo=3=bld");
|
||||
const auto ms = MatchSpec::parse(str).value();
|
||||
const auto ms_named = ms.to_named_spec();
|
||||
REQUIRE(ms_named.name() == ms.name());
|
||||
REQUIRE(ms_named.version().is_explicitly_free());
|
||||
REQUIRE(ms_named.build_string().is_explicitly_free());
|
||||
REQUIRE(ms_named.build_number().is_explicitly_free());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MatchSpec::contains", "[mamba::specs][mamba::specs::MatchSpec]")
|
||||
{
|
||||
// Note that tests for individual ``contains`` functions (``VersionSpec::contains``,
|
||||
|
|
Loading…
Reference in New Issue