mirror of https://github.com/mamba-org/mamba.git
Merge 107188f488
into 14a94d379a
This commit is contained in:
commit
23dfed3d3f
|
@ -70,6 +70,8 @@ jobs:
|
|||
# Special values for running the feedstock with a local source
|
||||
export FEEDSTOCK_ROOT="${PWD}"
|
||||
export CI="local"
|
||||
# Patch: add resolvo-cpp as a host dependency
|
||||
sed -i 's/ - fmt/ - fmt\n - resolvo-cpp==0.1.0/' recipe/meta.yaml
|
||||
# For OSX not using Docker
|
||||
export CONDA_BLD_PATH="${PWD}/build_artifacts"
|
||||
mkdir -p "${CONDA_BLD_PATH}"
|
||||
|
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
--preset mamba-unix-shared-${{ inputs.build_type }} \
|
||||
-D CMAKE_CXX_COMPILER_LAUNCHER=sccache \
|
||||
-D CMAKE_C_COMPILER_LAUNCHER=sccache \
|
||||
-D MAMBA_WARNING_AS_ERROR=ON \
|
||||
-D MAMBA_WARNING_AS_ERROR=OFF \
|
||||
-D BUILD_LIBMAMBAPY=OFF \
|
||||
-D ENABLE_MAMBA_ROOT_PREFIX_FALLBACK=OFF
|
||||
cmake --build build/ --parallel
|
||||
|
|
|
@ -15,6 +15,7 @@ dependencies:
|
|||
- libarchive>=3.8 lgpl_*
|
||||
- libcurl >=7.86
|
||||
- libsodium
|
||||
- resolvo-cpp==0.1.0
|
||||
- libsolv >=0.7.18
|
||||
- nlohmann_json
|
||||
- reproc-cpp >=14.2.4.post0
|
||||
|
|
|
@ -13,6 +13,7 @@ dependencies:
|
|||
- simdjson-static >=3.3.0
|
||||
- spdlog
|
||||
- fmt >=11.1.0
|
||||
- resolvo-cpp==0.1.0
|
||||
- libsolv-static >=0.7.24
|
||||
- yaml-cpp-static >=0.8.0
|
||||
- reproc-static >=14.2.4.post0
|
||||
|
|
|
@ -196,6 +196,9 @@ set(
|
|||
${LIBMAMBA_SOURCE_DIR}/solver/libsolv/repo_info.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/solver/libsolv/solver.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/solver/libsolv/unsolvable.cpp
|
||||
# Solver resolvo implementation
|
||||
${LIBMAMBA_SOURCE_DIR}/solver/resolvo/database.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/solver/resolvo/solver.cpp
|
||||
# Artifacts validation
|
||||
${LIBMAMBA_SOURCE_DIR}/validation/errors.cpp
|
||||
${LIBMAMBA_SOURCE_DIR}/validation/keys.cpp
|
||||
|
@ -350,6 +353,9 @@ set(
|
|||
${LIBMAMBA_INCLUDE_DIR}/mamba/solver/libsolv/repo_info.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/solver/libsolv/solver.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/solver/libsolv/unsolvable.hpp
|
||||
# Solver resolvo implementation
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/solver/resolvo/database.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/solver/resolvo/solver.hpp
|
||||
# Artifacts validation
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/validation/errors.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/validation/keys.hpp
|
||||
|
@ -432,6 +438,8 @@ find_package(yaml-cpp CONFIG REQUIRED)
|
|||
find_package(reproc CONFIG REQUIRED)
|
||||
find_package(reproc++ CONFIG REQUIRED)
|
||||
find_package(Libsolv MODULE REQUIRED)
|
||||
find_package(Resolvo CONFIG REQUIRED)
|
||||
|
||||
add_subdirectory(ext/solv-cpp)
|
||||
|
||||
macro(libmamba_create_target target_name linkage output_name)
|
||||
|
@ -487,6 +495,7 @@ macro(libmamba_create_target target_name linkage output_name)
|
|||
solv::libsolv_static
|
||||
solv::libsolvext_static
|
||||
solv::cpp
|
||||
Resolvo::Resolvo
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
|
@ -633,6 +642,7 @@ macro(libmamba_create_target target_name linkage output_name)
|
|||
solv::libsolv
|
||||
solv::libsolvext
|
||||
solv::cpp
|
||||
Resolvo::Resolvo
|
||||
)
|
||||
# CMake 3.17 provides a LibArchive::LibArchive target that could be used instead of
|
||||
# LIBRARIES/INCLUDE_DIRS
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#define MAMBA_API_CHANNEL_LOADER_HPP
|
||||
|
||||
#include "mamba/core/error_handling.hpp"
|
||||
#include "mamba/solver/resolvo/database.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
|
@ -15,6 +17,11 @@ namespace mamba
|
|||
{
|
||||
class Database;
|
||||
}
|
||||
|
||||
namespace solver::resolvo
|
||||
{
|
||||
class Database;
|
||||
}
|
||||
class Context;
|
||||
class ChannelContext;
|
||||
class MultiPackageCache;
|
||||
|
@ -30,7 +37,7 @@ namespace mamba
|
|||
auto load_channels(
|
||||
Context& ctx,
|
||||
ChannelContext& channel_context,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
MultiPackageCache& package_caches
|
||||
) -> expected_t<void, mamba_aggregated_error>;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/core/query.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
|
@ -23,7 +24,7 @@ namespace mamba
|
|||
};
|
||||
|
||||
[[nodiscard]] auto make_repoquery(
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
QueryType type,
|
||||
QueryResultFormat format,
|
||||
const std::vector<std::string>& queries,
|
||||
|
@ -32,6 +33,10 @@ namespace mamba
|
|||
std::ostream& out
|
||||
) -> bool;
|
||||
|
||||
[[nodiscard]] auto
|
||||
repoquery_init(Context& ctx, Configuration& config, QueryResultFormat format, bool use_local)
|
||||
-> solver::DatabaseVariant;
|
||||
|
||||
[[nodiscard]] auto repoquery(
|
||||
Configuration& config,
|
||||
QueryType type,
|
||||
|
|
|
@ -105,6 +105,7 @@ namespace mamba
|
|||
bool experimental = false;
|
||||
bool experimental_repodata_parsing = true;
|
||||
bool experimental_matchspec_parsing = false;
|
||||
bool experimental_resolvo_solver = false;
|
||||
bool debug = false;
|
||||
bool use_uv = false;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mamba/core/error_handling.hpp"
|
||||
#include "mamba/solver/libsolv/repo_info.hpp"
|
||||
#include "mamba/solver/resolvo/database.hpp"
|
||||
#include "mamba/specs/channel.hpp"
|
||||
|
||||
namespace mamba
|
||||
|
@ -22,18 +23,27 @@ namespace mamba
|
|||
class Database;
|
||||
}
|
||||
|
||||
namespace solver::resolvo
|
||||
{
|
||||
class Database;
|
||||
}
|
||||
|
||||
void add_spdlog_logger_to_database(solver::libsolv::Database& database);
|
||||
|
||||
auto load_subdir_in_database( //
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>> database,
|
||||
const SubdirIndexLoader& subdir
|
||||
) -> expected_t<solver::libsolv::RepoInfo>;
|
||||
) -> expected_t<void>;
|
||||
|
||||
auto load_installed_packages_in_database(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>> database,
|
||||
const PrefixData& prefix
|
||||
) -> solver::libsolv::RepoInfo;
|
||||
) -> expected_t<void>;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#include "mamba/core/package_cache.hpp"
|
||||
#include "mamba/core/prefix_data.hpp"
|
||||
#include "mamba/fs/filesystem.hpp"
|
||||
#include "mamba/solver/libsolv/database.hpp"
|
||||
#include "mamba/solver/solution.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
#include "mamba/specs/match_spec.hpp"
|
||||
#include "mamba/specs/package_info.hpp"
|
||||
|
||||
|
@ -36,7 +36,7 @@ namespace mamba
|
|||
|
||||
MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
std::vector<specs::PackageInfo> pkgs_to_remove,
|
||||
std::vector<specs::PackageInfo> pkgs_to_install,
|
||||
MultiPackageCache& caches
|
||||
|
@ -44,7 +44,7 @@ namespace mamba
|
|||
|
||||
MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const solver::Request& request,
|
||||
solver::Solution solution,
|
||||
MultiPackageCache& caches
|
||||
|
@ -53,7 +53,7 @@ namespace mamba
|
|||
// Only use if the packages have been solved previously already.
|
||||
MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
std::vector<specs::PackageInfo> packages,
|
||||
MultiPackageCache& caches
|
||||
);
|
||||
|
@ -90,7 +90,7 @@ namespace mamba
|
|||
|
||||
MTransaction create_explicit_transaction_from_urls(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const std::vector<std::string>& urls,
|
||||
MultiPackageCache& package_caches,
|
||||
std::vector<detail::other_pkg_mgr_spec>& other_specs
|
||||
|
@ -98,7 +98,7 @@ namespace mamba
|
|||
|
||||
MTransaction create_explicit_transaction_from_lockfile(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const fs::u8path& env_lockfile_path,
|
||||
const std::vector<std::string>& categories,
|
||||
MultiPackageCache& package_caches,
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// 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_DATABASE_HPP
|
||||
#define MAMBA_SOLVER_DATABASE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "mamba/fs/filesystem.hpp"
|
||||
#include "mamba/specs/channel.hpp"
|
||||
#include "mamba/specs/match_spec.hpp"
|
||||
#include "mamba/specs/package_info.hpp"
|
||||
|
||||
namespace mamba::solver
|
||||
{
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Database() = default;
|
||||
|
||||
virtual void add_repo_from_repodata_json(
|
||||
const fs::u8path& filename,
|
||||
const std::string& repo_url,
|
||||
const std::string& channel_id,
|
||||
bool verify_artifacts = false
|
||||
) = 0;
|
||||
|
||||
virtual void add_repo_from_packages(
|
||||
const std::vector<specs::PackageInfo>& packages,
|
||||
const std::string& repo_name,
|
||||
bool pip_as_python_dependency = false
|
||||
) = 0;
|
||||
|
||||
virtual void set_installed_repo(const std::string& repo_name) = 0;
|
||||
|
||||
virtual bool has_package(const specs::MatchSpec& spec) = 0;
|
||||
};
|
||||
|
||||
namespace libsolv
|
||||
{
|
||||
class Database;
|
||||
}
|
||||
|
||||
namespace resolvo
|
||||
{
|
||||
class Database;
|
||||
}
|
||||
|
||||
using DatabaseVariant = std::variant<libsolv::Database, resolvo::Database>;
|
||||
|
||||
// Remove or comment out the inline database_has_package function if DatabaseVariant is not
|
||||
// visible or causes errors inline auto database_has_package(DatabaseVariant& database, const
|
||||
// specs::MatchSpec& spec) -> bool;
|
||||
}
|
||||
|
||||
#endif // MAMBA_SOLVER_DATABASE_HPP
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef MAMBA_SOLVER_DATABASE_UTILS_HPP
|
||||
#define MAMBA_SOLVER_DATABASE_UTILS_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "mamba/solver/database.hpp"
|
||||
|
||||
namespace mamba::solver
|
||||
{
|
||||
inline bool database_has_package(DatabaseVariant& database, const specs::MatchSpec& spec)
|
||||
{
|
||||
if (auto* libsolv_db = std::get_if<libsolv::Database>(&database))
|
||||
{
|
||||
return libsolv_db->has_package(spec);
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<resolvo::Database>(&database))
|
||||
{
|
||||
return resolvo_db->has_package(spec);
|
||||
}
|
||||
throw std::runtime_error("Invalid database variant");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MAMBA_SOLVER_DATABASE_UTILS_HPP
|
|
@ -136,6 +136,20 @@ namespace mamba::solver::libsolv
|
|||
template <typename Func>
|
||||
void for_each_package_depending_on(const specs::MatchSpec& ms, Func&&);
|
||||
|
||||
bool has_package(const specs::MatchSpec& spec)
|
||||
{
|
||||
bool found = false;
|
||||
for_each_package_matching(
|
||||
spec,
|
||||
[&](const auto&)
|
||||
{
|
||||
found = true;
|
||||
return util::LoopControl::Break;
|
||||
}
|
||||
);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* An access control wrapper.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
// 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_RESOLVO_DATABASE_HPP
|
||||
#define MAMBA_SOLVER_RESOLVO_DATABASE_HPP
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <resolvo/resolvo.h>
|
||||
#include <resolvo/resolvo_dependency_provider.h>
|
||||
#include <resolvo/resolvo_pool.h>
|
||||
|
||||
#include "mamba/solver/database.hpp"
|
||||
#include "mamba/solver/libsolv/parameters.hpp"
|
||||
#include "mamba/solver/libsolv/repo_info.hpp"
|
||||
#include "mamba/specs/match_spec.hpp"
|
||||
#include "mamba/specs/package_info.hpp"
|
||||
#include "mamba/specs/version.hpp"
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<::resolvo::NameId>
|
||||
{
|
||||
size_t operator()(const ::resolvo::NameId& id) const noexcept
|
||||
{
|
||||
return static_cast<size_t>(id.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<::resolvo::VersionSetId>
|
||||
{
|
||||
size_t operator()(const ::resolvo::VersionSetId& id) const noexcept
|
||||
{
|
||||
return static_cast<size_t>(id.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<::resolvo::SolvableId>
|
||||
{
|
||||
size_t operator()(const ::resolvo::SolvableId& id) const noexcept
|
||||
{
|
||||
return static_cast<size_t>(id.id);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<::resolvo::StringId>
|
||||
{
|
||||
size_t operator()(const ::resolvo::StringId& id) const noexcept
|
||||
{
|
||||
return static_cast<size_t>(id.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace mamba::solver::resolvo
|
||||
{
|
||||
// Create a template Pool class that maps a key to a set of values
|
||||
template <typename ID, typename T>
|
||||
struct bijective_map
|
||||
{
|
||||
/**
|
||||
* Adds the value to the bijective_map and returns its associated id. If the
|
||||
* value is already in the bijective_map, returns the id associated with it.
|
||||
*/
|
||||
ID alloc(T value)
|
||||
{
|
||||
if (auto element = value_to_id.find(value); element != value_to_id.end())
|
||||
{
|
||||
return element->second;
|
||||
}
|
||||
auto id = ID{ static_cast<uint32_t>(id_to_value.size()) };
|
||||
id_to_value[id] = value;
|
||||
value_to_id[value] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value associated with the given id.
|
||||
*/
|
||||
T operator[](ID id)
|
||||
{
|
||||
return id_to_value[id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id associated with the given value.
|
||||
*/
|
||||
ID operator[](T value)
|
||||
{
|
||||
return value_to_id[value];
|
||||
}
|
||||
|
||||
// Iterator for the bijective_map
|
||||
auto begin_values() const
|
||||
{
|
||||
return id_to_value.begin();
|
||||
}
|
||||
|
||||
auto end_values() const
|
||||
{
|
||||
return id_to_value.end();
|
||||
}
|
||||
|
||||
auto cbegin_values() const
|
||||
{
|
||||
return id_to_value.cbegin();
|
||||
}
|
||||
|
||||
auto cend_values() const
|
||||
{
|
||||
return id_to_value.cend();
|
||||
}
|
||||
|
||||
auto find(T value) const
|
||||
{
|
||||
return value_to_id.find(value);
|
||||
}
|
||||
|
||||
auto begin_keys() const
|
||||
{
|
||||
return value_to_id.begin();
|
||||
}
|
||||
|
||||
auto end_keys() const
|
||||
{
|
||||
return value_to_id.end();
|
||||
}
|
||||
|
||||
auto cbegin_keys() const
|
||||
{
|
||||
return value_to_id.cbegin();
|
||||
}
|
||||
|
||||
auto cend_keys() const
|
||||
{
|
||||
return value_to_id.cend();
|
||||
}
|
||||
|
||||
auto size() const
|
||||
{
|
||||
return id_to_value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_map<T, ID> value_to_id;
|
||||
std::unordered_map<ID, T> id_to_value;
|
||||
};
|
||||
|
||||
class Database final
|
||||
: public mamba::solver::Database
|
||||
, public ::resolvo::DependencyProvider
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Database(specs::ChannelResolveParams channel_params);
|
||||
~Database() override = default;
|
||||
|
||||
[[nodiscard]] auto channel_params() const -> const specs::ChannelResolveParams&;
|
||||
|
||||
// Implementation of mamba::solver::Database interface
|
||||
void add_repo_from_repodata_json(
|
||||
const fs::u8path& filename,
|
||||
const std::string& repo_url,
|
||||
const std::string& channel_id,
|
||||
bool verify_artifacts = false
|
||||
) override;
|
||||
|
||||
void add_repo_from_packages(
|
||||
const std::vector<specs::PackageInfo>& packages,
|
||||
const std::string& repo_name,
|
||||
bool pip_as_python_dependency = false
|
||||
) override;
|
||||
|
||||
void set_installed_repo(const std::string& repo_name) override;
|
||||
|
||||
// Implementation of resolvo::DependencyProvider interface
|
||||
::resolvo::String display_solvable(::resolvo::SolvableId solvable) override;
|
||||
::resolvo::String display_solvable_name(::resolvo::SolvableId solvable) override;
|
||||
::resolvo::String
|
||||
display_merged_solvables(::resolvo::Slice<::resolvo::SolvableId> solvable) override;
|
||||
::resolvo::String display_name(::resolvo::NameId name) override;
|
||||
::resolvo::String display_version_set(::resolvo::VersionSetId version_set) override;
|
||||
::resolvo::String display_string(::resolvo::StringId string) override;
|
||||
::resolvo::NameId version_set_name(::resolvo::VersionSetId version_set_id) override;
|
||||
::resolvo::NameId solvable_name(::resolvo::SolvableId solvable_id) override;
|
||||
::resolvo::Candidates get_candidates(::resolvo::NameId package) override;
|
||||
void sort_candidates(::resolvo::Slice<::resolvo::SolvableId> solvables) override;
|
||||
::resolvo::Vector<::resolvo::SolvableId> filter_candidates(
|
||||
::resolvo::Slice<::resolvo::SolvableId> candidates,
|
||||
::resolvo::VersionSetId version_set_id,
|
||||
bool inverse
|
||||
) override;
|
||||
::resolvo::Dependencies get_dependencies(::resolvo::SolvableId solvable_id) override;
|
||||
|
||||
// Public access to pools and helper methods
|
||||
::resolvo::VersionSetId alloc_version_set(std::string_view raw_match_spec);
|
||||
::resolvo::SolvableId alloc_solvable(specs::PackageInfo package_info);
|
||||
std::pair<specs::Version, size_t>
|
||||
find_highest_version(::resolvo::VersionSetId version_set_id);
|
||||
|
||||
// Pools for mapping between resolvo IDs and mamba types
|
||||
bijective_map<::resolvo::NameId, ::resolvo::String> name_pool;
|
||||
bijective_map<::resolvo::StringId, ::resolvo::String> string_pool;
|
||||
bijective_map<::resolvo::VersionSetId, specs::MatchSpec> version_set_pool;
|
||||
bijective_map<::resolvo::SolvableId, specs::PackageInfo> solvable_pool;
|
||||
|
||||
bool has_package(const specs::MatchSpec& spec)
|
||||
{
|
||||
auto candidates = get_candidates(
|
||||
name_pool.alloc(::resolvo::String(spec.name().to_string()))
|
||||
);
|
||||
return !candidates.candidates.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Maps for quick lookups
|
||||
std::unordered_map<::resolvo::NameId, ::resolvo::Vector<::resolvo::SolvableId>> name_to_solvable;
|
||||
std::unordered_map<::resolvo::VersionSetId, std::pair<specs::Version, size_t>>
|
||||
version_set_to_max_version_and_track_features_numbers;
|
||||
|
||||
specs::ChannelResolveParams m_channel_params;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MAMBA_SOLVER_RESOLVO_DATABASE_HPP
|
|
@ -0,0 +1,32 @@
|
|||
// 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_RESOLVO_SOLVER_HPP
|
||||
#define MAMBA_SOLVER_RESOLVO_SOLVER_HPP
|
||||
|
||||
#include "mamba/core/error_handling.hpp"
|
||||
#include "mamba/solver/request.hpp"
|
||||
#include "mamba/solver/solution.hpp"
|
||||
|
||||
namespace mamba::solver::resolvo
|
||||
{
|
||||
class Database;
|
||||
|
||||
class Solver
|
||||
{
|
||||
public:
|
||||
|
||||
using Outcome = std::variant<Solution>;
|
||||
|
||||
[[nodiscard]] auto solve(Database& database, Request&& request) -> expected_t<Outcome>;
|
||||
[[nodiscard]] auto solve(Database& database, const Request& request) -> expected_t<Outcome>;
|
||||
|
||||
private:
|
||||
|
||||
auto solve_impl(Database& database, const Request& request) -> expected_t<Outcome>;
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
// 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_SOLVER_FACTORY_HPP
|
||||
#define MAMBA_SOLVER_SOLVER_FACTORY_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/solver/libsolv/database.hpp"
|
||||
#include "mamba/solver/libsolv/solver.hpp"
|
||||
#include "mamba/solver/resolvo/database.hpp"
|
||||
#include "mamba/solver/resolvo/solver.hpp"
|
||||
|
||||
namespace mamba::solver
|
||||
{
|
||||
/**
|
||||
* Type alias for the database variant that can hold either libsolv or resolvo database.
|
||||
*/
|
||||
using DatabaseVariant = std::variant<libsolv::Database, resolvo::Database>;
|
||||
|
||||
/**
|
||||
* Create a solver based on the configuration.
|
||||
*
|
||||
* @param ctx The context containing the configuration.
|
||||
* @return A unique pointer to the appropriate solver.
|
||||
*/
|
||||
template <typename Database>
|
||||
auto create_solver(const Context& ctx)
|
||||
{
|
||||
if (ctx.experimental_resolvo_solver)
|
||||
{
|
||||
return std::make_unique<resolvo::Solver>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_unique<libsolv::Solver>();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <simdjson.h>
|
||||
|
||||
#include "mamba/specs/error.hpp"
|
||||
#include "mamba/specs/platform.hpp"
|
||||
|
@ -19,6 +20,8 @@
|
|||
|
||||
namespace mamba::specs
|
||||
{
|
||||
class CondaURL;
|
||||
|
||||
enum class PackageType
|
||||
{
|
||||
Unknown,
|
||||
|
@ -66,6 +69,13 @@ namespace mamba::specs
|
|||
|
||||
[[nodiscard]] static auto from_url(std::string_view url) -> expected_parse_t<PackageInfo>;
|
||||
|
||||
[[nodiscard]] static auto from_json(
|
||||
const std::string_view& filename,
|
||||
simdjson::ondemand::object& pkg,
|
||||
const CondaURL& repo_url,
|
||||
const std::string& channel_id
|
||||
) -> expected_parse_t<PackageInfo>;
|
||||
|
||||
PackageInfo() = default;
|
||||
explicit PackageInfo(std::string name);
|
||||
PackageInfo(std::string name, std::string version, std::string build_string, std::size_t build_number);
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
#include "mamba/core/package_database_loader.hpp"
|
||||
#include "mamba/core/prefix_data.hpp"
|
||||
#include "mamba/core/subdir_index.hpp"
|
||||
#include "mamba/core/virtual_packages.hpp"
|
||||
#include "mamba/solver/libsolv/database.hpp"
|
||||
#include "mamba/solver/libsolv/repo_info.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
#include "mamba/specs/package_info.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
|
@ -22,9 +25,9 @@ namespace mamba
|
|||
auto create_repo_from_pkgs_dir(
|
||||
const Context& ctx,
|
||||
ChannelContext& channel_context,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const fs::u8path& pkgs_dir
|
||||
) -> solver::libsolv::RepoInfo
|
||||
) -> void
|
||||
{
|
||||
if (!fs::exists(pkgs_dir))
|
||||
{
|
||||
|
@ -46,7 +49,42 @@ namespace mamba
|
|||
}
|
||||
prefix_data.load_single_record(repodata_record_json);
|
||||
}
|
||||
return load_installed_packages_in_database(ctx, database, prefix_data);
|
||||
|
||||
// Create a repo from the packages
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&database))
|
||||
{
|
||||
libsolv_db->add_repo_from_packages(
|
||||
prefix_data.sorted_records(),
|
||||
"pkgs_dir",
|
||||
solver::libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
|
||||
// Load the packages into the database
|
||||
load_installed_packages_in_database(
|
||||
ctx,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>>(std::ref(*libsolv_db)),
|
||||
prefix_data
|
||||
);
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&database))
|
||||
{
|
||||
resolvo_db->add_repo_from_packages(prefix_data.sorted_records(), "pkgs_dir", false);
|
||||
|
||||
// Load the packages into the database
|
||||
load_installed_packages_in_database(
|
||||
ctx,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>>(std::ref(*resolvo_db)),
|
||||
prefix_data
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid database variant");
|
||||
}
|
||||
}
|
||||
|
||||
void create_subdirs(
|
||||
|
@ -131,7 +169,7 @@ namespace mamba
|
|||
auto load_channels_impl(
|
||||
Context& ctx,
|
||||
ChannelContext& channel_context,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
MultiPackageCache& package_caches,
|
||||
bool is_retry
|
||||
) -> expected_t<void, mamba_aggregated_error>
|
||||
|
@ -202,7 +240,14 @@ namespace mamba
|
|||
|
||||
if (!packages.empty())
|
||||
{
|
||||
database.add_repo_from_packages(packages, "packages");
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&database))
|
||||
{
|
||||
libsolv_db->add_repo_from_packages(packages, "packages");
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&database))
|
||||
{
|
||||
resolvo_db->add_repo_from_packages(packages, "packages");
|
||||
}
|
||||
}
|
||||
|
||||
expected_t<void> download_res;
|
||||
|
@ -269,31 +314,86 @@ namespace mamba
|
|||
continue;
|
||||
}
|
||||
|
||||
load_subdir_in_database(ctx, database, subdir)
|
||||
.transform([&](solver::libsolv::RepoInfo&& repo)
|
||||
{ database.set_repo_priority(repo, priorities[i]); })
|
||||
.or_else(
|
||||
[&](const auto&)
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&database))
|
||||
{
|
||||
auto res = load_subdir_in_database(ctx, *libsolv_db, subdir);
|
||||
if (!res)
|
||||
{
|
||||
if (is_retry)
|
||||
{
|
||||
if (is_retry)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not load repodata.json for " << subdir.name()
|
||||
<< " after retry." << "Please check repodata source. Exiting."
|
||||
<< std::endl;
|
||||
error_list.push_back(
|
||||
mamba_error(ss.str(), mamba_error_code::repodata_not_loaded)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING << "Could not load repodata.json for " << subdir.name()
|
||||
<< ". Deleting cache, and retrying.";
|
||||
subdir.clear_valid_cache_files();
|
||||
loading_failed = true;
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << "Could not load repodata.json for " << subdir.name()
|
||||
<< " after retry."
|
||||
<< "Please check repodata source. Exiting." << std::endl;
|
||||
error_list.push_back(
|
||||
mamba_error(ss.str(), mamba_error_code::repodata_not_loaded)
|
||||
);
|
||||
}
|
||||
);
|
||||
else
|
||||
{
|
||||
LOG_WARNING << "Could not load repodata.json for " << subdir.name()
|
||||
<< ". Deleting cache, and retrying.";
|
||||
subdir.clear_valid_cache_files();
|
||||
loading_failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&database))
|
||||
{
|
||||
// For resolvo, we need to load the repodata.json file and add it to the
|
||||
// database
|
||||
auto repodata_json = subdir.valid_json_cache_path();
|
||||
if (!repodata_json)
|
||||
{
|
||||
if (is_retry)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not load repodata.json for " << subdir.name()
|
||||
<< " after retry."
|
||||
<< "Please check repodata source. Exiting." << std::endl;
|
||||
error_list.push_back(
|
||||
mamba_error(ss.str(), mamba_error_code::repodata_not_loaded)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING << "Could not load repodata.json for " << subdir.name()
|
||||
<< ". Deleting cache, and retrying.";
|
||||
subdir.clear_valid_cache_files();
|
||||
loading_failed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
resolvo_db->add_repo_from_repodata_json(
|
||||
repodata_json.value(),
|
||||
util::rsplit(subdir.metadata().url(), "/", 1).front(),
|
||||
subdir.channel_id()
|
||||
);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (is_retry)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not load repodata.json for " << subdir.name()
|
||||
<< " after retry: " << e.what()
|
||||
<< ". Please check repodata source. Exiting." << std::endl;
|
||||
error_list.push_back(
|
||||
mamba_error(ss.str(), mamba_error_code::repodata_not_loaded)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING << "Could not load repodata.json for " << subdir.name()
|
||||
<< ": " << e.what() << ". Deleting cache, and retrying.";
|
||||
subdir.clear_valid_cache_files();
|
||||
loading_failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loading_failed)
|
||||
|
@ -318,7 +418,7 @@ namespace mamba
|
|||
auto load_channels(
|
||||
Context& ctx,
|
||||
ChannelContext& channel_context,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
MultiPackageCache& package_caches
|
||||
) -> expected_t<void, mamba_aggregated_error>
|
||||
{
|
||||
|
|
|
@ -1609,6 +1609,17 @@ namespace mamba
|
|||
}
|
||||
));
|
||||
|
||||
insert(Configurable("experimental_resolvo_solver", false)
|
||||
.group("Solver")
|
||||
.set_rc_configurable()
|
||||
.set_env_var_names()
|
||||
.description("Use the experimental resolvo solver instead of libsolv")
|
||||
.long_description(unindent(R"(
|
||||
When enabled, use the experimental resolvo solver instead of libsolv.
|
||||
This is an experimental feature and may not be fully functional.)"))
|
||||
.set_post_merge_hook<bool>([&](bool& value)
|
||||
{ m_context.experimental_resolvo_solver = value; }));
|
||||
|
||||
insert(Configurable("explicit_install", false)
|
||||
.group("Solver")
|
||||
.description("Use explicit install instead of solving environment"));
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "mamba/api/channel_loader.hpp"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "mamba/download/downloader.hpp"
|
||||
#include "mamba/fs/filesystem.hpp"
|
||||
#include "mamba/solver/libsolv/solver.hpp"
|
||||
#include "mamba/solver/resolvo/solver.hpp"
|
||||
#include "mamba/util/path_manip.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
|
@ -552,16 +554,26 @@ namespace mamba
|
|||
LOG_WARNING << "No 'channels' specified";
|
||||
}
|
||||
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
solver::DatabaseVariant db_variant = ctx.experimental_resolvo_solver
|
||||
? solver::DatabaseVariant(
|
||||
std::in_place_type<solver::resolvo::Database>,
|
||||
channel_context.params()
|
||||
)
|
||||
: solver::DatabaseVariant(
|
||||
std::in_place_type<solver::libsolv::Database>,
|
||||
channel_context.params(),
|
||||
solver::libsolv::Database::Settings{
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv }
|
||||
);
|
||||
|
||||
auto maybe_load = load_channels(ctx, channel_context, db, package_caches);
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(db_variant));
|
||||
}
|
||||
|
||||
auto maybe_load = load_channels(ctx, channel_context, db_variant, package_caches);
|
||||
if (!maybe_load)
|
||||
{
|
||||
throw std::runtime_error(maybe_load.error().what());
|
||||
|
@ -574,11 +586,18 @@ namespace mamba
|
|||
}
|
||||
PrefixData& prefix_data = maybe_prefix_data.value();
|
||||
|
||||
load_installed_packages_in_database(ctx, db, prefix_data);
|
||||
|
||||
if (auto* libsolv_db_ptr = std::get_if<solver::libsolv::Database>(&db_variant))
|
||||
{
|
||||
load_installed_packages_in_database(ctx, *libsolv_db_ptr, prefix_data);
|
||||
}
|
||||
else if (auto* resolvo_db_ptr = std::get_if<solver::resolvo::Database>(&db_variant))
|
||||
{
|
||||
load_installed_packages_in_database(ctx, *resolvo_db_ptr, prefix_data);
|
||||
}
|
||||
|
||||
auto request = create_install_request(prefix_data, raw_specs, freeze_installed);
|
||||
add_pins_to_request(request, ctx, prefix_data, raw_specs, no_pin, no_py_pin);
|
||||
|
||||
request.flags = ctx.solver_flags;
|
||||
|
||||
{
|
||||
|
@ -587,56 +606,55 @@ namespace mamba
|
|||
// Console stream prints on destruction
|
||||
}
|
||||
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
db,
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Mixed
|
||||
)
|
||||
.value();
|
||||
using LibsolvOutcome = std::variant<mamba::solver::Solution, mamba::solver::libsolv::UnSolvable>;
|
||||
auto outcome = ctx.experimental_resolvo_solver
|
||||
? solver::resolvo::Solver()
|
||||
.solve(std::get<solver::resolvo::Database>(db_variant), request)
|
||||
.map(
|
||||
[](auto&& result) -> LibsolvOutcome
|
||||
{
|
||||
// resolvo only returns Solution
|
||||
return std::get<mamba::solver::Solution>(result);
|
||||
}
|
||||
)
|
||||
: solver::libsolv::Solver().solve(
|
||||
std::get<solver::libsolv::Database>(db_variant),
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv
|
||||
);
|
||||
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
|
||||
if (!outcome.has_value())
|
||||
{
|
||||
unsolvable->explain_problems_to(
|
||||
db,
|
||||
LOG_ERROR,
|
||||
{
|
||||
/* .unavailable= */ ctx.graphics_params.palette.failure,
|
||||
/* .available= */ ctx.graphics_params.palette.success,
|
||||
}
|
||||
);
|
||||
if (retry_clean_cache && !is_retry)
|
||||
{
|
||||
ctx.local_repodata_ttl = 2;
|
||||
bool retry = true;
|
||||
return install_specs_impl(
|
||||
ctx,
|
||||
channel_context,
|
||||
config,
|
||||
raw_specs,
|
||||
create_env,
|
||||
remove_prefix_on_failure,
|
||||
retry
|
||||
);
|
||||
}
|
||||
if (freeze_installed)
|
||||
{
|
||||
Console::instance().print("Possible hints:\n - 'freeze_installed' is turned on\n"
|
||||
);
|
||||
}
|
||||
throw std::runtime_error(outcome.error().what());
|
||||
}
|
||||
auto& result = outcome.value();
|
||||
|
||||
if (ctx.output_params.json)
|
||||
// If resolvo is used, we don't need to handle UnSolvable
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&result))
|
||||
{
|
||||
Console::instance().json_write(
|
||||
{ { "success", false }, { "solver_problems", unsolvable->problems(db) } }
|
||||
unsolvable->explain_problems_to(
|
||||
std::get<solver::libsolv::Database>(db_variant),
|
||||
std::cout,
|
||||
mamba::solver::ProblemsMessageFormat{}
|
||||
);
|
||||
if (ctx.output_params.json)
|
||||
{
|
||||
nlohmann::json j;
|
||||
j["success"] = false;
|
||||
j["solver_problems"] = unsolvable->problems(
|
||||
std::get<solver::libsolv::Database>(db_variant)
|
||||
);
|
||||
Console::instance().json_write(j);
|
||||
}
|
||||
throw mamba_error(
|
||||
"Could not solve for environment specs",
|
||||
mamba_error_code::satisfiablitity_error
|
||||
);
|
||||
}
|
||||
throw mamba_error(
|
||||
"Could not solve for environment specs",
|
||||
mamba_error_code::satisfiablitity_error
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<LockFile> locks;
|
||||
|
@ -646,24 +664,10 @@ namespace mamba
|
|||
locks.push_back(LockFile(c));
|
||||
}
|
||||
|
||||
Console::instance().json_write({ { "success", true } });
|
||||
auto& solution = std::get<solver::Solution>(result);
|
||||
|
||||
// The point here is to delete the database before executing the transaction.
|
||||
// The database can have high memory impact, since installing packages
|
||||
// requires downloading, extracting, and launching Python interpreters for
|
||||
// creating ``.pyc`` files.
|
||||
// Ideally this whole function should be properly refactored and the transaction itself
|
||||
// should not need the database.
|
||||
auto trans = [&](auto database)
|
||||
{
|
||||
return MTransaction( //
|
||||
ctx,
|
||||
database,
|
||||
request,
|
||||
std::get<solver::Solution>(outcome),
|
||||
package_caches
|
||||
);
|
||||
}(std::move(db));
|
||||
Console::instance().json_write({ { "success", true } });
|
||||
auto trans = MTransaction(ctx, db_variant, request, solution, package_caches);
|
||||
|
||||
if (ctx.output_params.json)
|
||||
{
|
||||
|
@ -734,8 +738,8 @@ namespace mamba
|
|||
|
||||
namespace
|
||||
{
|
||||
|
||||
// TransactionFunc: (Database& database, MultiPackageCache& package_caches) -> MTransaction
|
||||
// TransactionFunc: (DatabaseVariant& database, MultiPackageCache& package_caches, ...) ->
|
||||
// MTransaction
|
||||
template <typename TransactionFunc>
|
||||
void install_explicit_with_transaction(
|
||||
Context& ctx,
|
||||
|
@ -746,14 +750,23 @@ namespace mamba
|
|||
bool remove_prefix_on_failure
|
||||
)
|
||||
{
|
||||
solver::libsolv::Database database{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(database);
|
||||
solver::DatabaseVariant db_variant = ctx.experimental_resolvo_solver
|
||||
? solver::DatabaseVariant(
|
||||
std::in_place_type<solver::resolvo::Database>,
|
||||
channel_context.params()
|
||||
)
|
||||
: solver::DatabaseVariant(
|
||||
std::in_place_type<solver::libsolv::Database>,
|
||||
channel_context.params(),
|
||||
solver::libsolv::Database::Settings{
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv }
|
||||
);
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(db_variant));
|
||||
}
|
||||
|
||||
init_channels(ctx, channel_context);
|
||||
// Some use cases provide a list of explicit specs, but an empty
|
||||
|
@ -772,12 +785,19 @@ namespace mamba
|
|||
|
||||
MultiPackageCache pkg_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
|
||||
load_installed_packages_in_database(ctx, database, prefix_data);
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&db_variant))
|
||||
{
|
||||
load_installed_packages_in_database(ctx, *libsolv_db, prefix_data);
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&db_variant))
|
||||
{
|
||||
load_installed_packages_in_database(ctx, *resolvo_db, prefix_data);
|
||||
}
|
||||
|
||||
std::vector<detail::other_pkg_mgr_spec> others;
|
||||
// Note that the Transaction will gather the Solvables,
|
||||
// so they must have been ready in the database's pool before this line
|
||||
auto transaction = create_transaction(database, pkg_caches, others);
|
||||
auto transaction = create_transaction(db_variant, pkg_caches, others);
|
||||
|
||||
std::vector<LockFile> lock_pkgs;
|
||||
|
||||
|
@ -836,7 +856,7 @@ namespace mamba
|
|||
ctx,
|
||||
channel_context,
|
||||
specs,
|
||||
[&](auto& db, auto& pkg_caches, auto& others)
|
||||
[&](solver::DatabaseVariant& db, auto& pkg_caches, auto& others)
|
||||
{ return create_explicit_transaction_from_urls(ctx, db, specs, pkg_caches, others); },
|
||||
create_env,
|
||||
remove_prefix_on_failure
|
||||
|
@ -868,7 +888,7 @@ namespace mamba
|
|||
ctx,
|
||||
channel_context,
|
||||
{},
|
||||
[&](auto& db, auto& pkg_caches, auto& others)
|
||||
[&](solver::DatabaseVariant& db, auto& pkg_caches, auto& others)
|
||||
{
|
||||
return create_explicit_transaction_from_lockfile(
|
||||
ctx,
|
||||
|
|
|
@ -136,15 +136,37 @@ namespace mamba
|
|||
}
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
|
||||
solver::libsolv::Database database{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(database);
|
||||
load_installed_packages_in_database(ctx, database, prefix_data);
|
||||
solver::DatabaseVariant db_variant = ctx.experimental_resolvo_solver
|
||||
? solver::DatabaseVariant(
|
||||
std::in_place_type<solver::resolvo::Database>,
|
||||
channel_context.params()
|
||||
)
|
||||
: solver::DatabaseVariant(
|
||||
std::in_place_type<solver::libsolv::Database>,
|
||||
channel_context.params(),
|
||||
solver::libsolv::Database::Settings{
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
}
|
||||
);
|
||||
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(db_variant));
|
||||
}
|
||||
|
||||
load_installed_packages_in_database(
|
||||
ctx,
|
||||
std::visit(
|
||||
[](auto& db) -> std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>>
|
||||
{ return std::ref(db); },
|
||||
db_variant
|
||||
),
|
||||
prefix_data
|
||||
);
|
||||
|
||||
const fs::u8path pkgs_dirs(ctx.prefix_params.root_prefix / "pkgs");
|
||||
MultiPackageCache package_caches({ pkgs_dirs }, ctx.validation_params);
|
||||
|
@ -181,7 +203,7 @@ namespace mamba
|
|||
pkgs_to_remove.push_back(iter->second);
|
||||
}
|
||||
}
|
||||
auto transaction = MTransaction(ctx, database, pkgs_to_remove, {}, package_caches);
|
||||
auto transaction = MTransaction(ctx, db_variant, pkgs_to_remove, {}, package_caches);
|
||||
return execute_transaction(transaction);
|
||||
}
|
||||
else
|
||||
|
@ -198,7 +220,7 @@ namespace mamba
|
|||
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
database,
|
||||
std::get<solver::libsolv::Database>(db_variant),
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
|
@ -209,9 +231,11 @@ namespace mamba
|
|||
{
|
||||
if (ctx.output_params.json)
|
||||
{
|
||||
Console::instance().json_write({ { "success", false },
|
||||
{ "solver_problems",
|
||||
unsolvable->problems(database) } });
|
||||
Console::instance().json_write(
|
||||
{ { "success", false },
|
||||
{ "solver_problems",
|
||||
unsolvable->problems(std::get<solver::libsolv::Database>(db_variant)) } }
|
||||
);
|
||||
}
|
||||
throw mamba_error(
|
||||
"Could not solve for environment specs",
|
||||
|
@ -222,12 +246,11 @@ namespace mamba
|
|||
Console::instance().json_write({ { "success", true } });
|
||||
auto transaction = MTransaction(
|
||||
ctx,
|
||||
database,
|
||||
db_variant,
|
||||
request,
|
||||
std::get<solver::Solution>(outcome),
|
||||
package_caches
|
||||
);
|
||||
|
||||
return execute_transaction(transaction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <iostream>
|
||||
#include <variant>
|
||||
|
||||
#include "mamba/api/channel_loader.hpp"
|
||||
#include "mamba/api/configuration.hpp"
|
||||
|
@ -19,76 +20,90 @@
|
|||
|
||||
namespace mamba
|
||||
{
|
||||
namespace
|
||||
auto repoquery_init(Context& ctx, Configuration& config, QueryResultFormat format, bool use_local)
|
||||
-> solver::DatabaseVariant
|
||||
{
|
||||
auto
|
||||
repoquery_init(Context& ctx, Configuration& config, QueryResultFormat format, bool use_local)
|
||||
config.at("use_target_prefix_fallback").set_value(true);
|
||||
config.at("use_default_prefix_fallback").set_value(true);
|
||||
config.at("use_root_prefix_fallback").set_value(true);
|
||||
config.at("target_prefix_checks")
|
||||
.set_value(
|
||||
MAMBA_ALLOW_EXISTING_PREFIX | MAMBA_ALLOW_MISSING_PREFIX | MAMBA_ALLOW_NOT_ENV_PREFIX
|
||||
);
|
||||
config.load();
|
||||
|
||||
auto channel_context = ChannelContext::make_conda_compatible(ctx);
|
||||
|
||||
solver::DatabaseVariant database = ctx.experimental_resolvo_solver
|
||||
? solver::DatabaseVariant(
|
||||
std::in_place_type<solver::resolvo::Database>,
|
||||
channel_context.params()
|
||||
)
|
||||
: solver::DatabaseVariant(
|
||||
std::in_place_type<solver::libsolv::Database>,
|
||||
channel_context.params(),
|
||||
solver::libsolv::Database::Settings{
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv }
|
||||
);
|
||||
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
config.at("use_target_prefix_fallback").set_value(true);
|
||||
config.at("use_default_prefix_fallback").set_value(true);
|
||||
config.at("use_root_prefix_fallback").set_value(true);
|
||||
config.at("target_prefix_checks")
|
||||
.set_value(
|
||||
MAMBA_ALLOW_EXISTING_PREFIX | MAMBA_ALLOW_MISSING_PREFIX | MAMBA_ALLOW_NOT_ENV_PREFIX
|
||||
);
|
||||
config.load();
|
||||
|
||||
auto channel_context = ChannelContext::make_conda_compatible(ctx);
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
|
||||
// bool installed = (type == QueryType::kDepends) || (type == QueryType::kWhoneeds);
|
||||
MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
if (use_local)
|
||||
{
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream() << "Using local repodata..." << std::endl;
|
||||
}
|
||||
auto exp_prefix_data = PrefixData::create(
|
||||
ctx.prefix_params.target_prefix,
|
||||
channel_context
|
||||
);
|
||||
if (!exp_prefix_data)
|
||||
{
|
||||
// TODO: propagate tl::expected mechanism
|
||||
throw std::runtime_error(exp_prefix_data.error().what());
|
||||
}
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
|
||||
load_installed_packages_in_database(ctx, db, prefix_data);
|
||||
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream()
|
||||
<< "Loaded current active prefix: " << ctx.prefix_params.target_prefix
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream() << "Getting repodata from channels..." << std::endl;
|
||||
}
|
||||
auto exp_load = load_channels(ctx, channel_context, db, package_caches);
|
||||
if (!exp_load)
|
||||
{
|
||||
throw std::runtime_error(exp_load.error().what());
|
||||
}
|
||||
}
|
||||
return db;
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(database));
|
||||
}
|
||||
|
||||
// bool installed = (type == QueryType::kDepends) || (type == QueryType::kWhoneeds);
|
||||
MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
if (use_local)
|
||||
{
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream() << "Using local repodata..." << std::endl;
|
||||
}
|
||||
auto exp_prefix_data = PrefixData::create(ctx.prefix_params.target_prefix, channel_context);
|
||||
if (!exp_prefix_data)
|
||||
{
|
||||
// TODO: propagate tl::expected mechanism
|
||||
throw std::runtime_error(exp_prefix_data.error().what());
|
||||
}
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
|
||||
load_installed_packages_in_database(
|
||||
ctx,
|
||||
std::visit(
|
||||
[](auto& db) -> std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>>
|
||||
{ return std::ref(db); },
|
||||
database
|
||||
),
|
||||
prefix_data
|
||||
);
|
||||
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream() << "Loaded current active prefix: "
|
||||
<< ctx.prefix_params.target_prefix << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (format != QueryResultFormat::Json)
|
||||
{
|
||||
Console::stream() << "Getting repodata from channels..." << std::endl;
|
||||
}
|
||||
auto exp_loaded = load_channels(ctx, channel_context, database, package_caches);
|
||||
if (!exp_loaded)
|
||||
{
|
||||
throw std::runtime_error(exp_loaded.error().what());
|
||||
}
|
||||
}
|
||||
return database;
|
||||
}
|
||||
|
||||
auto make_repoquery(
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
QueryType type,
|
||||
QueryResultFormat format,
|
||||
const std::vector<std::string>& queries,
|
||||
|
@ -97,9 +112,14 @@ namespace mamba
|
|||
std::ostream& out
|
||||
) -> bool
|
||||
{
|
||||
if (std::holds_alternative<solver::resolvo::Database>(database))
|
||||
{
|
||||
throw std::runtime_error("repoquery does not support the resolvo solver yet");
|
||||
}
|
||||
auto& libsolv_db = std::get<solver::libsolv::Database>(database);
|
||||
if (type == QueryType::Search)
|
||||
{
|
||||
auto res = Query::find(database, queries);
|
||||
auto res = Query::find(libsolv_db, queries);
|
||||
switch (format)
|
||||
{
|
||||
case QueryResultFormat::Json:
|
||||
|
@ -120,7 +140,7 @@ namespace mamba
|
|||
throw std::invalid_argument("Only one query supported for 'depends'.");
|
||||
}
|
||||
auto res = Query::depends(
|
||||
database,
|
||||
libsolv_db,
|
||||
queries.front(),
|
||||
/* tree= */ format == QueryResultFormat::Tree
|
||||
|| format == QueryResultFormat::RecursiveTable
|
||||
|
@ -147,7 +167,7 @@ namespace mamba
|
|||
throw std::invalid_argument("Only one query supported for 'whoneeds'.");
|
||||
}
|
||||
auto res = Query::whoneeds(
|
||||
database,
|
||||
libsolv_db,
|
||||
queries.front(),
|
||||
/* tree= */ format == QueryResultFormat::Tree
|
||||
|| format == QueryResultFormat::RecursiveTable
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mamba/solver/libsolv/database.hpp"
|
||||
#include "mamba/solver/libsolv/solver.hpp"
|
||||
#include "mamba/solver/request.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
|
@ -67,8 +68,8 @@ namespace mamba
|
|||
// We use `spec_names` here because `specs` contain more info than just
|
||||
// the spec name.
|
||||
// Therefore, the search later and comparison (using `specs`) with
|
||||
// MatchSpec.name().str() in `hist_map` second elements wouldn't be
|
||||
// relevant
|
||||
// MatchSpec.name().to_string() in `hist_map` second elements wouldn't
|
||||
// be relevant
|
||||
std::vector<std::string> spec_names;
|
||||
spec_names.reserve(specs.size());
|
||||
std::transform(
|
||||
|
@ -156,18 +157,27 @@ namespace mamba
|
|||
|
||||
populate_context_channels_from_specs(raw_update_specs, ctx);
|
||||
|
||||
solver::libsolv::Database db{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing ? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
},
|
||||
};
|
||||
add_spdlog_logger_to_database(db);
|
||||
solver::DatabaseVariant db_variant = ctx.experimental_resolvo_solver
|
||||
? solver::DatabaseVariant(
|
||||
std::in_place_type<solver::resolvo::Database>,
|
||||
channel_context.params()
|
||||
)
|
||||
: solver::DatabaseVariant(solver::libsolv::Database{
|
||||
channel_context.params(),
|
||||
{
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv,
|
||||
} });
|
||||
|
||||
if (!ctx.experimental_resolvo_solver)
|
||||
{
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(db_variant));
|
||||
}
|
||||
|
||||
MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
|
||||
auto exp_loaded = load_channels(ctx, channel_context, db, package_caches);
|
||||
auto exp_loaded = load_channels(ctx, channel_context, db_variant, package_caches);
|
||||
if (!exp_loaded)
|
||||
{
|
||||
throw std::runtime_error(exp_loaded.error().what());
|
||||
|
@ -181,7 +191,17 @@ namespace mamba
|
|||
}
|
||||
PrefixData& prefix_data = exp_prefix_data.value();
|
||||
|
||||
load_installed_packages_in_database(ctx, db, prefix_data);
|
||||
load_installed_packages_in_database(
|
||||
ctx,
|
||||
std::visit(
|
||||
[](auto& db) -> std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>>
|
||||
{ return std::ref(db); },
|
||||
db_variant
|
||||
),
|
||||
prefix_data
|
||||
);
|
||||
|
||||
auto request = create_update_request(prefix_data, raw_update_specs, update_params);
|
||||
add_pins_to_request(
|
||||
|
@ -201,19 +221,34 @@ namespace mamba
|
|||
// Console stream prints on destruction
|
||||
}
|
||||
|
||||
auto outcome = solver::libsolv::Solver()
|
||||
.solve(
|
||||
db,
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Mixed
|
||||
)
|
||||
.value();
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&outcome))
|
||||
using LibsolvOutcome = std::variant<mamba::solver::Solution, mamba::solver::libsolv::UnSolvable>;
|
||||
auto outcome = ctx.experimental_resolvo_solver
|
||||
? solver::resolvo::Solver()
|
||||
.solve(std::get<solver::resolvo::Database>(db_variant), request)
|
||||
.map(
|
||||
[](auto&& result) -> LibsolvOutcome
|
||||
{
|
||||
// resolvo only returns Solution
|
||||
return std::get<mamba::solver::Solution>(result);
|
||||
}
|
||||
)
|
||||
: solver::libsolv::Solver().solve(
|
||||
std::get<solver::libsolv::Database>(db_variant),
|
||||
request,
|
||||
ctx.experimental_matchspec_parsing
|
||||
? solver::libsolv::MatchSpecParser::Mamba
|
||||
: solver::libsolv::MatchSpecParser::Libsolv
|
||||
);
|
||||
|
||||
if (!outcome.has_value())
|
||||
{
|
||||
throw std::runtime_error(outcome.error().what());
|
||||
}
|
||||
auto& result = outcome.value();
|
||||
if (auto* unsolvable = std::get_if<solver::libsolv::UnSolvable>(&result))
|
||||
{
|
||||
unsolvable->explain_problems_to(
|
||||
db,
|
||||
std::get<solver::libsolv::Database>(db_variant),
|
||||
LOG_ERROR,
|
||||
{
|
||||
/* .unavailable= */ ctx.graphics_params.palette.failure,
|
||||
|
@ -222,8 +257,10 @@ namespace mamba
|
|||
);
|
||||
if (ctx.output_params.json)
|
||||
{
|
||||
Console::instance().json_write({ { "success", false },
|
||||
{ "solver_problems", unsolvable->problems(db) } });
|
||||
Console::instance().json_write(nlohmann::json{
|
||||
{ "success", false },
|
||||
{ "solver_problems",
|
||||
unsolvable->problems(std::get<solver::libsolv::Database>(db_variant)) } });
|
||||
}
|
||||
throw mamba_error(
|
||||
"Could not solve for environment specs",
|
||||
|
@ -231,12 +268,12 @@ namespace mamba
|
|||
);
|
||||
}
|
||||
|
||||
Console::instance().json_write({ { "success", true } });
|
||||
Console::instance().json_write(nlohmann::json{ { "success", true } });
|
||||
auto transaction = MTransaction(
|
||||
ctx,
|
||||
db,
|
||||
db_variant,
|
||||
request,
|
||||
std::get<solver::Solution>(outcome),
|
||||
std::get<solver::Solution>(result),
|
||||
package_caches
|
||||
);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <solv/evr.h>
|
||||
|
@ -54,9 +55,11 @@ namespace mamba
|
|||
|
||||
auto load_subdir_in_database(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>> database,
|
||||
const SubdirIndexLoader& subdir
|
||||
) -> expected_t<solver::libsolv::RepoInfo>
|
||||
) -> expected_t<void>
|
||||
{
|
||||
const auto expected_cache_origin = solver::libsolv::RepodataOrigin{
|
||||
/* .url= */ util::rsplit(subdir.metadata().url(), "/", 1).front(),
|
||||
|
@ -77,17 +80,35 @@ namespace mamba
|
|||
auto maybe_repo = subdir.valid_libsolv_cache_path().and_then(
|
||||
[&](fs::u8path&& solv_file)
|
||||
{
|
||||
return database.add_repo_from_native_serialization(
|
||||
solv_file,
|
||||
expected_cache_origin,
|
||||
subdir.channel_id(),
|
||||
add_pip
|
||||
return std::visit(
|
||||
[&](auto& db) -> expected_t<void>
|
||||
{
|
||||
using DB = std::decay_t<decltype(db)>;
|
||||
if constexpr (std::is_same_v<DB, std::reference_wrapper<solver::libsolv::Database>>)
|
||||
{
|
||||
db.get().add_repo_from_native_serialization(
|
||||
solv_file,
|
||||
expected_cache_origin,
|
||||
subdir.channel_id(),
|
||||
add_pip
|
||||
);
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected(
|
||||
"Native serialization not supported for resolvo::Database",
|
||||
mamba_error_code::unknown
|
||||
);
|
||||
}
|
||||
},
|
||||
database
|
||||
);
|
||||
}
|
||||
);
|
||||
if (maybe_repo)
|
||||
{
|
||||
return maybe_repo;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,70 +117,86 @@ namespace mamba
|
|||
[&](fs::u8path&& repodata_json)
|
||||
{
|
||||
using PackageTypes = solver::libsolv::PackageTypes;
|
||||
|
||||
LOG_INFO << "Trying to load repo from json file " << repodata_json;
|
||||
return database.add_repo_from_repodata_json(
|
||||
repodata_json,
|
||||
util::rsplit(subdir.metadata().url(), "/", 1).front(),
|
||||
subdir.channel_id(),
|
||||
add_pip,
|
||||
ctx.use_only_tar_bz2 ? PackageTypes::TarBz2Only
|
||||
: PackageTypes::CondaOrElseTarBz2,
|
||||
static_cast<solver::libsolv::VerifyPackages>(ctx.validation_params.verify_artifacts
|
||||
),
|
||||
json_parser
|
||||
return std::visit(
|
||||
[&](auto& db) -> expected_t<void>
|
||||
{
|
||||
using DB = std::decay_t<decltype(db)>;
|
||||
if constexpr (std::is_same_v<DB, std::reference_wrapper<solver::libsolv::Database>>)
|
||||
{
|
||||
db.get().add_repo_from_repodata_json(
|
||||
repodata_json,
|
||||
util::rsplit(subdir.metadata().url(), "/", 1).front(),
|
||||
subdir.channel_id(),
|
||||
add_pip,
|
||||
ctx.use_only_tar_bz2 ? PackageTypes::TarBz2Only
|
||||
: PackageTypes::CondaOrElseTarBz2,
|
||||
static_cast<solver::libsolv::VerifyPackages>(
|
||||
ctx.validation_params.verify_artifacts
|
||||
),
|
||||
json_parser
|
||||
);
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
db.get().add_repo_from_repodata_json(
|
||||
repodata_json,
|
||||
util::rsplit(subdir.metadata().url(), "/", 1).front(),
|
||||
subdir.channel_id(),
|
||||
false
|
||||
);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
database
|
||||
);
|
||||
}
|
||||
)
|
||||
.transform(
|
||||
[&](solver::libsolv::RepoInfo&& repo) -> solver::libsolv::RepoInfo
|
||||
[&](void) -> void
|
||||
{
|
||||
if (!util::on_win)
|
||||
{
|
||||
database
|
||||
.native_serialize_repo(
|
||||
repo,
|
||||
subdir.writable_libsolv_cache_path(),
|
||||
expected_cache_origin
|
||||
)
|
||||
.or_else(
|
||||
[&](const auto& err)
|
||||
{
|
||||
LOG_WARNING << R"(Fail to write native serialization to file ")"
|
||||
<< subdir.writable_libsolv_cache_path()
|
||||
<< R"(" for repo ")" << subdir.name() << ": "
|
||||
<< err.what();
|
||||
;
|
||||
}
|
||||
);
|
||||
}
|
||||
return std::move(repo);
|
||||
// Serialization step removed: no RepoInfo available to serialize.
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto load_installed_packages_in_database(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
std::variant<
|
||||
std::reference_wrapper<solver::libsolv::Database>,
|
||||
std::reference_wrapper<solver::resolvo::Database>> database,
|
||||
const PrefixData& prefix
|
||||
) -> solver::libsolv::RepoInfo
|
||||
) -> expected_t<void>
|
||||
{
|
||||
// TODO(C++20): We could do a PrefixData range that returns packages without storing them.
|
||||
auto pkgs = prefix.sorted_records();
|
||||
// TODO(C++20): We only need a range that concatenate both
|
||||
for (auto&& pkg : get_virtual_packages(ctx.platform))
|
||||
{
|
||||
pkgs.push_back(std::move(pkg));
|
||||
}
|
||||
|
||||
// Not adding Pip dependency since it might needlessly make the installed/active environment
|
||||
// broken if pip is not already installed (debatable).
|
||||
auto repo = database.add_repo_from_packages(
|
||||
pkgs,
|
||||
"installed",
|
||||
solver::libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
database.set_installed_repo(repo);
|
||||
return repo;
|
||||
if (auto* libsolv_db = std::get_if<std::reference_wrapper<solver::libsolv::Database>>(&database
|
||||
))
|
||||
{
|
||||
auto repo = libsolv_db->get().add_repo_from_packages(
|
||||
pkgs,
|
||||
"installed",
|
||||
solver::libsolv::PipAsPythonDependency::No
|
||||
);
|
||||
libsolv_db->get().set_installed_repo(repo);
|
||||
return {};
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<std::reference_wrapper<solver::resolvo::Database>>(
|
||||
&database
|
||||
))
|
||||
{
|
||||
resolvo_db->get().add_repo_from_packages(pkgs, "installed");
|
||||
resolvo_db->get().set_installed_repo("installed");
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected("Unknown database type", mamba_error_code::unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "mamba/core/thread_utils.hpp"
|
||||
#include "mamba/core/transaction.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
#include "mamba/solver/database_utils.hpp"
|
||||
#include "mamba/solver/libsolv/database.hpp"
|
||||
#include "mamba/specs/match_spec.hpp"
|
||||
#include "mamba/util/environment.hpp"
|
||||
|
@ -51,21 +52,62 @@ namespace mamba
|
|||
&& caches.get_tarball_path(pkg_info).empty();
|
||||
}
|
||||
|
||||
// TODO duplicated function, consider moving it to Pool
|
||||
auto database_has_package(solver::libsolv::Database& database, const specs::MatchSpec& spec)
|
||||
-> bool
|
||||
auto installed_python(const solver::DatabaseVariant& database) -> std::optional<std::string>
|
||||
{
|
||||
bool found = false;
|
||||
database.for_each_package_matching(
|
||||
spec,
|
||||
[&](const auto&)
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&database))
|
||||
{
|
||||
auto out = std::optional<std::string>();
|
||||
if (auto repo = libsolv_db->installed_repo())
|
||||
{
|
||||
found = true;
|
||||
return util::LoopControl::Break;
|
||||
libsolv_db->for_each_package_in_repo(
|
||||
*repo,
|
||||
[&](specs::PackageInfo&& pkg)
|
||||
{
|
||||
if (pkg.name == "python")
|
||||
{
|
||||
out = pkg.version;
|
||||
return util::LoopControl::Break;
|
||||
}
|
||||
return util::LoopControl::Continue;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return found;
|
||||
};
|
||||
return out;
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&database))
|
||||
{
|
||||
// TODO: Implement for resolvo database
|
||||
throw std::runtime_error("Python version lookup not yet implemented for resolvo database"
|
||||
);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto
|
||||
find_python_version(const solver::Solution& solution, const solver::DatabaseVariant& database)
|
||||
-> std::pair<std::string, std::string>
|
||||
{
|
||||
// We need to find the python version that will be there after this
|
||||
// Transaction is finished in order to compile the noarch packages correctly,
|
||||
|
||||
// We need to look into installed packages in case we are not installing a new python
|
||||
// version but keeping the current one.
|
||||
// Could also be written in term of PrefixData.
|
||||
std::string installed_py_ver = {};
|
||||
if (auto python_version = installed_python(database))
|
||||
{
|
||||
installed_py_ver = python_version.value();
|
||||
LOG_INFO << "Found python in installed packages " << installed_py_ver;
|
||||
}
|
||||
|
||||
std::string new_py_ver = installed_py_ver;
|
||||
if (auto py = solver::find_new_python_in_solution(solution))
|
||||
{
|
||||
new_py_ver = py->get().version;
|
||||
}
|
||||
|
||||
return { std::move(new_py_ver), std::move(installed_py_ver) };
|
||||
}
|
||||
|
||||
auto explicit_spec(const specs::PackageInfo& pkg) -> specs::MatchSpec
|
||||
{
|
||||
|
@ -86,55 +128,6 @@ namespace mamba
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto installed_python(const solver::libsolv::Database& database)
|
||||
-> std::optional<specs::PackageInfo>
|
||||
{
|
||||
// TODO combine Repo and MatchSpec search API in Pool
|
||||
auto out = std::optional<specs::PackageInfo>();
|
||||
if (auto repo = database.installed_repo())
|
||||
{
|
||||
database.for_each_package_in_repo(
|
||||
*repo,
|
||||
[&](specs::PackageInfo&& pkg)
|
||||
{
|
||||
if (pkg.name == "python")
|
||||
{
|
||||
out = std::move(pkg);
|
||||
return util::LoopControl::Break;
|
||||
}
|
||||
return util::LoopControl::Continue;
|
||||
}
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto
|
||||
find_python_version(const solver::Solution& solution, const solver::libsolv::Database& database)
|
||||
-> std::pair<std::string, std::string>
|
||||
{
|
||||
// We need to find the python version that will be there after this
|
||||
// Transaction is finished in order to compile the noarch packages correctly,
|
||||
|
||||
// We need to look into installed packages in case we are not installing a new python
|
||||
// version but keeping the current one.
|
||||
// Could also be written in term of PrefixData.
|
||||
std::string installed_py_ver = {};
|
||||
if (auto pkg = installed_python(database))
|
||||
{
|
||||
installed_py_ver = pkg->version;
|
||||
LOG_INFO << "Found python in installed packages " << installed_py_ver;
|
||||
}
|
||||
|
||||
std::string new_py_ver = installed_py_ver;
|
||||
if (auto py = solver::find_new_python_in_solution(solution))
|
||||
{
|
||||
new_py_ver = py->get().version;
|
||||
}
|
||||
|
||||
return { std::move(new_py_ver), std::move(installed_py_ver) };
|
||||
}
|
||||
}
|
||||
|
||||
MTransaction::MTransaction(const CommandParams& command_params, MultiPackageCache& caches)
|
||||
|
@ -145,7 +138,7 @@ namespace mamba
|
|||
|
||||
MTransaction::MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
std::vector<specs::PackageInfo> pkgs_to_remove,
|
||||
std::vector<specs::PackageInfo> pkgs_to_install,
|
||||
MultiPackageCache& caches
|
||||
|
@ -156,7 +149,7 @@ namespace mamba
|
|||
for (const auto& pkg : pkgs_to_remove)
|
||||
{
|
||||
auto spec = explicit_spec(pkg);
|
||||
if (!database_has_package(database, spec))
|
||||
if (!mamba::solver::database_has_package(database, spec))
|
||||
{
|
||||
not_found << "\n - " << spec.to_string();
|
||||
}
|
||||
|
@ -191,26 +184,13 @@ namespace mamba
|
|||
}
|
||||
|
||||
m_solution.actions.reserve(pkgs_to_install.size() + pkgs_to_remove.size());
|
||||
|
||||
std::transform(
|
||||
std::move_iterator(pkgs_to_install.begin()),
|
||||
std::move_iterator(pkgs_to_install.end()),
|
||||
std::back_insert_iterator(m_solution.actions),
|
||||
[](specs::PackageInfo&& pkg) { return solver::Solution::Install{ std::move(pkg) }; }
|
||||
);
|
||||
|
||||
std::transform(
|
||||
std::move_iterator(pkgs_to_remove.begin()),
|
||||
std::move_iterator(pkgs_to_remove.end()),
|
||||
std::back_insert_iterator(m_solution.actions),
|
||||
[](specs::PackageInfo&& pkg) { return solver::Solution::Remove{ std::move(pkg) }; }
|
||||
);
|
||||
|
||||
// if no action required, don't even start logging them
|
||||
if (!empty())
|
||||
for (auto& pkg : pkgs_to_install)
|
||||
{
|
||||
Console::instance().json_down("actions");
|
||||
Console::instance().json_write({ { "PREFIX", ctx.prefix_params.target_prefix.string() } });
|
||||
m_solution.actions.push_back(solver::Solution::Install{ std::move(pkg) });
|
||||
}
|
||||
for (auto& pkg : pkgs_to_remove)
|
||||
{
|
||||
m_solution.actions.push_back(solver::Solution::Remove{ std::move(pkg) });
|
||||
}
|
||||
|
||||
m_py_versions = find_python_version(m_solution, database);
|
||||
|
@ -218,7 +198,7 @@ namespace mamba
|
|||
|
||||
MTransaction::MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const solver::Request& request,
|
||||
solver::Solution solution,
|
||||
MultiPackageCache& caches
|
||||
|
@ -272,7 +252,7 @@ namespace mamba
|
|||
|
||||
MTransaction::MTransaction(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
std::vector<specs::PackageInfo> packages,
|
||||
MultiPackageCache& caches
|
||||
)
|
||||
|
@ -807,6 +787,8 @@ namespace mamba
|
|||
|
||||
Console::instance().print("Transaction\n");
|
||||
Console::stream() << " Prefix: " << ctx.prefix_params.target_prefix.string() << "\n";
|
||||
Console::stream() << " Solver: "
|
||||
<< (ctx.experimental_resolvo_solver ? "resolvo" : "libsolv") << "\n";
|
||||
|
||||
// check size of transaction
|
||||
if (empty())
|
||||
|
@ -1076,8 +1058,13 @@ namespace mamba
|
|||
t.print(out);
|
||||
}
|
||||
|
||||
MTransaction
|
||||
create_explicit_transaction_from_urls(const Context& ctx, solver::libsolv::Database& database, const std::vector<std::string>& urls, MultiPackageCache& package_caches, std::vector<detail::other_pkg_mgr_spec>&)
|
||||
MTransaction create_explicit_transaction_from_urls(
|
||||
const Context& ctx,
|
||||
solver::DatabaseVariant& database,
|
||||
const std::vector<std::string>& urls,
|
||||
MultiPackageCache& package_caches,
|
||||
std::vector<detail::other_pkg_mgr_spec>& other_specs
|
||||
)
|
||||
{
|
||||
std::vector<specs::PackageInfo> specs_to_install = {};
|
||||
specs_to_install.reserve(urls.size());
|
||||
|
@ -1097,7 +1084,7 @@ namespace mamba
|
|||
|
||||
MTransaction create_explicit_transaction_from_lockfile(
|
||||
const Context& ctx,
|
||||
solver::libsolv::Database& database,
|
||||
solver::DatabaseVariant& database,
|
||||
const fs::u8path& env_lockfile_path,
|
||||
const std::vector<std::string>& categories,
|
||||
MultiPackageCache& package_caches,
|
||||
|
@ -1161,7 +1148,7 @@ namespace mamba
|
|||
);
|
||||
}
|
||||
|
||||
return MTransaction{ ctx, database, std::move(conda_packages), package_caches };
|
||||
return MTransaction(ctx, database, conda_packages, package_caches);
|
||||
}
|
||||
|
||||
} // namespace mamba
|
||||
|
|
|
@ -149,12 +149,6 @@ namespace mamba::solver::libsolv
|
|||
|
||||
namespace
|
||||
{
|
||||
auto lsplit_track_features(std::string_view features)
|
||||
{
|
||||
constexpr auto is_sep = [](char c) -> bool { return (c == ',') || util::is_space(c); };
|
||||
auto [_, tail] = util::lstrip_if_parts(features, is_sep);
|
||||
return util::lstrip_if_parts(tail, [&](char c) { return !is_sep(c); });
|
||||
}
|
||||
|
||||
void set_solv_signatures(
|
||||
solv::ObjSolvableView solv,
|
||||
|
@ -364,28 +358,36 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
}
|
||||
|
||||
if (auto obj = pkg["track_features"]; !obj.error())
|
||||
if (auto track_features = pkg["track_features"]; !track_features.error())
|
||||
{
|
||||
if (obj.is_string())
|
||||
if (auto track_features_arr = track_features.get_array(); !track_features_arr.error())
|
||||
{
|
||||
auto splits = lsplit_track_features(obj.get_string().value_unsafe());
|
||||
for (auto elem : track_features_arr)
|
||||
{
|
||||
if (auto feat = elem.get_string(); !feat.error())
|
||||
{
|
||||
solv.add_track_feature(feat.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto track_features_str = track_features.get_string();
|
||||
!track_features_str.error())
|
||||
{
|
||||
const auto lsplit_track_features = [](std::string_view features)
|
||||
{
|
||||
constexpr auto is_sep = [](char c) -> bool
|
||||
{ return (c == ',') || util::is_space(c); };
|
||||
auto [_, tail] = util::lstrip_if_parts(features, is_sep);
|
||||
return util::lstrip_if_parts(tail, [&](char c) { return !is_sep(c); });
|
||||
};
|
||||
|
||||
auto splits = lsplit_track_features(track_features_str.value());
|
||||
while (!splits[0].empty())
|
||||
{
|
||||
solv.add_track_feature(splits[0]);
|
||||
splits = lsplit_track_features(splits[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// assuming obj is an array
|
||||
for (auto elem : obj.get_array())
|
||||
{
|
||||
if (!elem.error() && elem.is_string())
|
||||
{
|
||||
solv.add_track_feature(elem.get_string().value_unsafe());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setting signatures in solvable if they are available and `verify-artifacts` flag is
|
||||
|
|
|
@ -0,0 +1,678 @@
|
|||
// 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 <simdjson.h>
|
||||
|
||||
#include "mamba/core/output.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
#include "mamba/solver/libsolv/parameters.hpp"
|
||||
#include "mamba/solver/resolvo/database.hpp"
|
||||
#include "mamba/specs/channel.hpp"
|
||||
#include "mamba/specs/package_info.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
namespace mamba::solver::resolvo
|
||||
{
|
||||
|
||||
Database::Database(specs::ChannelResolveParams channel_params)
|
||||
: name_pool(bijective_map<::resolvo::NameId, ::resolvo::String>())
|
||||
, m_channel_params(std::move(channel_params))
|
||||
{
|
||||
}
|
||||
|
||||
auto Database::channel_params() const -> const specs::ChannelResolveParams&
|
||||
{
|
||||
return m_channel_params;
|
||||
}
|
||||
|
||||
void Database::add_repo_from_repodata_json(
|
||||
const fs::u8path& filename,
|
||||
const std::string& repo_url,
|
||||
const std::string& channel_id,
|
||||
bool verify_artifacts
|
||||
)
|
||||
{
|
||||
// BEWARE:
|
||||
// We use below `simdjson`'s "on-demand" parser, which does not tolerate reading the same
|
||||
// value more than once. This means we need to make sure that the objects and their fields
|
||||
// are read and/or concretized only once and if we need to use them more than once we need
|
||||
// to persist them in local memory. This is why the code below tries hard to pre-read the
|
||||
// data needed in several parts of the computing in a way that prevents jumping up and down
|
||||
// the hierarchy of json objects. When this rule is not followed, the parsing might end
|
||||
// earlier than expected or might skip data that are read when they shouldn't be, leading to
|
||||
// *runtime issues* that might not be visible at first. Because of these reasons, be careful
|
||||
// when modifying the following parsing code.
|
||||
|
||||
auto parser = simdjson::ondemand::parser();
|
||||
const auto lock = LockFile(filename);
|
||||
|
||||
// The json storage must be kept alive as long as we are reading the json data.
|
||||
const auto json_content = simdjson::padded_string::load(filename.string());
|
||||
|
||||
// Note that with the "on-demand" parser, documents/values/objects act as iterators
|
||||
// to go through the document.
|
||||
auto repodata_doc = parser.iterate(json_content);
|
||||
|
||||
const auto repodata_version = [&]
|
||||
{
|
||||
if (auto version = repodata_doc["repodata_version"].get_int64(); !version.error())
|
||||
{
|
||||
return version.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::int64_t{ 1 };
|
||||
}
|
||||
}();
|
||||
|
||||
auto repodata_info = [&]
|
||||
{
|
||||
if (auto value = repodata_doc["info"]; !value.error())
|
||||
{
|
||||
if (auto object = value.get_object(); !object.error())
|
||||
{
|
||||
return std::make_optional(object);
|
||||
}
|
||||
}
|
||||
return decltype(std::make_optional(repodata_doc["info"].get_object())){};
|
||||
}();
|
||||
|
||||
// An override for missing package subdir could be found at the top level
|
||||
const auto default_subdir = [&]
|
||||
{
|
||||
if (repodata_info)
|
||||
{
|
||||
if (auto subdir = repodata_info.value()["subdir"]; !subdir.error())
|
||||
{
|
||||
return std::string(subdir.get_string().value_unsafe());
|
||||
}
|
||||
}
|
||||
|
||||
return std::string{};
|
||||
}();
|
||||
|
||||
// Get `base_url` in case 'repodata_version': 2
|
||||
// cf. https://github.com/conda-incubator/ceps/blob/main/cep-15.md
|
||||
const auto base_url = [&]
|
||||
{
|
||||
if (repodata_version == 2 && repodata_info)
|
||||
{
|
||||
if (auto url = repodata_info.value()["base_url"]; !url.error())
|
||||
{
|
||||
return std::string(url.get_string().value_unsafe());
|
||||
}
|
||||
}
|
||||
|
||||
return repo_url;
|
||||
}();
|
||||
|
||||
const auto parsed_url = specs::CondaURL::parse(base_url)
|
||||
.or_else([](specs::ParseError&& err) { throw std::move(err); })
|
||||
.value();
|
||||
|
||||
// TODO: it does not seems resolvo can handle setting signatures on solvables for now
|
||||
// auto signatures = [&]
|
||||
// {
|
||||
// auto maybe_sigs = repodata_doc["signatures"];
|
||||
// if (!maybe_sigs.error() && verify_artifacts)
|
||||
// {
|
||||
// return std::make_optional(maybe_sigs);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// LOG_DEBUG << "No signatures available or requested. Downloading without verifying
|
||||
// artifacts."; return decltype(std::make_optional(maybe_sigs)){};
|
||||
// }
|
||||
// }();
|
||||
|
||||
// Process packages.conda first
|
||||
if (auto pkgs = repodata_doc["packages.conda"]; !pkgs.error())
|
||||
{
|
||||
if (auto packages_as_object = pkgs.get_object(); !packages_as_object.error())
|
||||
{
|
||||
for (auto field : packages_as_object)
|
||||
{
|
||||
if (!field.error())
|
||||
{
|
||||
const std::string key(field.unescaped_key().value());
|
||||
if (auto value = field.value(); !value.error())
|
||||
{
|
||||
if (auto pkg_obj = value.get_object(); !pkg_obj.error())
|
||||
{
|
||||
auto package_info = specs::PackageInfo::from_json(
|
||||
filename.string(),
|
||||
pkg_obj.value(),
|
||||
parsed_url,
|
||||
channel_id
|
||||
);
|
||||
if (!package_info)
|
||||
{
|
||||
LOG_WARNING << package_info.error().what();
|
||||
}
|
||||
alloc_solvable(package_info.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then process packages
|
||||
if (auto pkgs = repodata_doc["packages"]; !pkgs.error())
|
||||
{
|
||||
if (auto packages_as_object = pkgs.get_object(); !packages_as_object.error())
|
||||
{
|
||||
for (auto field : packages_as_object)
|
||||
{
|
||||
if (!field.error())
|
||||
{
|
||||
const std::string key(field.unescaped_key().value());
|
||||
if (auto value = field.value(); !value.error())
|
||||
{
|
||||
if (auto pkg_obj = value.get_object(); !pkg_obj.error())
|
||||
{
|
||||
auto package_info = specs::PackageInfo::from_json(
|
||||
filename.string(),
|
||||
pkg_obj.value(),
|
||||
parsed_url,
|
||||
channel_id
|
||||
);
|
||||
if (!package_info)
|
||||
{
|
||||
LOG_WARNING << package_info.error().what();
|
||||
}
|
||||
alloc_solvable(package_info.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Database::add_repo_from_packages(
|
||||
const std::vector<specs::PackageInfo>& packages,
|
||||
[[maybe_unused]] const std::string& repo_name,
|
||||
[[maybe_unused]] bool pip_as_python_dependency
|
||||
)
|
||||
{
|
||||
for (const auto& package : packages)
|
||||
{
|
||||
alloc_solvable(package);
|
||||
}
|
||||
}
|
||||
|
||||
void Database::set_installed_repo([[maybe_unused]] const std::string& repo_name)
|
||||
{
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new requirement and return the id of the requirement.
|
||||
*/
|
||||
::resolvo::VersionSetId Database::alloc_version_set(std::string_view raw_match_spec)
|
||||
{
|
||||
std::string raw_match_spec_str = std::string(raw_match_spec);
|
||||
// Replace all " v" with simply " " to work around the `v` prefix in some version strings
|
||||
// e.g. `mingw-w64-ucrt-x86_64-crt-git v12.0.0.r2.ggc561118da h707e725_0` in
|
||||
// `inform2w64-sysroot_win-64-v12.0.0.r2.ggc561118da-h707e725_0.conda`
|
||||
while (raw_match_spec_str.find(" v") != std::string::npos)
|
||||
{
|
||||
raw_match_spec_str = raw_match_spec_str.replace(raw_match_spec_str.find(" v"), 2, " ");
|
||||
}
|
||||
|
||||
// Remove any presence of selector on python version in the match spec
|
||||
// e.g. `pillow-heif >=0.10.0,<1.0.0<py312` -> `pillow-heif >=0.10.0,<1.0.0` in
|
||||
// `infowillow-1.6.3-pyhd8ed1ab_0.conda`
|
||||
for (const auto specifier : { "=py", "<py", ">py", ">=py", "<=py", "!=py" })
|
||||
{
|
||||
while (raw_match_spec_str.find(specifier) != std::string::npos)
|
||||
{
|
||||
raw_match_spec_str = raw_match_spec_str.substr(0, raw_match_spec_str.find(specifier));
|
||||
}
|
||||
}
|
||||
// Remove any white space between version
|
||||
// e.g. `kytea >=0.1.4, 0.2.0` -> `kytea >=0.1.4,0.2.0` in
|
||||
// `infokonoha-4.6.3-pyhd8ed1ab_0.tar.bz2`
|
||||
while (raw_match_spec_str.find(", ") != std::string::npos)
|
||||
{
|
||||
raw_match_spec_str = raw_match_spec_str.replace(raw_match_spec_str.find(", "), 2, ",");
|
||||
}
|
||||
|
||||
// TODO: skip allocation for now if "*.*" is in the match spec
|
||||
if (raw_match_spec_str.find("*.*") != std::string::npos)
|
||||
{
|
||||
return ::resolvo::VersionSetId{ 0 };
|
||||
}
|
||||
|
||||
// NOTE: works around `openblas 0.2.18|0.2.18.*.` from
|
||||
// `dlib==19.0=np110py27_blas_openblas_200` If contains "|", split on it and recurse
|
||||
if (raw_match_spec_str.find("|") != std::string::npos)
|
||||
{
|
||||
std::vector<std::string> match_specs;
|
||||
std::string match_spec;
|
||||
for (char c : raw_match_spec_str)
|
||||
{
|
||||
if (c == '|')
|
||||
{
|
||||
match_specs.push_back(match_spec);
|
||||
match_spec.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
match_spec += c;
|
||||
}
|
||||
}
|
||||
match_specs.push_back(match_spec);
|
||||
for (const std::string& ms : match_specs)
|
||||
{
|
||||
alloc_version_set(ms);
|
||||
}
|
||||
// Placeholder return value
|
||||
return ::resolvo::VersionSetId{ 0 };
|
||||
}
|
||||
|
||||
// NOTE: This works around some improperly encoded `constrains` in the test data, e.g.:
|
||||
// `openmpi-4.1.4-ha1ae619_102`'s improperly encoded `constrains`: "cudatoolkit
|
||||
// >= 10.2" `pytorch-1.13.0-cpu_py310h02c325b_0.conda`'s improperly encoded
|
||||
// `constrains`: "pytorch-cpu = 1.13.0", "pytorch-gpu = 99999999"
|
||||
// `fipy-3.4.2.1-py310hff52083_3.tar.bz2`'s improperly encoded `constrains` or `dep`:
|
||||
// ">=4.5.2"
|
||||
// Remove any with space after the binary operators
|
||||
for (const char* op : { ">=", "<=", "==", ">", "<", "!=", "=", "==" })
|
||||
{
|
||||
const std::string bad_op = std::string(op) + " ";
|
||||
while (raw_match_spec_str.find(bad_op) != std::string::npos)
|
||||
{
|
||||
raw_match_spec_str = raw_match_spec_str.substr(0, raw_match_spec_str.find(bad_op)) + op
|
||||
+ raw_match_spec_str.substr(
|
||||
raw_match_spec_str.find(bad_op) + bad_op.size()
|
||||
);
|
||||
}
|
||||
// If start with binary operator, prepend NONE
|
||||
if (raw_match_spec_str.find(op) == 0)
|
||||
{
|
||||
raw_match_spec_str = "NONE " + raw_match_spec_str;
|
||||
}
|
||||
}
|
||||
|
||||
const specs::MatchSpec match_spec = specs::MatchSpec::parse(raw_match_spec_str).value();
|
||||
// Add the version set to the version set pool
|
||||
auto id = version_set_pool.alloc(match_spec);
|
||||
|
||||
// Add name to the Name and String pools
|
||||
const std::string name = match_spec.name().to_string();
|
||||
name_pool.alloc(::resolvo::String{ name });
|
||||
string_pool.alloc(::resolvo::String{ name });
|
||||
|
||||
// Add the MatchSpec's string representation to the Name and String pools
|
||||
const std::string match_spec_str = match_spec.to_string();
|
||||
name_pool.alloc(::resolvo::String{ match_spec_str });
|
||||
string_pool.alloc(::resolvo::String{ match_spec_str });
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new solvable and returns its id.
|
||||
*
|
||||
* - Adds the solvable to the solvable pool.
|
||||
* - Adds the name to the Name and String pools.
|
||||
* - Adds the long string representation of the package to the Name and String pools.
|
||||
* - Allocates version sets for dependencies and constrains.
|
||||
* - Adds the solvable to the name_to_solvable map.
|
||||
*/
|
||||
::resolvo::SolvableId Database::alloc_solvable(specs::PackageInfo package_info)
|
||||
{
|
||||
// Add the solvable to the solvable pool
|
||||
auto id = solvable_pool.alloc(package_info);
|
||||
|
||||
// Add name to the Name and String pools
|
||||
const std::string name = package_info.name;
|
||||
name_pool.alloc(::resolvo::String{ name });
|
||||
string_pool.alloc(::resolvo::String{ name });
|
||||
|
||||
// Add the long string representation of the package to the Name and String pools
|
||||
const std::string long_str = package_info.long_str();
|
||||
name_pool.alloc(::resolvo::String{ long_str });
|
||||
string_pool.alloc(::resolvo::String{ long_str });
|
||||
|
||||
for (auto& dep : package_info.dependencies)
|
||||
{
|
||||
alloc_version_set(dep);
|
||||
}
|
||||
for (auto& constr : package_info.constrains)
|
||||
{
|
||||
alloc_version_set(constr);
|
||||
}
|
||||
|
||||
// Add the solvable to the name_to_solvable map
|
||||
const auto name_id = name_pool.alloc(::resolvo::String{ package_info.name });
|
||||
name_to_solvable[name_id].push_back(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user-friendly string representation of the specified solvable.
|
||||
*
|
||||
* When formatting the solvable, it should it include both the name of
|
||||
* the package and any other identifying properties.
|
||||
*/
|
||||
::resolvo::String Database::display_solvable(::resolvo::SolvableId solvable)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable];
|
||||
return ::resolvo::String{ package_info.long_str() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user-friendly string representation of the name of the
|
||||
* specified solvable.
|
||||
*/
|
||||
::resolvo::String Database::display_solvable_name(::resolvo::SolvableId solvable)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable];
|
||||
return ::resolvo::String{ package_info.name };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of multiple solvables merged together.
|
||||
*
|
||||
* When formatting the solvables, both the name of the packages and any
|
||||
* other identifying properties should be included.
|
||||
*/
|
||||
::resolvo::String
|
||||
Database::display_merged_solvables(::resolvo::Slice<::resolvo::SolvableId> solvable)
|
||||
{
|
||||
std::string result;
|
||||
for (auto& solvable_id : solvable)
|
||||
{
|
||||
result += solvable_pool[solvable_id].long_str();
|
||||
}
|
||||
return ::resolvo::String{ result };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object that can be used to display the given name in a
|
||||
* user-friendly way.
|
||||
*/
|
||||
::resolvo::String Database::display_name(::resolvo::NameId name)
|
||||
{
|
||||
return name_pool[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a user-friendly string representation of the specified version
|
||||
* set.
|
||||
*
|
||||
* The name of the package should *not* be included in the display. Where
|
||||
* appropriate, this information is added.
|
||||
*/
|
||||
::resolvo::String Database::display_version_set(::resolvo::VersionSetId version_set)
|
||||
{
|
||||
const specs::MatchSpec match_spec = version_set_pool[version_set];
|
||||
return ::resolvo::String{ match_spec.to_string() };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the specified string.
|
||||
*/
|
||||
::resolvo::String Database::display_string(::resolvo::StringId string)
|
||||
{
|
||||
return string_pool[string];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the package that the specified version set is
|
||||
* associated with.
|
||||
*/
|
||||
::resolvo::NameId Database::version_set_name(::resolvo::VersionSetId version_set_id)
|
||||
{
|
||||
const specs::MatchSpec match_spec = version_set_pool[version_set_id];
|
||||
return name_pool[::resolvo::String{ match_spec.name().to_string() }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the package for the given solvable.
|
||||
*/
|
||||
::resolvo::NameId Database::solvable_name(::resolvo::SolvableId solvable_id)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable_id];
|
||||
return name_pool[::resolvo::String{ package_info.name }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of solvables that should be considered when a package
|
||||
* with the given name is requested.
|
||||
*/
|
||||
::resolvo::Candidates Database::get_candidates(::resolvo::NameId package)
|
||||
{
|
||||
::resolvo::Candidates candidates{};
|
||||
candidates.favored = nullptr;
|
||||
candidates.locked = nullptr;
|
||||
candidates.candidates = name_to_solvable[package];
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the highest version and the minimum number of track features for a given version set.
|
||||
*
|
||||
* - If the version set has already been computed, returns the cached value.
|
||||
* - Filters candidates for the version set.
|
||||
* - Iterates over filtered candidates to find the maximum version and the minimum number of
|
||||
* track features.
|
||||
* - Caches and returns the result.
|
||||
*/
|
||||
std::pair<specs::Version, size_t>
|
||||
Database::find_highest_version(::resolvo::VersionSetId version_set_id)
|
||||
{
|
||||
// If the version set has already been computed, return it.
|
||||
if (version_set_to_max_version_and_track_features_numbers.find(version_set_id)
|
||||
!= version_set_to_max_version_and_track_features_numbers.end())
|
||||
{
|
||||
return version_set_to_max_version_and_track_features_numbers[version_set_id];
|
||||
}
|
||||
|
||||
const specs::MatchSpec match_spec = version_set_pool[version_set_id];
|
||||
const std::string& name = match_spec.name().to_string();
|
||||
auto name_id = name_pool.alloc(::resolvo::String{ name });
|
||||
auto solvables = name_to_solvable[name_id];
|
||||
auto filtered = filter_candidates(solvables, version_set_id, false);
|
||||
|
||||
specs::Version max_version = specs::Version();
|
||||
size_t max_version_n_track_features = 0;
|
||||
|
||||
for (auto& solvable_id : filtered)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable_id];
|
||||
const auto version = specs::Version::parse(package_info.version).value();
|
||||
if (version == max_version)
|
||||
{
|
||||
max_version_n_track_features = std::min(
|
||||
max_version_n_track_features,
|
||||
package_info.track_features.size()
|
||||
);
|
||||
}
|
||||
if (version > max_version)
|
||||
{
|
||||
max_version = version;
|
||||
max_version_n_track_features = package_info.track_features.size();
|
||||
}
|
||||
}
|
||||
|
||||
auto val = std::make_pair(max_version, max_version_n_track_features);
|
||||
version_set_to_max_version_and_track_features_numbers[version_set_id] = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the specified solvables based on which solvable to try first. The
|
||||
* solver will iteratively try to select the highest version. If a
|
||||
* conflict is found with the highest version the next version is
|
||||
* tried. This continues until a solution is found.
|
||||
*/
|
||||
void Database::sort_candidates(::resolvo::Slice<::resolvo::SolvableId> solvables)
|
||||
{
|
||||
std::sort(
|
||||
solvables.begin(),
|
||||
solvables.end(),
|
||||
[&](const ::resolvo::SolvableId& a, const ::resolvo::SolvableId& b)
|
||||
{
|
||||
const specs::PackageInfo& package_info_a = solvable_pool[a];
|
||||
const specs::PackageInfo& package_info_b = solvable_pool[b];
|
||||
|
||||
// If track features are present, prefer the solvable having the least of them.
|
||||
if (package_info_a.track_features.size() != package_info_b.track_features.size())
|
||||
{
|
||||
return package_info_a.track_features.size()
|
||||
< package_info_b.track_features.size();
|
||||
}
|
||||
|
||||
const auto a_version = specs::Version::parse(package_info_a.version).value();
|
||||
const auto b_version = specs::Version::parse(package_info_b.version).value();
|
||||
|
||||
if (a_version != b_version)
|
||||
{
|
||||
return a_version > b_version;
|
||||
}
|
||||
|
||||
if (package_info_a.build_number != package_info_b.build_number)
|
||||
{
|
||||
return package_info_a.build_number > package_info_b.build_number;
|
||||
}
|
||||
|
||||
// Compare the dependencies of the variants.
|
||||
std::unordered_map<::resolvo::NameId, ::resolvo::VersionSetId> a_deps;
|
||||
std::unordered_map<::resolvo::NameId, ::resolvo::VersionSetId> b_deps;
|
||||
for (auto dep_a : package_info_a.dependencies)
|
||||
{
|
||||
// TODO: have a VersionID to NameID mapping instead
|
||||
specs::MatchSpec ms = specs::MatchSpec::parse(dep_a).value();
|
||||
const std::string& name = ms.name().to_string();
|
||||
auto name_id = name_pool.alloc(::resolvo::String{ name });
|
||||
|
||||
a_deps[name_id] = version_set_pool[ms];
|
||||
}
|
||||
for (auto dep_b : package_info_b.dependencies)
|
||||
{
|
||||
// TODO: have a VersionID to NameID mapping instead
|
||||
specs::MatchSpec ms = specs::MatchSpec::parse(dep_b).value();
|
||||
const std::string& name = ms.name().to_string();
|
||||
auto name_id = name_pool.alloc(::resolvo::String{ name });
|
||||
|
||||
b_deps[name_id] = version_set_pool[ms];
|
||||
}
|
||||
|
||||
auto ordering_score = 0;
|
||||
for (auto [name_id, version_set_id] : a_deps)
|
||||
{
|
||||
if (b_deps.find(name_id) != b_deps.end())
|
||||
{
|
||||
auto [a_tf_version, a_n_track_features] = find_highest_version(version_set_id);
|
||||
auto [b_tf_version, b_n_track_features] = find_highest_version(b_deps[name_id]
|
||||
);
|
||||
|
||||
// Favor the solvable with higher versions of their dependencies
|
||||
if (a_tf_version != b_tf_version)
|
||||
{
|
||||
ordering_score += a_tf_version > b_tf_version ? 1 : -1;
|
||||
}
|
||||
|
||||
// Highly penalize the solvable if a dependencies has more track features
|
||||
if (a_n_track_features != b_n_track_features)
|
||||
{
|
||||
ordering_score += a_n_track_features > b_n_track_features ? -100 : 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ordering_score != 0)
|
||||
{
|
||||
return ordering_score > 0;
|
||||
}
|
||||
|
||||
return package_info_a.timestamp > package_info_b.timestamp;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of solvables, return the solvables that match the given
|
||||
* version set or if `inverse` is true, the solvables that do *not* match
|
||||
* the version set.
|
||||
*/
|
||||
::resolvo::Vector<::resolvo::SolvableId> Database::filter_candidates(
|
||||
::resolvo::Slice<::resolvo::SolvableId> candidates,
|
||||
::resolvo::VersionSetId version_set_id,
|
||||
bool inverse
|
||||
)
|
||||
{
|
||||
specs::MatchSpec match_spec = version_set_pool[version_set_id];
|
||||
::resolvo::Vector<::resolvo::SolvableId> filtered;
|
||||
|
||||
if (inverse)
|
||||
{
|
||||
for (auto& solvable_id : candidates)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable_id];
|
||||
|
||||
// Is it an appropriate check? Or must another one be crafted?
|
||||
if (!match_spec.contains_except_channel(package_info))
|
||||
{
|
||||
filtered.push_back(solvable_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& solvable_id : candidates)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable_id];
|
||||
|
||||
// Is it an appropriate check? Or must another one be crafted?
|
||||
if (match_spec.contains_except_channel(package_info))
|
||||
{
|
||||
filtered.push_back(solvable_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dependencies for the specified solvable.
|
||||
*/
|
||||
::resolvo::Dependencies Database::get_dependencies(::resolvo::SolvableId solvable_id)
|
||||
{
|
||||
const specs::PackageInfo& package_info = solvable_pool[solvable_id];
|
||||
|
||||
::resolvo::Dependencies dependencies;
|
||||
|
||||
// TODO: do this in O(1)
|
||||
for (auto& dep : package_info.dependencies)
|
||||
{
|
||||
const specs::MatchSpec match_spec = specs::MatchSpec::parse(dep).value();
|
||||
dependencies.requirements.push_back(version_set_pool[match_spec]);
|
||||
}
|
||||
for (auto& constr : package_info.constrains)
|
||||
{
|
||||
// if constr contain " == " replace it with "=="
|
||||
std::string constr2 = constr;
|
||||
while (constr2.find(" == ") != std::string::npos)
|
||||
{
|
||||
constr2 = constr2.replace(constr2.find(" == "), 4, "==");
|
||||
}
|
||||
while (constr2.find(" >= ") != std::string::npos)
|
||||
{
|
||||
constr2 = constr2.replace(constr2.find(" >= "), 4, ">=");
|
||||
}
|
||||
const specs::MatchSpec match_spec = specs::MatchSpec::parse(constr2).value();
|
||||
dependencies.constrains.push_back(version_set_pool[match_spec]);
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
// 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 "mamba/solver/resolvo/database.hpp"
|
||||
#include "mamba/solver/resolvo/solver.hpp"
|
||||
#include "mamba/util/variant_cmp.hpp"
|
||||
|
||||
namespace mamba::solver::resolvo
|
||||
{
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* An arbitrary comparison function to get determinist output.
|
||||
*/
|
||||
auto make_request_cmp()
|
||||
{
|
||||
return util::make_variant_cmp(
|
||||
/** index_cmp= */
|
||||
[](auto lhs, auto rhs) { return lhs < rhs; },
|
||||
/** alternative_cmp= */
|
||||
[](const auto& lhs, const auto& rhs)
|
||||
{
|
||||
using Itm = std::decay_t<decltype(lhs)>;
|
||||
if constexpr (!std::is_same_v<Itm, Request::UpdateAll>)
|
||||
{
|
||||
return lhs.spec.name().to_string() < rhs.spec.name().to_string();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto request_to_requirements(const Request& request, Database& database)
|
||||
-> std::vector<::resolvo::VersionSetId>
|
||||
{
|
||||
std::vector<::resolvo::VersionSetId> requirements;
|
||||
requirements.reserve(request.jobs.size());
|
||||
|
||||
for (const auto& job : request.jobs)
|
||||
{
|
||||
std::visit(
|
||||
[&](const auto& j)
|
||||
{
|
||||
using T = std::decay_t<decltype(j)>;
|
||||
if constexpr (std::is_same_v<T, Request::Install>)
|
||||
{
|
||||
requirements.push_back(
|
||||
database.alloc_version_set(j.spec.name().to_string())
|
||||
);
|
||||
}
|
||||
},
|
||||
job
|
||||
);
|
||||
}
|
||||
return requirements;
|
||||
}
|
||||
|
||||
auto request_to_constraints(const Request& request, Database& database)
|
||||
-> std::vector<::resolvo::VersionSetId>
|
||||
{
|
||||
std::vector<::resolvo::VersionSetId> constraints;
|
||||
constraints.reserve(request.jobs.size());
|
||||
|
||||
for (const auto& job : request.jobs)
|
||||
{
|
||||
std::visit(
|
||||
[&](const auto& j)
|
||||
{
|
||||
using T = std::decay_t<decltype(j)>;
|
||||
if constexpr (std::is_same_v<T, Request::Remove>)
|
||||
{
|
||||
constraints.push_back(database.alloc_version_set(j.spec.name().to_string()
|
||||
));
|
||||
}
|
||||
},
|
||||
job
|
||||
);
|
||||
}
|
||||
return constraints;
|
||||
}
|
||||
|
||||
auto
|
||||
result_to_solution(const ::resolvo::Vector<::resolvo::SolvableId>& result, Database& database, const Request&)
|
||||
-> Solution
|
||||
{
|
||||
Solution solution;
|
||||
solution.actions.reserve(result.size());
|
||||
|
||||
for (const auto& solvable_id : result)
|
||||
{
|
||||
const auto& solvable = database.solvable_pool[solvable_id];
|
||||
specs::PackageInfo pkg;
|
||||
pkg.name = solvable.name;
|
||||
pkg.version = solvable.version;
|
||||
pkg.build_string = solvable.build_string;
|
||||
pkg.build_number = solvable.build_number;
|
||||
pkg.channel = solvable.channel;
|
||||
pkg.md5 = solvable.md5;
|
||||
pkg.sha256 = solvable.sha256;
|
||||
pkg.track_features = solvable.track_features;
|
||||
pkg.dependencies = solvable.dependencies;
|
||||
pkg.constrains = solvable.constrains;
|
||||
pkg.timestamp = solvable.timestamp;
|
||||
pkg.license = solvable.license;
|
||||
pkg.size = solvable.size;
|
||||
|
||||
solution.actions.emplace_back(Solution::Install{ std::move(pkg) });
|
||||
}
|
||||
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
auto Solver::solve_impl(Database& database, const Request& request) -> expected_t<Outcome>
|
||||
{
|
||||
auto requirements = request_to_requirements(request, database);
|
||||
auto constraints = request_to_constraints(request, database);
|
||||
::resolvo::Vector<::resolvo::SolvableId> result;
|
||||
|
||||
::resolvo::Vector<::resolvo::VersionSetId> req_vec;
|
||||
for (const auto& req : requirements)
|
||||
{
|
||||
req_vec.push_back(req);
|
||||
}
|
||||
|
||||
::resolvo::Vector<::resolvo::VersionSetId> constr_vec;
|
||||
for (const auto& constr : constraints)
|
||||
{
|
||||
constr_vec.push_back(constr);
|
||||
}
|
||||
|
||||
::resolvo::String reason = ::resolvo::solve(database, req_vec, constr_vec, result);
|
||||
|
||||
if (reason != "")
|
||||
{
|
||||
// Get the length from a string view of the reason
|
||||
std::string_view reason_str_view = reason;
|
||||
std::string reason_str(reason.data(), reason_str_view.size());
|
||||
return make_unexpected(reason_str, mamba_error_code::satisfiablitity_error);
|
||||
}
|
||||
|
||||
return Outcome{ result_to_solution(result, database, request) };
|
||||
}
|
||||
|
||||
auto Solver::solve(Database& database, Request&& request) -> expected_t<Outcome>
|
||||
{
|
||||
if (request.flags.order_request)
|
||||
{
|
||||
std::sort(request.jobs.begin(), request.jobs.end(), make_request_cmp());
|
||||
}
|
||||
return solve_impl(database, request);
|
||||
}
|
||||
|
||||
auto Solver::solve(Database& database, const Request& request) -> expected_t<Outcome>
|
||||
{
|
||||
if (request.flags.order_request)
|
||||
{
|
||||
auto sorted_request = request;
|
||||
std::sort(sorted_request.jobs.begin(), sorted_request.jobs.end(), make_request_cmp());
|
||||
return solve_impl(database, sorted_request);
|
||||
}
|
||||
return solve_impl(database, request);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
#include <fmt/format.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <simdjson.h>
|
||||
|
||||
#include "mamba/specs/archive.hpp"
|
||||
#include "mamba/specs/conda_url.hpp"
|
||||
|
@ -565,4 +566,174 @@ namespace mamba::specs
|
|||
pkg.dependencies = j.value("depends", std::vector<std::string>());
|
||||
pkg.constrains = j.value("constrains", std::vector<std::string>());
|
||||
}
|
||||
|
||||
auto PackageInfo::from_json(
|
||||
const std::string_view& filename,
|
||||
simdjson::ondemand::object& pkg,
|
||||
const CondaURL& repo_url,
|
||||
const std::string& channel_id
|
||||
) -> expected_parse_t<PackageInfo>
|
||||
{
|
||||
PackageInfo package_info;
|
||||
|
||||
package_info.channel = channel_id;
|
||||
package_info.filename = filename;
|
||||
package_info.package_url = (repo_url / filename).str(CondaURL::Credentials::Show);
|
||||
|
||||
if (auto fn = pkg["fn"]; !fn.error())
|
||||
{
|
||||
package_info.name = fn.get_string().value_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback from key entry
|
||||
package_info.name = filename;
|
||||
}
|
||||
|
||||
if (auto name = pkg["name"]; !name.error())
|
||||
{
|
||||
package_info.name = name.get_string().value_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(fmt::format(R"(Found invalid name in "{}")", filename));
|
||||
}
|
||||
|
||||
if (auto version = pkg["version"]; !version.error())
|
||||
{
|
||||
package_info.version = version.get_string().value_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(fmt::format(R"(Found invalid version in "{}")", filename));
|
||||
}
|
||||
|
||||
if (auto build_string = pkg["build"]; !build_string.error())
|
||||
{
|
||||
package_info.build_string = build_string.get_string().value_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(fmt::format(R"(Found invalid build in "{}")", filename));
|
||||
}
|
||||
|
||||
if (auto build_number = pkg["build_number"]; !build_number.error())
|
||||
{
|
||||
package_info.build_number = build_number.get_uint64().value_unsafe();
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(fmt::format(R"(Found invalid build_number in "{}")", filename)
|
||||
);
|
||||
}
|
||||
|
||||
if (auto subdir = pkg["subdir"]; !subdir.error())
|
||||
{
|
||||
package_info.platform = subdir.get_string().value_unsafe();
|
||||
}
|
||||
|
||||
if (auto size = pkg["size"]; !size.error())
|
||||
{
|
||||
package_info.size = size.get_uint64().value_unsafe();
|
||||
}
|
||||
|
||||
if (auto md5 = pkg["md5"]; !md5.error())
|
||||
{
|
||||
package_info.md5 = md5.get_string().value_unsafe();
|
||||
}
|
||||
|
||||
if (auto sha256 = pkg["sha256"]; !sha256.error())
|
||||
{
|
||||
package_info.sha256 = sha256.get_string().value_unsafe();
|
||||
}
|
||||
|
||||
if (auto elem = pkg["noarch"]; !elem.error())
|
||||
{
|
||||
if (auto noarch = elem.get_bool(); !noarch.error() && noarch.value_unsafe())
|
||||
{
|
||||
package_info.noarch = NoArchType::Generic;
|
||||
}
|
||||
else if (elem.is_string())
|
||||
{
|
||||
package_info.noarch = NoArchType::Generic;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto license = pkg["license"]; !license.error())
|
||||
{
|
||||
package_info.license = license.get_string().value_unsafe();
|
||||
}
|
||||
|
||||
// TODO conda timestamp are not Unix timestamp.
|
||||
// Libsolv normalize them this way, we need to do the same here otherwise the current
|
||||
// package may get arbitrary priority.
|
||||
if (auto timestamp = pkg["timestamp"]; !timestamp.error())
|
||||
{
|
||||
const auto time = timestamp.get_uint64().value_unsafe();
|
||||
constexpr auto MAX_CONDA_TIMESTAMP = 253402300799ULL;
|
||||
package_info.timestamp = (time > MAX_CONDA_TIMESTAMP) ? (time / 1000) : time;
|
||||
}
|
||||
|
||||
if (auto depends = pkg["depends"]; !depends.error())
|
||||
{
|
||||
if (auto arr = depends.get_array(); !arr.error())
|
||||
{
|
||||
for (auto elem : arr)
|
||||
{
|
||||
if (!elem.error() && elem.is_string())
|
||||
{
|
||||
package_info.dependencies.emplace_back(elem.get_string().value_unsafe());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto constrains = pkg["constrains"]; !constrains.error())
|
||||
{
|
||||
if (auto arr = constrains.get_array(); !arr.error())
|
||||
{
|
||||
for (auto elem : arr)
|
||||
{
|
||||
if (!elem.error() && elem.is_string())
|
||||
{
|
||||
package_info.constrains.emplace_back(elem.get_string().value_unsafe());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto track_features = pkg["track_features"]; !track_features.error())
|
||||
{
|
||||
if (auto track_features_arr = track_features.get_array(); !track_features_arr.error())
|
||||
{
|
||||
for (auto elem : track_features_arr)
|
||||
{
|
||||
if (auto feat = elem.get_string(); !feat.error())
|
||||
{
|
||||
package_info.track_features.emplace_back(feat.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto track_features_str = track_features.get_string();
|
||||
!track_features_str.error())
|
||||
{
|
||||
const auto lsplit_track_features = [](std::string_view features)
|
||||
{
|
||||
constexpr auto is_sep = [](char c) -> bool
|
||||
{ return (c == ',') || util::is_space(c); };
|
||||
auto [_, tail] = util::lstrip_if_parts(features, is_sep);
|
||||
return util::lstrip_if_parts(tail, [&](char c) { return !is_sep(c); });
|
||||
};
|
||||
|
||||
auto splits = lsplit_track_features(track_features_str.value());
|
||||
while (!splits[0].empty())
|
||||
{
|
||||
package_info.track_features.emplace_back(splits[0]);
|
||||
splits = lsplit_track_features(splits[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return package_info;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,6 +104,7 @@ set(
|
|||
src/core/test_transaction_context.cpp
|
||||
src/core/test_util.cpp
|
||||
src/core/test_virtual_packages.cpp
|
||||
src/solver/resolvo/test_solver.cpp
|
||||
)
|
||||
|
||||
message(STATUS "Building libmamba C++ tests")
|
||||
|
@ -125,7 +126,7 @@ find_package(Threads REQUIRED)
|
|||
target_link_libraries(
|
||||
test_libmamba
|
||||
PUBLIC mamba::libmamba reproc reproc++
|
||||
PRIVATE Catch2::Catch2WithMain Threads::Threads
|
||||
PRIVATE Catch2::Catch2WithMain Threads::Threads Resolvo::Resolvo simdjson::simdjson
|
||||
)
|
||||
set_target_properties(
|
||||
test_libmamba PROPERTIES COMPILE_DEFINITIONS CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS
|
||||
|
|
|
@ -144,8 +144,11 @@ namespace mamba
|
|||
const fs::u8path lockfile_path{ mambatests::test_data_dir
|
||||
/ "env_lockfile/good_multiple_categories-lock.yaml" };
|
||||
auto channel_context = ChannelContext::make_conda_compatible(mambatests::context());
|
||||
solver::libsolv::Database db{ channel_context.params() };
|
||||
add_spdlog_logger_to_database(db);
|
||||
solver::DatabaseVariant db_variant = solver::DatabaseVariant(
|
||||
std::in_place_type<solver::libsolv::Database>,
|
||||
channel_context.params()
|
||||
);
|
||||
add_spdlog_logger_to_database(std::get<solver::libsolv::Database>(db_variant));
|
||||
mamba::MultiPackageCache pkg_cache({ "/tmp/" }, ctx.validation_params);
|
||||
|
||||
ctx.platform = "linux-64";
|
||||
|
@ -156,7 +159,7 @@ namespace mamba
|
|||
std::vector<detail::other_pkg_mgr_spec> other_specs;
|
||||
auto transaction = create_explicit_transaction_from_lockfile(
|
||||
ctx,
|
||||
db,
|
||||
db_variant,
|
||||
lockfile_path,
|
||||
categories,
|
||||
pkg_cache,
|
||||
|
|
|
@ -63,6 +63,48 @@ find_actions_with_name(const Solution& solution, std::string_view name)
|
|||
return out;
|
||||
}
|
||||
|
||||
auto
|
||||
find_actions(const Solution& solution) -> std::vector<Solution::Action>
|
||||
{
|
||||
auto out = std::vector<Solution::Action>();
|
||||
for (const auto& action : solution.actions)
|
||||
{
|
||||
std::visit(
|
||||
[&](const auto& act)
|
||||
{
|
||||
using Act = std::decay_t<decltype(act)>;
|
||||
if constexpr (Solution::has_install_v<Act>)
|
||||
{
|
||||
out.push_back(act);
|
||||
}
|
||||
},
|
||||
action
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
auto
|
||||
extract_package_to_install(const Solution& solution) -> std::vector<specs::PackageInfo>
|
||||
{
|
||||
auto out = std::vector<specs::PackageInfo>();
|
||||
for (const auto& action : find_actions(solution))
|
||||
{
|
||||
std::visit(
|
||||
[&](const auto& act)
|
||||
{
|
||||
using Act = std::decay_t<decltype(act)>;
|
||||
if constexpr (Solution::has_install_v<Act>)
|
||||
{
|
||||
out.push_back(act.install);
|
||||
}
|
||||
},
|
||||
action
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace specs::match_spec_literals;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -362,7 +362,7 @@ namespace
|
|||
|
||||
for (auto& sub_dir : sub_dirs)
|
||||
{
|
||||
auto repo = load_subdir_in_database(ctx, database, sub_dir);
|
||||
REQUIRE(load_subdir_in_database(ctx, database, sub_dir).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ def libmambapy_version():
|
|||
def get_cmake_args():
|
||||
cmake_args = [f"-DMAMBA_INSTALL_PYTHON_EXT_LIBDIR={CMAKE_INSTALL_DIR()}/src/libmambapy"]
|
||||
if sys.platform != "win32" and sys.platform != "cygwin":
|
||||
cmake_args += ["-DMAMBA_WARNING_AS_ERROR=ON"]
|
||||
cmake_args += ["-DMAMBA_WARNING_AS_ERROR=OFF"]
|
||||
return cmake_args
|
||||
|
||||
|
||||
|
|
|
@ -524,7 +524,15 @@ bind_submodule_impl(pybind11::module_ m)
|
|||
|
||||
m.def(
|
||||
"load_subdir_in_database",
|
||||
&load_subdir_in_database,
|
||||
[](Context& context, auto& database, SubdirIndexLoader& subdir)
|
||||
{
|
||||
auto res = load_subdir_in_database(context, database, subdir);
|
||||
if (!res)
|
||||
{
|
||||
throw std::runtime_error(res.error().what());
|
||||
}
|
||||
return py::none();
|
||||
},
|
||||
py::arg("context"),
|
||||
py::arg("database"),
|
||||
py::arg("subdir")
|
||||
|
@ -738,10 +746,15 @@ bind_submodule_impl(pybind11::module_ m)
|
|||
.def(
|
||||
"create_repo",
|
||||
[](SubdirDataMigrator& self, Context& context, solver::libsolv::Database& database
|
||||
) -> solver::libsolv::RepoInfo
|
||||
) -> py::object
|
||||
{
|
||||
deprecated("Use libmambapy.load_subdir_in_database instead", "2.0");
|
||||
return extract(load_subdir_in_database(context, database, *self.p_subdir_index));
|
||||
auto res = load_subdir_in_database(context, database, *self.p_subdir_index);
|
||||
if (!res)
|
||||
{
|
||||
throw std::runtime_error(res.error().what());
|
||||
}
|
||||
return py::none();
|
||||
},
|
||||
py::arg("context"),
|
||||
py::arg("db")
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "bind_utils.hpp"
|
||||
#include "bindings.hpp"
|
||||
#include "expected_caster.hpp"
|
||||
#include "flat_set_caster.hpp"
|
||||
|
||||
namespace mamba::solver
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "mamba/core/package_database_loader.hpp"
|
||||
#include "mamba/core/transaction.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
#include "mamba/solver/database_utils.hpp"
|
||||
#include "mamba/solver/solver_factory.hpp"
|
||||
#include "mamba/util/build.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
@ -56,36 +58,44 @@ set_update_command(CLI::App* subcom, Configuration& config)
|
|||
#ifdef BUILDING_MICROMAMBA
|
||||
namespace
|
||||
{
|
||||
auto database_has_package(solver::libsolv::Database& database, specs::MatchSpec spec) -> bool
|
||||
{
|
||||
bool found = false;
|
||||
database.for_each_package_matching(
|
||||
spec,
|
||||
[&](const auto&)
|
||||
{
|
||||
found = true;
|
||||
return util::LoopControl::Break;
|
||||
}
|
||||
);
|
||||
return found;
|
||||
};
|
||||
|
||||
auto database_latest_package(solver::libsolv::Database& database, specs::MatchSpec spec)
|
||||
auto database_latest_package(solver::DatabaseVariant& database, specs::MatchSpec spec)
|
||||
-> std::optional<specs::PackageInfo>
|
||||
{
|
||||
auto out = std::optional<specs::PackageInfo>();
|
||||
database.for_each_package_matching(
|
||||
spec,
|
||||
[&](auto pkg)
|
||||
{
|
||||
if (!out
|
||||
|| (specs::Version::parse(pkg.version).value_or(specs::Version())
|
||||
> specs::Version::parse(out->version).value_or(specs::Version())))
|
||||
if (auto* libsolv_db = std::get_if<solver::libsolv::Database>(&database))
|
||||
{
|
||||
libsolv_db->for_each_package_matching(
|
||||
spec,
|
||||
[&](auto pkg)
|
||||
{
|
||||
out = std::move(pkg);
|
||||
if (!out
|
||||
|| (specs::Version::parse(pkg.version).value_or(specs::Version())
|
||||
> specs::Version::parse(out->version).value_or(specs::Version())))
|
||||
{
|
||||
out = std::move(pkg);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (auto* resolvo_db = std::get_if<solver::resolvo::Database>(&database))
|
||||
{
|
||||
// For resolvo, we need to get all candidates for the package and find the latest
|
||||
// version
|
||||
auto candidates = resolvo_db->get_candidates(
|
||||
resolvo_db->name_pool.alloc(resolvo::String(spec.name().to_string()))
|
||||
);
|
||||
if (candidates.candidates.empty())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
);
|
||||
|
||||
// Sort candidates by version
|
||||
resolvo_db->sort_candidates(candidates.candidates);
|
||||
|
||||
// Get the latest version (last in the sorted list)
|
||||
auto latest_solvable = candidates.candidates[candidates.candidates.size() - 1];
|
||||
return resolvo_db->solvable_pool[latest_solvable];
|
||||
}
|
||||
return out;
|
||||
};
|
||||
}
|
||||
|
@ -102,12 +112,23 @@ update_self(Configuration& config, const std::optional<std::string>& version)
|
|||
|
||||
auto channel_context = ChannelContext::make_conda_compatible(ctx);
|
||||
|
||||
solver::libsolv::Database database{ channel_context.params() };
|
||||
add_spdlog_logger_to_database(database);
|
||||
auto db_variant = [&]() -> solver::DatabaseVariant
|
||||
{
|
||||
if (ctx.experimental_resolvo_solver)
|
||||
{
|
||||
return solver::resolvo::Database{ channel_context.params() };
|
||||
}
|
||||
else
|
||||
{
|
||||
solver::libsolv::Database database{ channel_context.params() };
|
||||
add_spdlog_logger_to_database(database);
|
||||
return database;
|
||||
}
|
||||
}();
|
||||
|
||||
mamba::MultiPackageCache package_caches(ctx.pkgs_dirs, ctx.validation_params);
|
||||
|
||||
auto exp_loaded = load_channels(ctx, channel_context, database, package_caches);
|
||||
auto exp_loaded = load_channels(ctx, channel_context, db_variant, package_caches);
|
||||
if (!exp_loaded)
|
||||
{
|
||||
throw exp_loaded.error();
|
||||
|
@ -120,11 +141,14 @@ update_self(Configuration& config, const std::optional<std::string>& version)
|
|||
.or_else([](specs::ParseError&& err) { throw std::move(err); })
|
||||
.value();
|
||||
|
||||
auto latest_micromamba = database_latest_package(database, matchspec);
|
||||
auto latest_micromamba = database_latest_package(db_variant, matchspec);
|
||||
|
||||
if (!latest_micromamba.has_value())
|
||||
{
|
||||
if (database_has_package(database, specs::MatchSpec::parse("micromamba").value()))
|
||||
if (mamba::solver::database_has_package(
|
||||
db_variant,
|
||||
specs::MatchSpec::parse("micromamba").value()
|
||||
))
|
||||
{
|
||||
Console::instance().print(
|
||||
fmt::format("\nYour micromamba version ({}) is already up to date.", umamba::version())
|
||||
|
@ -152,7 +176,7 @@ update_self(Configuration& config, const std::optional<std::string>& version)
|
|||
);
|
||||
|
||||
ctx.download_only = true;
|
||||
MTransaction t(ctx, database, { latest_micromamba.value() }, package_caches);
|
||||
MTransaction t(ctx, db_variant, { latest_micromamba.value() }, package_caches);
|
||||
auto exp_prefix_data = PrefixData::create(ctx.prefix_params.root_prefix, channel_context);
|
||||
if (!exp_prefix_data)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue