mirror of https://github.com/mamba-org/mamba.git
Improve Python conflicts (#2318)
Better handle the (almost) cyclic conflict due to the reversal of the dependencies bewteen python and python_abi.
This commit is contained in:
parent
0919fbd514
commit
66c721c220
|
@ -41,11 +41,6 @@ namespace mamba
|
|||
std::size_t build_number
|
||||
);
|
||||
|
||||
PackageInfo(const PackageInfo&) = default;
|
||||
PackageInfo(PackageInfo&&) noexcept = default;
|
||||
PackageInfo& operator=(const PackageInfo&) = default;
|
||||
PackageInfo& operator=(PackageInfo&&) noexcept = default;
|
||||
|
||||
bool operator==(const PackageInfo& other) const;
|
||||
|
||||
nlohmann::json json_record() const;
|
||||
|
|
|
@ -74,15 +74,13 @@ namespace mamba
|
|||
public:
|
||||
|
||||
using dependency_graph = DiGraph<PackageInfo>;
|
||||
using package_list = dependency_graph::node_list;
|
||||
using package_view_list = std::vector<package_list::const_iterator>;
|
||||
|
||||
query_result(QueryType type, const std::string& query, dependency_graph&& dep_graph);
|
||||
|
||||
~query_result() = default;
|
||||
|
||||
query_result(const query_result&);
|
||||
query_result& operator=(const query_result&);
|
||||
query_result(const query_result&) = default;
|
||||
query_result& operator=(const query_result&) = default;
|
||||
query_result(query_result&&) = default;
|
||||
query_result& operator=(query_result&&) = default;
|
||||
|
||||
|
@ -104,15 +102,18 @@ namespace mamba
|
|||
|
||||
private:
|
||||
|
||||
using node_id = dependency_graph::node_id;
|
||||
using package_id_list = std::vector<node_id>;
|
||||
using ordered_package_list = std::map<std::string, package_id_list>;
|
||||
|
||||
void reset_pkg_view_list();
|
||||
std::string get_package_repr(const PackageInfo& pkg) const;
|
||||
|
||||
QueryType m_type;
|
||||
std::string m_query;
|
||||
dependency_graph m_dep_graph;
|
||||
package_view_list m_pkg_view_list;
|
||||
using ordered_package_list = std::map<std::string, package_view_list>;
|
||||
ordered_package_list m_ordered_pkg_list;
|
||||
package_id_list m_pkg_id_list = {};
|
||||
ordered_package_list m_ordered_pkg_id_list = {};
|
||||
};
|
||||
} // namespace mamba
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
@ -19,7 +20,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include <fmt/color.h>
|
||||
#include <solv/solver.h>
|
||||
|
||||
#include "mamba/core/match_spec.hpp"
|
||||
#include "mamba/core/package_info.hpp"
|
||||
|
@ -42,6 +42,7 @@ namespace mamba
|
|||
using typename Base::value_type;
|
||||
|
||||
conflict_map() = default;
|
||||
conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs);
|
||||
|
||||
using Base::empty;
|
||||
using Base::size;
|
||||
|
@ -55,7 +56,13 @@ namespace mamba
|
|||
const_iterator end() const noexcept;
|
||||
|
||||
using Base::clear;
|
||||
void add(const key_type& a, const key_type& b);
|
||||
bool add(const key_type& a, const key_type& b);
|
||||
bool remove(const key_type& a, const key_type& b);
|
||||
bool remove(const key_type& a);
|
||||
|
||||
private:
|
||||
|
||||
bool remove_asym(const key_type& a, const key_type& b);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -70,31 +77,14 @@ namespace mamba
|
|||
};
|
||||
struct PackageNode : PackageInfo
|
||||
{
|
||||
std::optional<SolverRuleinfo> problem_type = {};
|
||||
|
||||
PackageNode(const PackageNode&) = default;
|
||||
PackageNode(PackageNode&&) noexcept = default;
|
||||
PackageNode& operator=(const PackageNode&) = default;
|
||||
PackageNode& operator=(PackageNode&&) noexcept = default;
|
||||
};
|
||||
struct UnresolvedDependencyNode : MatchSpec
|
||||
{
|
||||
SolverRuleinfo problem_type;
|
||||
|
||||
UnresolvedDependencyNode(const UnresolvedDependencyNode&) = default;
|
||||
UnresolvedDependencyNode(UnresolvedDependencyNode&&) noexcept = default;
|
||||
UnresolvedDependencyNode& operator=(const UnresolvedDependencyNode&) = default;
|
||||
UnresolvedDependencyNode& operator=(UnresolvedDependencyNode&&) noexcept = default;
|
||||
};
|
||||
struct ConstraintNode : MatchSpec
|
||||
{
|
||||
static constexpr SolverRuleinfo problem_type = SOLVER_RULE_PKG_CONSTRAINS;
|
||||
|
||||
ConstraintNode(const ConstraintNode&) = default;
|
||||
ConstraintNode(ConstraintNode&&) noexcept = default;
|
||||
ConstraintNode& operator=(const ConstraintNode&) = default;
|
||||
ConstraintNode& operator=(ConstraintNode&&) noexcept = default;
|
||||
};
|
||||
|
||||
using node_t = std::variant<RootNode, PackageNode, UnresolvedDependencyNode, ConstraintNode>;
|
||||
|
||||
using edge_t = MatchSpec;
|
||||
|
@ -118,6 +108,11 @@ namespace mamba
|
|||
node_id m_root_node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hand-crafted hurisitics to simplify conflicts in messy situations.
|
||||
*/
|
||||
ProblemsGraph simplify_conflicts(const ProblemsGraph& pbs);
|
||||
|
||||
class CompressedProblemsGraph
|
||||
{
|
||||
public:
|
||||
|
@ -252,9 +247,6 @@ namespace mamba
|
|||
std::array<std::string_view, 4> indents = { "│ ", " ", "├─ ", "└─ " };
|
||||
};
|
||||
|
||||
std::ostream& print_problem_summary_msg(std::ostream& out, const CompressedProblemsGraph& pbs);
|
||||
std::string problem_summary_msg(const CompressedProblemsGraph& pbs);
|
||||
|
||||
std::ostream& print_problem_tree_msg(
|
||||
std::ostream& out,
|
||||
const CompressedProblemsGraph& pbs,
|
||||
|
@ -267,6 +259,15 @@ namespace mamba
|
|||
* Implementation of conflict_map *
|
||||
************************************/
|
||||
|
||||
template <typename T>
|
||||
conflict_map<T>::conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs)
|
||||
{
|
||||
for (const auto& [a, b] : conflicts_pairs)
|
||||
{
|
||||
add(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool conflict_map<T>::has_conflict(const key_type& a) const
|
||||
{
|
||||
|
@ -298,10 +299,56 @@ namespace mamba
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void conflict_map<T>::add(const key_type& a, const key_type& b)
|
||||
bool conflict_map<T>::add(const key_type& a, const key_type& b)
|
||||
{
|
||||
Base::operator[](a).insert(b);
|
||||
Base::operator[](b).insert(a);
|
||||
auto [_, inserted] = Base::operator[](a).insert(b);
|
||||
if (a != b)
|
||||
{
|
||||
Base::operator[](b).insert(a);
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool conflict_map<T>::remove_asym(const key_type& a, const key_type& b)
|
||||
{
|
||||
auto iter = Base::find(a);
|
||||
if (iter == Base::end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto& cflcts = iter->second;
|
||||
const bool erased = cflcts.erase(b);
|
||||
if (cflcts.empty())
|
||||
{
|
||||
Base::erase(a);
|
||||
}
|
||||
return erased;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool conflict_map<T>::remove(const key_type& a, const key_type& b)
|
||||
{
|
||||
return remove_asym(a, b) && ((a == b) || remove_asym(b, a));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool conflict_map<T>::remove(const key_type& a)
|
||||
{
|
||||
auto a_iter = Base::find(a);
|
||||
if (a_iter == Base::end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (const auto& b : a_iter->second)
|
||||
{
|
||||
if (a != b) // Cannot modify while we iterate on it
|
||||
{
|
||||
remove_asym(b, a);
|
||||
}
|
||||
}
|
||||
Base::erase(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace mamba
|
|||
using typename Base::allocator_type;
|
||||
using typename Base::const_iterator;
|
||||
using typename Base::const_reverse_iterator;
|
||||
using typename Base::size_type;
|
||||
using typename Base::value_type;
|
||||
using key_compare = Compare;
|
||||
using value_compare = Compare;
|
||||
|
@ -86,6 +87,10 @@ namespace mamba
|
|||
template <typename InputIterator>
|
||||
void insert(InputIterator first, InputIterator last);
|
||||
|
||||
const_iterator erase(const_iterator pos);
|
||||
const_iterator erase(const_iterator first, const_iterator last);
|
||||
size_type erase(const value_type& value);
|
||||
|
||||
private:
|
||||
|
||||
key_compare m_compare;
|
||||
|
@ -138,20 +143,22 @@ namespace mamba
|
|||
|
||||
using node_t = Node;
|
||||
using node_id = std::size_t;
|
||||
using node_list = std::vector<node_t>;
|
||||
using node_map = std::map<node_id, node_t>;
|
||||
using node_id_list = vector_set<node_id>;
|
||||
using adjacency_list = std::vector<node_id_list>;
|
||||
|
||||
node_id add_node(const node_t& value);
|
||||
node_id add_node(node_t&& value);
|
||||
void add_edge(node_id from, node_id to);
|
||||
bool add_edge(node_id from, node_id to);
|
||||
bool remove_edge(node_id from, node_id to);
|
||||
bool remove_node(node_id id);
|
||||
|
||||
bool empty() const;
|
||||
std::size_t number_of_nodes() const;
|
||||
std::size_t number_of_edges() const;
|
||||
std::size_t number_of_nodes() const noexcept;
|
||||
std::size_t number_of_edges() const noexcept;
|
||||
std::size_t in_degree(node_id id) const noexcept;
|
||||
std::size_t out_degree(node_id id) const noexcept;
|
||||
const node_list& nodes() const;
|
||||
const node_map& nodes() const;
|
||||
const node_t& node(node_id id) const;
|
||||
node_t& node(node_id id);
|
||||
const node_id_list& successors(node_id id) const;
|
||||
|
@ -163,16 +170,18 @@ namespace mamba
|
|||
|
||||
// TODO C++20 better to return a range since this search cannot be interupted from the
|
||||
// visitor
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc for_each_node_id(UnaryFunc func) const;
|
||||
template <typename BinaryFunc>
|
||||
BinaryFunc for_each_edge(BinaryFunc func) const;
|
||||
BinaryFunc for_each_edge_id(BinaryFunc func) const;
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc for_each_leaf(UnaryFunc func) const;
|
||||
UnaryFunc for_each_leaf_id(UnaryFunc func) const;
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc for_each_leaf_from(node_id source, UnaryFunc func) const;
|
||||
UnaryFunc for_each_leaf_id_from(node_id source, UnaryFunc func) const;
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc for_each_root(UnaryFunc func) const;
|
||||
UnaryFunc for_each_root_id(UnaryFunc func) const;
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc for_each_root_from(node_id source, UnaryFunc func) const;
|
||||
UnaryFunc for_each_root_id_from(node_id source, UnaryFunc func) const;
|
||||
|
||||
// TODO C++20 better to return a range since this search cannot be interupted from the
|
||||
// visitor
|
||||
|
@ -181,6 +190,8 @@ namespace mamba
|
|||
|
||||
protected:
|
||||
|
||||
using derived_t = Derived;
|
||||
|
||||
DiGraphBase() = default;
|
||||
DiGraphBase(const DiGraphBase&) = default;
|
||||
DiGraphBase(DiGraphBase&&) = default;
|
||||
|
@ -188,14 +199,10 @@ namespace mamba
|
|||
DiGraphBase& operator=(DiGraphBase&&) = default;
|
||||
~DiGraphBase() = default;
|
||||
|
||||
Derived& derived_cast()
|
||||
{
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
const Derived& derived_cast() const
|
||||
{
|
||||
return static_cast<const Derived&>(*this);
|
||||
}
|
||||
node_id number_of_node_id() const noexcept;
|
||||
|
||||
Derived& derived_cast();
|
||||
const Derived& derived_cast() const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -219,8 +226,11 @@ namespace mamba
|
|||
const adjacency_list& successors
|
||||
) const;
|
||||
|
||||
node_list m_node_list;
|
||||
// Source of truth for exsising nodes
|
||||
node_map m_node_map;
|
||||
// May contains empty slots after `remove_node`
|
||||
adjacency_list m_predecessors;
|
||||
// May contains empty slots after `remove_node`
|
||||
adjacency_list m_successors;
|
||||
std::size_t m_number_of_edges = 0;
|
||||
};
|
||||
|
@ -265,18 +275,46 @@ namespace mamba
|
|||
};
|
||||
|
||||
template <typename Node, typename Edge = void>
|
||||
class DiGraph : public DiGraphBase<Node, DiGraph<Node, Edge>>
|
||||
class DiGraph : private DiGraphBase<Node, DiGraph<Node, Edge>>
|
||||
{
|
||||
public:
|
||||
|
||||
using Base = DiGraphBase<Node, DiGraph<Node, Edge>>;
|
||||
using edge_t = Edge;
|
||||
using typename Base::adjacency_list;
|
||||
using typename Base::node_id;
|
||||
using typename Base::node_id_list;
|
||||
using typename Base::node_map;
|
||||
using typename Base::node_t;
|
||||
using edge_t = Edge;
|
||||
using edge_id = std::pair<node_id, node_id>;
|
||||
using edge_map = std::map<edge_id, edge_t>;
|
||||
|
||||
void add_edge(node_id from, node_id to, const edge_t& data);
|
||||
void add_edge(node_id from, node_id to, edge_t&& data);
|
||||
using Base::empty;
|
||||
using Base::has_edge;
|
||||
using Base::has_node;
|
||||
using Base::in_degree;
|
||||
using Base::node;
|
||||
using Base::nodes;
|
||||
using Base::number_of_edges;
|
||||
using Base::number_of_nodes;
|
||||
using Base::out_degree;
|
||||
using Base::predecessors;
|
||||
using Base::successors;
|
||||
|
||||
using Base::for_each_edge_id;
|
||||
using Base::for_each_leaf_id;
|
||||
using Base::for_each_leaf_id_from;
|
||||
using Base::for_each_node_id;
|
||||
using Base::for_each_root_id;
|
||||
using Base::for_each_root_id_from;
|
||||
|
||||
using Base::depth_first_search;
|
||||
|
||||
using Base::add_node;
|
||||
bool add_edge(node_id from, node_id to, const edge_t& data);
|
||||
bool add_edge(node_id from, node_id to, edge_t&& data);
|
||||
bool remove_edge(node_id from, node_id to);
|
||||
bool remove_node(node_id id);
|
||||
|
||||
const edge_map& edges() const;
|
||||
const edge_t& edge(node_id from, node_id to) const;
|
||||
|
@ -286,8 +324,10 @@ namespace mamba
|
|||
|
||||
private:
|
||||
|
||||
friend class DiGraphBase<Node, DiGraph<Node, Edge>>; // required for private CRTP
|
||||
|
||||
template <typename T>
|
||||
void add_edge_impl(node_id from, node_id to, T&& data);
|
||||
bool add_edge_impl(node_id from, node_id to, T&& data);
|
||||
|
||||
edge_map m_edges;
|
||||
};
|
||||
|
@ -422,13 +462,40 @@ namespace mamba
|
|||
template <typename K, typename C, typename A>
|
||||
template <typename U>
|
||||
auto vector_set<K, C, A>::insert_impl(U&& value) -> std::pair<const_iterator, bool>
|
||||
{
|
||||
auto it = std::lower_bound(begin(), end(), value, m_compare);
|
||||
if ((it != end()) && (key_eq(*it, value)))
|
||||
{
|
||||
return { it, false };
|
||||
}
|
||||
it = Base::insert(it, std::forward<U>(value));
|
||||
return { it, true };
|
||||
}
|
||||
|
||||
template <typename K, typename C, typename A>
|
||||
auto vector_set<K, C, A>::erase(const_iterator pos) -> const_iterator
|
||||
{
|
||||
// No need to sort or remove duplicates again
|
||||
return Base::erase(pos);
|
||||
}
|
||||
|
||||
template <typename K, typename C, typename A>
|
||||
auto vector_set<K, C, A>::erase(const_iterator first, const_iterator last) -> const_iterator
|
||||
{
|
||||
// No need to sort or remove duplicates again
|
||||
return Base::erase(first, last);
|
||||
}
|
||||
|
||||
template <typename K, typename C, typename A>
|
||||
auto vector_set<K, C, A>::erase(const value_type& value) -> size_type
|
||||
{
|
||||
auto it = std::lower_bound(begin(), end(), value, m_compare);
|
||||
if ((it == end()) || (!(key_eq(*it, value))))
|
||||
{
|
||||
Base::insert(it, std::forward<U>(value));
|
||||
return 0;
|
||||
}
|
||||
return { it, false };
|
||||
erase(it);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <typename K, typename C, typename A>
|
||||
|
@ -455,13 +522,13 @@ namespace mamba
|
|||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::number_of_nodes() const -> std::size_t
|
||||
auto DiGraphBase<N, G>::number_of_nodes() const noexcept -> std::size_t
|
||||
{
|
||||
return m_node_list.size();
|
||||
return m_node_map.size();
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::number_of_edges() const -> std::size_t
|
||||
auto DiGraphBase<N, G>::number_of_edges() const noexcept -> std::size_t
|
||||
{
|
||||
return m_number_of_edges;
|
||||
}
|
||||
|
@ -479,21 +546,21 @@ namespace mamba
|
|||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::nodes() const -> const node_list&
|
||||
auto DiGraphBase<N, G>::nodes() const -> const node_map&
|
||||
{
|
||||
return m_node_list;
|
||||
return m_node_map;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::node(node_id id) const -> const node_t&
|
||||
{
|
||||
return m_node_list[id];
|
||||
return m_node_map.at(id);
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::node(node_id id) -> node_t&
|
||||
{
|
||||
return m_node_list[id];
|
||||
return m_node_map.at(id);
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
|
@ -523,7 +590,7 @@ namespace mamba
|
|||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::has_node(node_id id) const -> bool
|
||||
{
|
||||
return id < number_of_nodes();
|
||||
return nodes().count(id) > 0;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
|
@ -545,64 +612,129 @@ namespace mamba
|
|||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
void DiGraphBase<N, G>::add_edge(node_id from, node_id to)
|
||||
template <class V>
|
||||
auto DiGraphBase<N, G>::add_node_impl(V&& value) -> node_id
|
||||
{
|
||||
const node_id id = number_of_node_id();
|
||||
m_node_map.emplace(id, std::forward<V>(value));
|
||||
m_successors.push_back(node_id_list());
|
||||
m_predecessors.push_back(node_id_list());
|
||||
return id;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
bool DiGraphBase<N, G>::remove_node(node_id id)
|
||||
{
|
||||
if (!has_node(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto succs = successors(id); // Cannot iterate on object being modified
|
||||
for (const auto& to : succs)
|
||||
{
|
||||
remove_edge(id, to);
|
||||
}
|
||||
const auto preds = predecessors(id); // Cannot iterate on object being modified
|
||||
for (const auto& from : preds)
|
||||
{
|
||||
remove_edge(from, id);
|
||||
}
|
||||
m_node_map.erase(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
bool DiGraphBase<N, G>::add_edge(node_id from, node_id to)
|
||||
{
|
||||
if (has_edge(from, to))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_successors[from].insert(to);
|
||||
m_predecessors[to].insert(from);
|
||||
++m_number_of_edges;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
bool DiGraphBase<N, G>::remove_edge(node_id from, node_id to)
|
||||
{
|
||||
if (!has_edge(from, to))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_successors[from].erase(to);
|
||||
m_predecessors[to].erase(from);
|
||||
--m_number_of_edges;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_node_id(UnaryFunc func) const
|
||||
{
|
||||
for (const auto& [i, _] : m_node_map)
|
||||
{
|
||||
func(i);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <typename BinaryFunc>
|
||||
BinaryFunc DiGraphBase<N, G>::for_each_edge(BinaryFunc func) const
|
||||
BinaryFunc DiGraphBase<N, G>::for_each_edge_id(BinaryFunc func) const
|
||||
{
|
||||
const auto n_nodes = number_of_nodes();
|
||||
for (node_id i = 0; i < n_nodes; ++i)
|
||||
{
|
||||
for (node_id j : successors(i))
|
||||
for_each_node_id(
|
||||
[&](node_id i)
|
||||
{
|
||||
func(i, j);
|
||||
for (node_id j : successors(i))
|
||||
{
|
||||
func(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return func;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_leaf(UnaryFunc func) const
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_leaf_id(UnaryFunc func) const
|
||||
{
|
||||
const auto n_nodes = number_of_nodes();
|
||||
for (node_id i = 0; i < n_nodes; ++i)
|
||||
{
|
||||
if (out_degree(i) == 0)
|
||||
for_each_node_id(
|
||||
[&](node_id i)
|
||||
{
|
||||
func(i);
|
||||
if (out_degree(i) == 0)
|
||||
{
|
||||
func(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return func;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_root(UnaryFunc func) const
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_root_id(UnaryFunc func) const
|
||||
{
|
||||
const auto n_nodes = number_of_nodes();
|
||||
for (node_id i = 0; i < n_nodes; ++i)
|
||||
{
|
||||
if (in_degree(i) == 0)
|
||||
for_each_node_id(
|
||||
[&](node_id i)
|
||||
{
|
||||
func(i);
|
||||
if (in_degree(i) == 0)
|
||||
{
|
||||
func(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return func;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_leaf_from(node_id source, UnaryFunc func) const
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_leaf_id_from(node_id source, UnaryFunc func) const
|
||||
{
|
||||
using graph_t = DiGraphBase<N, G>;
|
||||
struct LeafVisitor : default_visitor<graph_t>
|
||||
struct LeafVisitor : default_visitor<derived_t>
|
||||
{
|
||||
UnaryFunc& m_func;
|
||||
|
||||
|
@ -611,7 +743,7 @@ namespace mamba
|
|||
{
|
||||
}
|
||||
|
||||
void start_node(node_id n, const graph_t& g)
|
||||
void start_node(node_id n, const derived_t& g)
|
||||
{
|
||||
if (g.out_degree(n) == 0)
|
||||
{
|
||||
|
@ -626,10 +758,9 @@ namespace mamba
|
|||
|
||||
template <typename N, typename G>
|
||||
template <typename UnaryFunc>
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_root_from(node_id source, UnaryFunc func) const
|
||||
UnaryFunc DiGraphBase<N, G>::for_each_root_id_from(node_id source, UnaryFunc func) const
|
||||
{
|
||||
using graph_t = DiGraphBase<N, G>;
|
||||
struct RootVisitor : default_visitor<graph_t>
|
||||
struct RootVisitor : default_visitor<derived_t>
|
||||
{
|
||||
UnaryFunc& m_func;
|
||||
|
||||
|
@ -638,7 +769,7 @@ namespace mamba
|
|||
{
|
||||
}
|
||||
|
||||
void start_node(node_id n, const graph_t& g)
|
||||
void start_node(node_id n, const derived_t& g)
|
||||
{
|
||||
if (g.in_degree(n) == 0)
|
||||
{
|
||||
|
@ -657,21 +788,11 @@ namespace mamba
|
|||
{
|
||||
if (!empty())
|
||||
{
|
||||
visited_list status(m_node_list.size(), visited::no);
|
||||
visited_list status(number_of_node_id(), visited::no);
|
||||
depth_first_search_impl(visitor, node, status, reverse ? m_predecessors : m_successors);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <class V>
|
||||
auto DiGraphBase<N, G>::add_node_impl(V&& value) -> node_id
|
||||
{
|
||||
m_node_list.push_back(std::forward<V>(value));
|
||||
m_successors.push_back(node_id_list());
|
||||
m_predecessors.push_back(node_id_list());
|
||||
return m_node_list.size() - 1u;
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
template <class V>
|
||||
void DiGraphBase<N, G>::depth_first_search_impl(
|
||||
|
@ -705,26 +826,41 @@ namespace mamba
|
|||
visitor.finish_node(node, derived_cast());
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::number_of_node_id() const noexcept -> node_id
|
||||
{
|
||||
// Not number_of_nodes because due to remove nodes it may be larger
|
||||
return m_successors.size();
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::derived_cast() -> derived_t&
|
||||
{
|
||||
return static_cast<derived_t&>(*this);
|
||||
}
|
||||
|
||||
template <typename N, typename G>
|
||||
auto DiGraphBase<N, G>::derived_cast() const -> const derived_t&
|
||||
{
|
||||
return static_cast<const derived_t&>(*this);
|
||||
}
|
||||
|
||||
/*******************************
|
||||
* Algorithms implementation *
|
||||
*******************************/
|
||||
|
||||
template <typename Node, typename Derived>
|
||||
auto is_reachable(
|
||||
const DiGraphBase<Node, Derived>& graph,
|
||||
typename DiGraphBase<Node, Derived>::node_id source,
|
||||
typename DiGraphBase<Node, Derived>::node_id target
|
||||
) -> bool
|
||||
template <typename Graph>
|
||||
auto
|
||||
is_reachable(const Graph& graph, typename Graph::node_id source, typename Graph::node_id target)
|
||||
-> bool
|
||||
{
|
||||
using graph_t = DiGraphBase<Node, Derived>;
|
||||
using node_id = typename graph_t::node_id;
|
||||
|
||||
struct : default_visitor<graph_t>
|
||||
struct : default_visitor<Graph>
|
||||
{
|
||||
using node_id = typename Graph::node_id;
|
||||
node_id target;
|
||||
bool target_visited = false;
|
||||
|
||||
void start_node(node_id node, const graph_t&)
|
||||
void start_node(node_id node, const Graph&)
|
||||
{
|
||||
target_visited = target_visited || (node == target);
|
||||
}
|
||||
|
@ -739,24 +875,50 @@ namespace mamba
|
|||
*********************************/
|
||||
|
||||
template <typename N, typename E>
|
||||
void DiGraph<N, E>::add_edge(node_id from, node_id to, const edge_t& data)
|
||||
bool DiGraph<N, E>::add_edge(node_id from, node_id to, const edge_t& data)
|
||||
{
|
||||
add_edge_impl(from, to, data);
|
||||
return add_edge_impl(from, to, data);
|
||||
}
|
||||
|
||||
template <typename N, typename E>
|
||||
void DiGraph<N, E>::add_edge(node_id from, node_id to, edge_t&& data)
|
||||
bool DiGraph<N, E>::add_edge(node_id from, node_id to, edge_t&& data)
|
||||
{
|
||||
add_edge_impl(from, to, std::move(data));
|
||||
return add_edge_impl(from, to, std::move(data));
|
||||
}
|
||||
|
||||
template <typename N, typename E>
|
||||
template <typename T>
|
||||
void DiGraph<N, E>::add_edge_impl(node_id from, node_id to, T&& data)
|
||||
bool DiGraph<N, E>::add_edge_impl(node_id from, node_id to, T&& data)
|
||||
{
|
||||
Base::add_edge(from, to);
|
||||
auto ledge_id = std::make_pair(from, to);
|
||||
m_edges.insert(std::make_pair(ledge_id, std::forward<T>(data)));
|
||||
if (const bool added = Base::add_edge(from, to); added)
|
||||
{
|
||||
auto l_edge_id = std::pair(from, to);
|
||||
m_edges.insert(std::pair(l_edge_id, std::forward<T>(data)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename N, typename E>
|
||||
bool DiGraph<N, E>::remove_edge(node_id from, node_id to)
|
||||
{
|
||||
m_edges.erase({ from, to }); // No-op if edge does not exists
|
||||
return Base::remove_edge(from, to);
|
||||
}
|
||||
|
||||
template <typename N, typename E>
|
||||
bool DiGraph<N, E>::remove_node(node_id id)
|
||||
{
|
||||
// No-op if edge does not exists
|
||||
for (const auto& to : successors(id))
|
||||
{
|
||||
m_edges.erase({ id, to });
|
||||
}
|
||||
for (const auto& from : predecessors(id))
|
||||
{
|
||||
m_edges.erase({ from, id });
|
||||
}
|
||||
return Base::remove_node(id);
|
||||
}
|
||||
|
||||
template <typename N, typename E>
|
||||
|
|
|
@ -151,57 +151,60 @@ namespace mamba
|
|||
m_pool.get().create_whatprovides();
|
||||
}
|
||||
|
||||
auto print_solvable = [](auto& pkg)
|
||||
namespace
|
||||
{
|
||||
auto out = Console::stream();
|
||||
std::string header = fmt::format("{} {} {}", pkg->name, pkg->version, pkg->build_string);
|
||||
fmt::print(out, "{:^40}\n{:-^{}}\n\n", header, "", header.size() > 40 ? header.size() : 40);
|
||||
|
||||
static constexpr const char* fmtstring = " {:<15} {}\n";
|
||||
fmt::print(out, fmtstring, "File Name", pkg->fn);
|
||||
fmt::print(out, fmtstring, "Name", pkg->name);
|
||||
fmt::print(out, fmtstring, "Version", pkg->version);
|
||||
fmt::print(out, fmtstring, "Build", pkg->build_string);
|
||||
fmt::print(out, fmtstring, "Build Number", pkg->build_number);
|
||||
fmt::print(out, " {:<15} {} Kb\n", "Size", pkg->size / 1000);
|
||||
fmt::print(out, fmtstring, "License", pkg->license);
|
||||
fmt::print(out, fmtstring, "Subdir", pkg->subdir);
|
||||
|
||||
std::string url_remaining, url_scheme, url_auth, url_token;
|
||||
split_scheme_auth_token(pkg->url, url_remaining, url_scheme, url_auth, url_token);
|
||||
|
||||
fmt::print(out, " {:<15} {}://{}\n", "URL", url_scheme, url_remaining);
|
||||
|
||||
fmt::print(out, fmtstring, "MD5", pkg->md5.empty() ? "Not available" : pkg->md5);
|
||||
fmt::print(out, fmtstring, "SHA256", pkg->sha256.empty() ? "Not available" : pkg->sha256);
|
||||
if (pkg->track_features.size())
|
||||
auto print_solvable(const PackageInfo& pkg)
|
||||
{
|
||||
fmt::print(out, fmtstring, "Track Features", pkg->track_features);
|
||||
}
|
||||
auto out = Console::stream();
|
||||
std::string header = fmt::format("{} {} {}", pkg.name, pkg.version, pkg.build_string);
|
||||
fmt::print(out, "{:^40}\n{:-^{}}\n\n", header, "", header.size() > 40 ? header.size() : 40);
|
||||
|
||||
// std::cout << fmt::format<char>(
|
||||
// " {:<15} {:%Y-%m-%d %H:%M:%S} UTC\n", "Timestamp", fmt::gmtime(pkg->timestamp));
|
||||
static constexpr const char* fmtstring = " {:<15} {}\n";
|
||||
fmt::print(out, fmtstring, "File Name", pkg.fn);
|
||||
fmt::print(out, fmtstring, "Name", pkg.name);
|
||||
fmt::print(out, fmtstring, "Version", pkg.version);
|
||||
fmt::print(out, fmtstring, "Build", pkg.build_string);
|
||||
fmt::print(out, fmtstring, "Build Number", pkg.build_number);
|
||||
fmt::print(out, " {:<15} {} Kb\n", "Size", pkg.size / 1000);
|
||||
fmt::print(out, fmtstring, "License", pkg.license);
|
||||
fmt::print(out, fmtstring, "Subdir", pkg.subdir);
|
||||
|
||||
if (!pkg->depends.empty())
|
||||
{
|
||||
fmt::print(out, "\n Dependencies:\n");
|
||||
for (auto& d : pkg->depends)
|
||||
std::string url_remaining, url_scheme, url_auth, url_token;
|
||||
split_scheme_auth_token(pkg.url, url_remaining, url_scheme, url_auth, url_token);
|
||||
|
||||
fmt::print(out, " {:<15} {}://{}\n", "URL", url_scheme, url_remaining);
|
||||
|
||||
fmt::print(out, fmtstring, "MD5", pkg.md5.empty() ? "Not available" : pkg.md5);
|
||||
fmt::print(out, fmtstring, "SHA256", pkg.sha256.empty() ? "Not available" : pkg.sha256);
|
||||
if (pkg.track_features.size())
|
||||
{
|
||||
fmt::print(out, " - {}\n", d);
|
||||
fmt::print(out, fmtstring, "Track Features", pkg.track_features);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkg->constrains.empty())
|
||||
{
|
||||
fmt::print(out, "\n Run Constraints:\n");
|
||||
for (auto& c : pkg->constrains)
|
||||
// std::cout << fmt::format<char>(
|
||||
// " {:<15} {:%Y-%m-%d %H:%M:%S} UTC\n", "Timestamp", fmt::gmtime(pkg.timestamp));
|
||||
|
||||
if (!pkg.depends.empty())
|
||||
{
|
||||
fmt::print(out, " - {}\n", c);
|
||||
fmt::print(out, "\n Dependencies:\n");
|
||||
for (auto& d : pkg.depends)
|
||||
{
|
||||
fmt::print(out, " - {}\n", d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out << '\n';
|
||||
};
|
||||
if (!pkg.constrains.empty())
|
||||
{
|
||||
fmt::print(out, "\n Run Constraints:\n");
|
||||
for (auto& c : pkg.constrains)
|
||||
{
|
||||
fmt::print(out, " - {}\n", c);
|
||||
}
|
||||
}
|
||||
|
||||
out << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
query_result Query::find(const std::string& query) const
|
||||
{
|
||||
|
@ -326,59 +329,10 @@ namespace mamba
|
|||
: m_type(type)
|
||||
, m_query(query)
|
||||
, m_dep_graph(std::move(dep_graph))
|
||||
, m_pkg_view_list(m_dep_graph.number_of_nodes())
|
||||
, m_ordered_pkg_list()
|
||||
{
|
||||
reset_pkg_view_list();
|
||||
}
|
||||
|
||||
query_result::query_result(const query_result& rhs)
|
||||
: m_type(rhs.m_type)
|
||||
, m_query(rhs.m_query)
|
||||
, m_dep_graph(rhs.m_dep_graph)
|
||||
, m_pkg_view_list()
|
||||
, m_ordered_pkg_list()
|
||||
{
|
||||
using std::swap;
|
||||
auto offset_lbd = [&rhs, this](auto iter)
|
||||
{ return m_dep_graph.nodes().begin() + (iter - rhs.m_dep_graph.nodes().begin()); };
|
||||
|
||||
{
|
||||
package_view_list tmp(rhs.m_pkg_view_list.size());
|
||||
std::transform(rhs.m_pkg_view_list.begin(), rhs.m_pkg_view_list.end(), tmp.begin(), offset_lbd);
|
||||
swap(tmp, m_pkg_view_list);
|
||||
}
|
||||
|
||||
if (!rhs.m_ordered_pkg_list.empty())
|
||||
{
|
||||
auto tmp(rhs.m_ordered_pkg_list);
|
||||
std::for_each(
|
||||
tmp.begin(),
|
||||
tmp.end(),
|
||||
[offset_lbd](auto& entry) {
|
||||
std::transform(
|
||||
entry.second.begin(),
|
||||
entry.second.end(),
|
||||
entry.second.begin(),
|
||||
offset_lbd
|
||||
);
|
||||
}
|
||||
);
|
||||
swap(m_ordered_pkg_list, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
query_result& query_result::operator=(const query_result& rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
{
|
||||
using std::swap;
|
||||
query_result tmp(rhs);
|
||||
swap(*this, tmp);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
QueryType query_result::query_type() const
|
||||
{
|
||||
return m_type;
|
||||
|
@ -391,26 +345,19 @@ namespace mamba
|
|||
|
||||
query_result& query_result::sort(std::string field)
|
||||
{
|
||||
auto fun = PackageInfo::less(field);
|
||||
auto compare_ids = [&, fun = PackageInfo::less(field)](node_id lhs, node_id rhs)
|
||||
{ return fun(m_dep_graph.node(lhs), m_dep_graph.node(rhs)); };
|
||||
|
||||
if (!m_ordered_pkg_list.empty())
|
||||
if (!m_ordered_pkg_id_list.empty())
|
||||
{
|
||||
for (auto& entry : m_ordered_pkg_list)
|
||||
for (auto& [_, pkg_id_list] : m_ordered_pkg_id_list)
|
||||
{
|
||||
std::sort(
|
||||
entry.second.begin(),
|
||||
entry.second.end(),
|
||||
[fun](const auto& lhs, const auto& rhs) { return fun(*lhs, *rhs); }
|
||||
);
|
||||
std::sort(pkg_id_list.begin(), pkg_id_list.end(), compare_ids);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(
|
||||
m_pkg_view_list.begin(),
|
||||
m_pkg_view_list.end(),
|
||||
[fun](const auto& lhs, const auto& rhs) { return fun(*lhs, *rhs); }
|
||||
);
|
||||
std::sort(m_pkg_id_list.begin(), m_pkg_id_list.end(), compare_ids);
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -419,25 +366,25 @@ namespace mamba
|
|||
query_result& query_result::groupby(std::string field)
|
||||
{
|
||||
auto fun = PackageInfo::get_field_getter(field);
|
||||
if (m_ordered_pkg_list.empty())
|
||||
if (m_ordered_pkg_id_list.empty())
|
||||
{
|
||||
for (auto& pkg : m_pkg_view_list)
|
||||
for (auto& id : m_pkg_id_list)
|
||||
{
|
||||
m_ordered_pkg_list[fun(*pkg)].push_back(pkg);
|
||||
m_ordered_pkg_id_list[fun(m_dep_graph.node(id))].push_back(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ordered_package_list tmp;
|
||||
for (auto& entry : m_ordered_pkg_list)
|
||||
for (auto& entry : m_ordered_pkg_id_list)
|
||||
{
|
||||
for (auto& pkg : entry.second)
|
||||
for (auto& id : entry.second)
|
||||
{
|
||||
std::string key = entry.first + '/' + fun(*pkg);
|
||||
tmp[key].push_back(pkg);
|
||||
std::string key = entry.first + '/' + fun(m_dep_graph.node(id));
|
||||
tmp[std::move(key)].push_back(id);
|
||||
}
|
||||
}
|
||||
m_ordered_pkg_list = std::move(tmp);
|
||||
m_ordered_pkg_id_list = std::move(tmp);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -445,7 +392,7 @@ namespace mamba
|
|||
query_result& query_result::reset()
|
||||
{
|
||||
reset_pkg_view_list();
|
||||
m_ordered_pkg_list.clear();
|
||||
m_ordered_pkg_id_list.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -456,7 +403,7 @@ namespace mamba
|
|||
|
||||
std::ostream& query_result::table(std::ostream& out, const std::vector<std::string>& fmt) const
|
||||
{
|
||||
if (m_pkg_view_list.empty())
|
||||
if (m_pkg_id_list.empty())
|
||||
{
|
||||
out << "No entries matching \"" << m_query << "\" found" << std::endl;
|
||||
}
|
||||
|
@ -480,7 +427,7 @@ namespace mamba
|
|||
}
|
||||
}
|
||||
|
||||
auto format_row = [&](auto& pkg)
|
||||
auto format_row = [&](const PackageInfo& pkg)
|
||||
{
|
||||
std::vector<mamba::printers::FormattedString> row;
|
||||
for (std::size_t i = 0; i < cmds.size(); ++i)
|
||||
|
@ -488,24 +435,24 @@ namespace mamba
|
|||
const auto& cmd = cmds[i];
|
||||
if (cmd == "Name")
|
||||
{
|
||||
row.push_back(pkg->name);
|
||||
row.push_back(pkg.name);
|
||||
}
|
||||
else if (cmd == "Version")
|
||||
{
|
||||
row.push_back(pkg->version);
|
||||
row.push_back(pkg.version);
|
||||
}
|
||||
else if (cmd == "Build")
|
||||
{
|
||||
row.push_back(pkg->build_string);
|
||||
row.push_back(pkg.build_string);
|
||||
}
|
||||
else if (cmd == "Channel")
|
||||
{
|
||||
row.push_back(cut_repo_name(pkg->channel));
|
||||
row.push_back(cut_repo_name(pkg.channel));
|
||||
}
|
||||
else if (cmd == "Depends")
|
||||
{
|
||||
std::string depends_qualifier;
|
||||
for (const auto& dep : pkg->depends)
|
||||
for (const auto& dep : pkg.depends)
|
||||
{
|
||||
if (starts_with(dep, args[i]))
|
||||
{
|
||||
|
@ -521,21 +468,21 @@ namespace mamba
|
|||
|
||||
printers::Table printer(headers);
|
||||
|
||||
if (!m_ordered_pkg_list.empty())
|
||||
if (!m_ordered_pkg_id_list.empty())
|
||||
{
|
||||
for (auto& entry : m_ordered_pkg_list)
|
||||
for (auto& entry : m_ordered_pkg_id_list)
|
||||
{
|
||||
for (auto& pkg : entry.second)
|
||||
for (const auto& id : entry.second)
|
||||
{
|
||||
printer.add_row(format_row(pkg));
|
||||
printer.add_row(format_row(m_dep_graph.node(id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& pkg : m_pkg_view_list)
|
||||
for (const auto& id : m_pkg_id_list)
|
||||
{
|
||||
printer.add_row(format_row(pkg));
|
||||
printer.add_row(format_row(m_dep_graph.node(id)));
|
||||
}
|
||||
}
|
||||
return printer.print(out);
|
||||
|
@ -557,7 +504,7 @@ namespace mamba
|
|||
void start_node(node_id node, const graph_type& g)
|
||||
{
|
||||
print_prefix(node);
|
||||
m_out << get_package_repr(g.nodes()[node]) << '\n';
|
||||
m_out << get_package_repr(g.node(node)) << '\n';
|
||||
if (node == 0u)
|
||||
{
|
||||
m_prefix_stack.push_back(" ");
|
||||
|
@ -595,7 +542,7 @@ namespace mamba
|
|||
void forward_or_cross_edge(node_id, node_id to, const graph_type& g)
|
||||
{
|
||||
print_prefix(to);
|
||||
m_out << g.nodes()[to].name
|
||||
m_out << g.node(to).name
|
||||
<< fmt::format(Context::instance().palette.shown, " already visited\n");
|
||||
}
|
||||
|
||||
|
@ -645,14 +592,14 @@ namespace mamba
|
|||
graph_printer printer(out);
|
||||
m_dep_graph.depth_first_search(printer);
|
||||
}
|
||||
else if (!m_pkg_view_list.empty())
|
||||
else if (!m_pkg_id_list.empty())
|
||||
{
|
||||
out << m_query << '\n';
|
||||
for (size_t i = 0; i < m_pkg_view_list.size() - 1; ++i)
|
||||
for (size_t i = 0; i < m_pkg_id_list.size() - 1; ++i)
|
||||
{
|
||||
out << " ├─ " << get_package_repr(*m_pkg_view_list[i]) << '\n';
|
||||
out << " ├─ " << get_package_repr(m_dep_graph.node(m_pkg_id_list[i])) << '\n';
|
||||
}
|
||||
out << " └─ " << get_package_repr(*m_pkg_view_list.back()) << '\n';
|
||||
out << " └─ " << get_package_repr(m_dep_graph.node(m_pkg_id_list.back())) << '\n';
|
||||
}
|
||||
|
||||
return out;
|
||||
|
@ -666,22 +613,22 @@ namespace mamba
|
|||
: (m_type == QueryType::kDEPENDS ? "depends" : "whoneeds");
|
||||
j["query"] = { { "query", MatchSpec(m_query).conda_build_form() }, { "type", query_type } };
|
||||
|
||||
std::string msg = m_pkg_view_list.empty() ? "No entries matching \"" + m_query + "\" found"
|
||||
: "";
|
||||
std::string msg = m_pkg_id_list.empty() ? "No entries matching \"" + m_query + "\" found"
|
||||
: "";
|
||||
j["result"] = { { "msg", msg }, { "status", "OK" } };
|
||||
|
||||
j["result"]["pkgs"] = nlohmann::json::array();
|
||||
for (size_t i = 0; i < m_pkg_view_list.size(); ++i)
|
||||
for (size_t i = 0; i < m_pkg_id_list.size(); ++i)
|
||||
{
|
||||
j["result"]["pkgs"].push_back(m_pkg_view_list[i]->json_record());
|
||||
j["result"]["pkgs"].push_back(m_dep_graph.node(m_pkg_id_list[i]).json_record());
|
||||
}
|
||||
|
||||
if (m_type != QueryType::kSEARCH && !m_pkg_view_list.empty())
|
||||
if (m_type != QueryType::kSEARCH && !m_pkg_id_list.empty())
|
||||
{
|
||||
bool has_root = !m_dep_graph.successors(0).empty();
|
||||
j["result"]["graph_roots"] = nlohmann::json::array();
|
||||
j["result"]["graph_roots"].push_back(
|
||||
has_root ? m_dep_graph.nodes()[0].json_record() : nlohmann::json(m_query)
|
||||
has_root ? m_dep_graph.node(0).json_record() : nlohmann::json(m_query)
|
||||
);
|
||||
}
|
||||
return j;
|
||||
|
@ -689,11 +636,11 @@ namespace mamba
|
|||
|
||||
std::ostream& query_result::pretty(std::ostream& out) const
|
||||
{
|
||||
if (!m_pkg_view_list.empty())
|
||||
if (!m_pkg_id_list.empty())
|
||||
{
|
||||
for (const auto& pkg : m_pkg_view_list)
|
||||
for (const auto& id : m_pkg_id_list)
|
||||
{
|
||||
print_solvable(pkg);
|
||||
print_solvable(m_dep_graph.node(id));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
@ -706,12 +653,13 @@ namespace mamba
|
|||
|
||||
void query_result::reset_pkg_view_list()
|
||||
{
|
||||
auto it = m_dep_graph.nodes().begin();
|
||||
std::generate(m_pkg_view_list.begin(), m_pkg_view_list.end(), [&it]() { return it++; });
|
||||
m_pkg_id_list.clear();
|
||||
m_pkg_id_list.reserve(m_dep_graph.number_of_nodes());
|
||||
m_dep_graph.for_each_node_id([&](node_id id) { m_pkg_id_list.push_back(id); });
|
||||
}
|
||||
|
||||
std::string query_result::get_package_repr(const PackageInfo& pkg) const
|
||||
{
|
||||
return pkg.version.empty() ? pkg.name : pkg.name + '[' + pkg.version + ']';
|
||||
return pkg.version.empty() ? pkg.name : fmt::format("{}[{}]", pkg.name, pkg.version);
|
||||
}
|
||||
} // namespace mamba
|
||||
|
|
|
@ -57,18 +57,9 @@ namespace mamba
|
|||
using edge_t = ProblemsGraph::edge_t;
|
||||
using conflicts_t = ProblemsGraph::conflicts_t;
|
||||
|
||||
ProblemsGraphCreator(const MSolver& solver, const MPool& pool)
|
||||
: m_solver{ solver }
|
||||
, m_pool{ pool }
|
||||
{
|
||||
m_root_node = m_graph.add_node(RootNode());
|
||||
parse_problems();
|
||||
}
|
||||
ProblemsGraphCreator(const MSolver& solver, const MPool& pool);
|
||||
|
||||
operator ProblemsGraph() &&
|
||||
{
|
||||
return { std::move(m_graph), std::move(m_conflicts), m_root_node };
|
||||
}
|
||||
ProblemsGraph problem_graph() &&;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -94,6 +85,19 @@ namespace mamba
|
|||
void parse_problems();
|
||||
};
|
||||
|
||||
ProblemsGraphCreator::ProblemsGraphCreator(const MSolver& solver, const MPool& pool)
|
||||
: m_solver{ solver }
|
||||
, m_pool{ pool }
|
||||
{
|
||||
m_root_node = m_graph.add_node(RootNode());
|
||||
parse_problems();
|
||||
}
|
||||
|
||||
ProblemsGraph ProblemsGraphCreator::problem_graph() &&
|
||||
{
|
||||
return { std::move(m_graph), std::move(m_conflicts), m_root_node };
|
||||
}
|
||||
|
||||
auto ProblemsGraphCreator::add_solvable(SolvId solv_id, node_t&& node, bool update) -> node_id
|
||||
{
|
||||
if (const auto iter = m_solv2node.find(solv_id); iter != m_solv2node.end())
|
||||
|
@ -123,11 +127,7 @@ namespace mamba
|
|||
{
|
||||
added = true;
|
||||
PackageInfo pkg_info(pool_id2solvable(m_pool, solv_id));
|
||||
node_id to_id = add_solvable(
|
||||
solv_id,
|
||||
PackageNode{ std::move(pkg_info), std::nullopt },
|
||||
false
|
||||
);
|
||||
node_id to_id = add_solvable(solv_id, PackageNode{ std::move(pkg_info) }, false);
|
||||
m_graph.add_edge(from_id, to_id, edge);
|
||||
}
|
||||
return added;
|
||||
|
@ -157,11 +157,11 @@ namespace mamba
|
|||
}
|
||||
auto src_id = add_solvable(
|
||||
problem.source_id,
|
||||
PackageNode{ std::move(source).value(), std::nullopt }
|
||||
PackageNode{ std::move(source).value() }
|
||||
);
|
||||
node_id tgt_id = add_solvable(
|
||||
problem.target_id,
|
||||
PackageNode{ std::move(target).value(), { type } }
|
||||
PackageNode{ std::move(target).value() }
|
||||
);
|
||||
node_id cons_id = add_solvable(problem.dep_id, ConstraintNode{ dep.value() });
|
||||
MatchSpec edge(dep.value());
|
||||
|
@ -182,7 +182,7 @@ namespace mamba
|
|||
}
|
||||
auto src_id = add_solvable(
|
||||
problem.source_id,
|
||||
PackageNode{ std::move(source).value(), std::nullopt }
|
||||
PackageNode{ std::move(source).value() }
|
||||
);
|
||||
MatchSpec edge(dep.value());
|
||||
bool added = add_expanded_deps_edges(src_id, problem.dep_id, edge);
|
||||
|
@ -225,7 +225,7 @@ namespace mamba
|
|||
MatchSpec edge(dep.value());
|
||||
node_id dep_id = add_solvable(
|
||||
problem.dep_id,
|
||||
UnresolvedDependencyNode{ std::move(dep).value(), type }
|
||||
UnresolvedDependencyNode{ std::move(dep).value() }
|
||||
);
|
||||
m_graph.add_edge(m_root_node, dep_id, std::move(edge));
|
||||
break;
|
||||
|
@ -244,11 +244,11 @@ namespace mamba
|
|||
MatchSpec edge(dep.value());
|
||||
node_id src_id = add_solvable(
|
||||
problem.source_id,
|
||||
PackageNode{ std::move(source).value(), std::nullopt }
|
||||
PackageNode{ std::move(source).value() }
|
||||
);
|
||||
node_id dep_id = add_solvable(
|
||||
problem.dep_id,
|
||||
UnresolvedDependencyNode{ std::move(dep).value(), type }
|
||||
UnresolvedDependencyNode{ std::move(dep).value() }
|
||||
);
|
||||
m_graph.add_edge(src_id, dep_id, std::move(edge));
|
||||
break;
|
||||
|
@ -267,11 +267,11 @@ namespace mamba
|
|||
}
|
||||
node_id src_id = add_solvable(
|
||||
problem.source_id,
|
||||
PackageNode{ std::move(source).value(), { type } }
|
||||
PackageNode{ std::move(source).value() }
|
||||
);
|
||||
node_id tgt_id = add_solvable(
|
||||
problem.target_id,
|
||||
PackageNode{ std::move(target).value(), { type } }
|
||||
PackageNode{ std::move(target).value() }
|
||||
);
|
||||
add_conflict(src_id, tgt_id);
|
||||
break;
|
||||
|
@ -289,13 +289,13 @@ namespace mamba
|
|||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto ProblemsGraph::from_solver(const MSolver& solver, const MPool& pool) -> ProblemsGraph
|
||||
{
|
||||
return ProblemsGraphCreator(solver, pool);
|
||||
return ProblemsGraphCreator(solver, pool).problem_graph();
|
||||
}
|
||||
|
||||
ProblemsGraph::ProblemsGraph(graph_t graph, conflicts_t conflicts, node_id root_node)
|
||||
|
@ -320,6 +320,74 @@ namespace mamba
|
|||
return m_root_node;
|
||||
}
|
||||
|
||||
/******************************************
|
||||
* Implementation of simplify_conflicts *
|
||||
******************************************/
|
||||
|
||||
ProblemsGraph simplify_conflicts(const ProblemsGraph& pbs)
|
||||
{
|
||||
using node_id = ProblemsGraph::node_id;
|
||||
using node_t = ProblemsGraph::node_t;
|
||||
|
||||
const auto& old_graph = pbs.graph();
|
||||
const auto& old_conflicts = pbs.conflicts();
|
||||
|
||||
const auto is_constraint = [](const node_t& node) -> bool
|
||||
{ return std::holds_alternative<ProblemsGraph::ConstraintNode>(node); };
|
||||
|
||||
const auto has_constraint_child = [&](node_id n)
|
||||
{
|
||||
if (old_graph.out_degree(n) == 1)
|
||||
{
|
||||
node_id const s = old_graph.successors(n).front();
|
||||
// If s is of constraint type, it has conflicts
|
||||
assert(!is_constraint(old_graph.node(s)) || old_conflicts.has_conflict(s));
|
||||
return is_constraint(old_graph.node(s));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Working on a copy since we cannot safely iterate through object
|
||||
// and modify them at the same time
|
||||
auto graph = pbs.graph();
|
||||
auto conflicts = pbs.conflicts();
|
||||
|
||||
for (const auto& [id, id_conflicts] : old_conflicts)
|
||||
{
|
||||
// We are trying to detect node that are in conflicts but are not leaves.
|
||||
// This shows up in Pyhon dependencies because the constraint on ``python`` and
|
||||
// ``python_abi`` was inversed.
|
||||
if (has_constraint_child(id))
|
||||
{
|
||||
const node_id id_child = old_graph.successors(id).front();
|
||||
for (const auto& c : id_conflicts)
|
||||
{
|
||||
// Since ``id`` has a constraint node child (``id-child``) and is itself in
|
||||
// conflict with another constraint node ``c``, we are moving the conflict
|
||||
// between the ``id_child`` and the parents ``c_parent`` of the ``c``.
|
||||
// This is a bit hacky but the intuition is to replicate the structure of
|
||||
// nodes conflicting with ``id_child`` with ``c_parent``.
|
||||
// They likely reprensent the same thing and so we want them to be able to
|
||||
// merge later on.
|
||||
if (is_constraint(old_graph.node(c)))
|
||||
{
|
||||
for (const node_id c_parent : old_graph.predecessors(c))
|
||||
{
|
||||
conflicts.add(id_child, c_parent);
|
||||
graph.remove_edge(c_parent, c);
|
||||
}
|
||||
conflicts.remove(id, c);
|
||||
if (!conflicts.has_conflict(c))
|
||||
{
|
||||
graph.remove_node(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { std::move(graph), std::move(conflicts), pbs.root_node() };
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Implementation of CompressedProblemsGraph *
|
||||
***********************************************/
|
||||
|
@ -388,21 +456,20 @@ namespace mamba
|
|||
using node_type_list = std::vector<T>;
|
||||
|
||||
/**
|
||||
* Group indices of variants with same alternative together.
|
||||
* Group ids of nodes with same alternative together.
|
||||
*
|
||||
* If a variant at index ``i`` in the input @p vrnts holds alternative with index ``k``
|
||||
* then the output will contain ``i`` in position ``k``.
|
||||
* If a node variant at with id ``id`` in the input graph @p g holds alternative
|
||||
* with variant index ``k`` then the output will contain ``id`` in position ``k``.
|
||||
*/
|
||||
template <typename... T>
|
||||
auto variant_by_index(const std::vector<std::variant<T...>>& vrnts)
|
||||
-> node_type_list<std::vector<std::size_t>>
|
||||
auto node_id_by_type(const ProblemsGraph::graph_t& g) -> node_type_list<old_node_id_list>
|
||||
{
|
||||
auto out = node_type_list<std::vector<std::size_t>>(sizeof...(T));
|
||||
const auto n = vrnts.size();
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
out[vrnts[i].index()].push_back(i);
|
||||
}
|
||||
using node_id = ProblemsGraph::node_id;
|
||||
using node_t = ProblemsGraph::node_t;
|
||||
static constexpr auto n_types = std::variant_size_v<node_t>;
|
||||
|
||||
auto out = node_type_list<old_node_id_list>(n_types);
|
||||
g.for_each_node_id([&](node_id id) { out[g.node(id).index()].push_back(id); });
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -417,7 +484,7 @@ namespace mamba
|
|||
*/
|
||||
template <typename CompFunc>
|
||||
auto
|
||||
merge_node_indices(const ProblemsGraph::graph_t::node_list& nodes, CompFunc&& merge_criteria)
|
||||
merge_node_indices(const node_type_list<old_node_id_list>& nodes_by_type, CompFunc&& merge_criteria)
|
||||
-> node_type_list<std::vector<old_node_id_list>>
|
||||
{
|
||||
auto merge_func = [&merge_criteria](const auto& node_indices_of_one_node_type)
|
||||
|
@ -427,7 +494,6 @@ namespace mamba
|
|||
std::forward<CompFunc>(merge_criteria)
|
||||
);
|
||||
};
|
||||
const auto nodes_by_type = variant_by_index(nodes);
|
||||
node_type_list<std::vector<old_node_id_list>> groups(nodes_by_type.size());
|
||||
std::transform(nodes_by_type.begin(), nodes_by_type.end(), groups.begin(), merge_func);
|
||||
return groups;
|
||||
|
@ -519,21 +585,21 @@ namespace mamba
|
|||
auto leaves_from = [&g](node_id n) -> vector_set<node_id>
|
||||
{
|
||||
auto leaves = std::vector<node_id>();
|
||||
g.for_each_leaf_from(n, [&leaves](node_id m) { leaves.push_back(m); });
|
||||
g.for_each_leaf_id_from(n, [&leaves](node_id m) { leaves.push_back(m); });
|
||||
return vector_set(std::move(leaves));
|
||||
};
|
||||
return (node_name(g.node(n1)) == node_name(g.node(n2)))
|
||||
// Merging conflicts would be counter-productive in explaining problems
|
||||
&& !(pbs.conflicts().in_conflict(n1, n2))
|
||||
// We don't want to use leaves_from for leaves because it resove to themselves,
|
||||
// We don't want to use leaves_from for leaves because it resolve to themselves,
|
||||
// preventing any merging.
|
||||
&& ((is_leaf(n1) && is_leaf(n2)) || (leaves_from(n1) == leaves_from(n2)))
|
||||
// We only check the parents for non-leaves meaning parents can "inject"
|
||||
// We only check the parents for leaves meaning parents can "inject"
|
||||
// themselves into a bigger problem
|
||||
&& ((!is_leaf(n1) && !is_leaf(n2)) || (g.predecessors(n1) == g.predecessors(n2)));
|
||||
}
|
||||
|
||||
using node_id_mapping = std::vector<CompressedProblemsGraph::node_id>;
|
||||
using node_id_mapping = std::map<ProblemsGraph::node_id, CompressedProblemsGraph::node_id>;
|
||||
|
||||
/**
|
||||
* For a given type of node, merge nodes together and add them to the new graph.
|
||||
|
@ -552,9 +618,6 @@ namespace mamba
|
|||
node_id_mapping& old_to_new
|
||||
)
|
||||
{
|
||||
// Check nothrow move for efficient push_back
|
||||
static_assert(std::is_nothrow_move_constructible_v<Node>);
|
||||
|
||||
auto get_old_node = [&old_graph](ProblemsGraph::node_id id)
|
||||
{
|
||||
auto node = old_graph.node(id);
|
||||
|
@ -591,11 +654,10 @@ namespace mamba
|
|||
auto new_graph = CompressedProblemsGraph::graph_t();
|
||||
const auto new_root_node = new_graph.add_node(CompressedProblemsGraph::RootNode());
|
||||
|
||||
auto old_to_new = std::vector<CompressedProblemsGraph::node_id>(
|
||||
old_graph.number_of_nodes()
|
||||
);
|
||||
auto old_to_new = node_id_mapping{};
|
||||
|
||||
auto old_ids_groups = merge_node_indices(
|
||||
old_graph.nodes(),
|
||||
node_id_by_type(pbs.graph()),
|
||||
std::forward<CompFunc>(merge_criteria)
|
||||
);
|
||||
|
||||
|
@ -656,15 +718,15 @@ namespace mamba
|
|||
{
|
||||
auto add_new_edge = [&](ProblemsGraph::node_id old_from, ProblemsGraph::node_id old_to)
|
||||
{
|
||||
auto const new_from = old_to_new[old_from];
|
||||
auto const new_to = old_to_new[old_to];
|
||||
auto const new_from = old_to_new.at(old_from);
|
||||
auto const new_to = old_to_new.at(old_to);
|
||||
if (!new_graph.has_edge(new_from, new_to))
|
||||
{
|
||||
new_graph.add_edge(new_from, new_to, CompressedProblemsGraph::edge_t());
|
||||
}
|
||||
new_graph.edge(new_from, new_to).insert(old_graph.edge(old_from, old_to));
|
||||
};
|
||||
old_graph.for_each_edge(add_new_edge);
|
||||
old_graph.for_each_edge_id(add_new_edge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -680,10 +742,10 @@ namespace mamba
|
|||
auto new_conflicts = CompressedProblemsGraph::conflicts_t();
|
||||
for (const auto& [old_from, old_with] : old_conflicts)
|
||||
{
|
||||
const auto new_from = old_to_new[old_from];
|
||||
const auto new_from = old_to_new.at(old_from);
|
||||
for (const auto old_to : old_with)
|
||||
{
|
||||
new_conflicts.add(new_from, old_to_new[old_to]);
|
||||
new_conflicts.add(new_from, old_to_new.at(old_to));
|
||||
}
|
||||
}
|
||||
return new_conflicts;
|
||||
|
@ -696,9 +758,9 @@ namespace mamba
|
|||
const merge_criteria_t& merge_criteria
|
||||
) -> CompressedProblemsGraph
|
||||
{
|
||||
graph_t graph;
|
||||
node_id root_node;
|
||||
node_id_mapping old_to_new;
|
||||
graph_t graph = {};
|
||||
node_id root_node = {};
|
||||
node_id_mapping old_to_new = {};
|
||||
if (merge_criteria)
|
||||
{
|
||||
auto merge_func =
|
||||
|
@ -957,18 +1019,6 @@ namespace mamba
|
|||
* Implementation of summary_msg *
|
||||
***********************************/
|
||||
|
||||
std::ostream& print_problem_summary_msg(std::ostream& out, const CompressedProblemsGraph&)
|
||||
{
|
||||
return out << "Could not solve for environment specs\n";
|
||||
}
|
||||
|
||||
std::string problem_summary_msg(const CompressedProblemsGraph& pbs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
print_problem_summary_msg(ss, pbs);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Implementation of problem_tree_msg *
|
||||
****************************************/
|
||||
|
@ -1077,7 +1127,7 @@ namespace mamba
|
|||
using TreeNodeIter = typename TreeNodeList::iterator;
|
||||
|
||||
vector_set<node_id> leaf_installables = {};
|
||||
std::vector<std::optional<Status>> m_node_visited;
|
||||
std::map<node_id, std::optional<Status>> m_node_visited = {};
|
||||
const CompressedProblemsGraph& m_pbs;
|
||||
|
||||
/**
|
||||
|
@ -1132,9 +1182,9 @@ namespace mamba
|
|||
*******************************/
|
||||
|
||||
TreeDFS::TreeDFS(const CompressedProblemsGraph& pbs)
|
||||
: m_node_visited(pbs.graph().number_of_nodes(), std::nullopt)
|
||||
, m_pbs(pbs)
|
||||
: m_pbs(pbs)
|
||||
{
|
||||
pbs.graph().for_each_node_id([&](auto id) { m_node_visited.emplace(id, std::nullopt); });
|
||||
}
|
||||
|
||||
auto TreeDFS::explore() -> std::vector<TreeNode>
|
||||
|
@ -1177,7 +1227,7 @@ namespace mamba
|
|||
{
|
||||
const bool has_predecessors = m_pbs.graph().predecessors(id).size() > 0;
|
||||
const bool has_successors = m_pbs.graph().successors(id).size() > 0;
|
||||
const bool is_visited = m_node_visited[id].has_value();
|
||||
const bool is_visited = m_node_visited.at(id).has_value();
|
||||
// We purposefully check if the node is a leaf before checking if it
|
||||
// is visited because showing a single node again is more intelligible than
|
||||
// refering to another one.
|
||||
|
@ -1335,11 +1385,6 @@ namespace mamba
|
|||
{
|
||||
return { out, status.value() };
|
||||
}
|
||||
if (node_uninstallable(id))
|
||||
{
|
||||
m_node_visited[id] = false;
|
||||
return { out, false };
|
||||
}
|
||||
|
||||
Status status = true;
|
||||
// TODO(C++20) an enumerate view ``views::zip(views::iota(), children_ids)``
|
||||
|
@ -1348,7 +1393,8 @@ namespace mamba
|
|||
{
|
||||
const auto children_pos = i == successors.size() - 1 ? SiblingNumber::last
|
||||
: SiblingNumber::not_last;
|
||||
Status child_status;
|
||||
Status child_status = true;
|
||||
assert(children.size() > 0);
|
||||
if (children.size() > 1)
|
||||
{
|
||||
std::tie(out, child_status) = visit_split(children, children_pos, ongoing, out);
|
||||
|
@ -1362,6 +1408,15 @@ namespace mamba
|
|||
++i;
|
||||
}
|
||||
|
||||
// Node installability status is checked *after* visiting the children because there
|
||||
// are cases where both non-leaf nodes and their children have conflicts so we need
|
||||
// to visit children to exaplin conflicts.
|
||||
// In most cases though, only leaves would have conflicts and the loop above would be
|
||||
// empty in such case.
|
||||
// Warning node_uninstallable has side effects and must be called unconditionally
|
||||
// (first here).
|
||||
status = !node_uninstallable(id) && status;
|
||||
|
||||
m_node_visited[id] = status;
|
||||
return { out, status };
|
||||
}
|
||||
|
|
|
@ -669,7 +669,8 @@ namespace mamba
|
|||
const auto& ctx = Context::instance();
|
||||
out << "Could not solve for environment specs\n";
|
||||
const auto pbs = ProblemsGraph::from_solver(*this, pool());
|
||||
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs);
|
||||
const auto pbs_simplified = simplify_conflicts(pbs);
|
||||
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs_simplified);
|
||||
print_problem_tree_msg(
|
||||
out,
|
||||
cp_pbs,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <fmt/format.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <solv/solver.h>
|
||||
|
||||
#include "mamba/core/channel.hpp"
|
||||
#include "mamba/core/mamba_fs.hpp"
|
||||
|
@ -34,13 +35,40 @@ namespace mamba
|
|||
EXPECT_EQ(c.size(), 0);
|
||||
EXPECT_FALSE(c.has_conflict(0));
|
||||
EXPECT_FALSE(c.in_conflict(0, 1));
|
||||
c.add(0, 1);
|
||||
c.add(1, 2);
|
||||
EXPECT_TRUE(c.add(0, 1));
|
||||
EXPECT_TRUE(c.add(1, 2));
|
||||
EXPECT_FALSE(c.add(1, 2));
|
||||
EXPECT_TRUE(c.has_conflict(0));
|
||||
EXPECT_TRUE(c.in_conflict(0, 1));
|
||||
EXPECT_TRUE(c.in_conflict(1, 2));
|
||||
EXPECT_TRUE(c.has_conflict(2));
|
||||
EXPECT_FALSE(c.in_conflict(0, 2));
|
||||
// With same
|
||||
EXPECT_TRUE(c.add(5, 5));
|
||||
EXPECT_TRUE(c.has_conflict(5));
|
||||
EXPECT_TRUE(c.in_conflict(5, 5));
|
||||
}
|
||||
|
||||
TEST(conflict_map, remove)
|
||||
{
|
||||
auto c = conflict_map<std::size_t>({ { 1, 1 }, { 1, 2 }, { 1, 3 }, { 2, 4 } });
|
||||
ASSERT_EQ(c.size(), 4);
|
||||
|
||||
ASSERT_TRUE(c.in_conflict(2, 4));
|
||||
ASSERT_TRUE(c.in_conflict(4, 2));
|
||||
EXPECT_TRUE(c.remove(2, 4));
|
||||
EXPECT_FALSE(c.in_conflict(4, 2));
|
||||
EXPECT_FALSE(c.in_conflict(2, 4));
|
||||
EXPECT_TRUE(c.has_conflict(2));
|
||||
EXPECT_FALSE(c.has_conflict(4));
|
||||
|
||||
EXPECT_FALSE(c.remove(2, 4));
|
||||
|
||||
EXPECT_TRUE(c.remove(1));
|
||||
EXPECT_FALSE(c.has_conflict(1));
|
||||
EXPECT_FALSE(c.in_conflict(1, 1));
|
||||
EXPECT_FALSE(c.in_conflict(1, 2));
|
||||
EXPECT_FALSE(c.in_conflict(3, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -399,6 +427,12 @@ namespace mamba
|
|||
return *solver;
|
||||
}
|
||||
|
||||
auto create_numba() -> MSolver&
|
||||
{
|
||||
static auto solver = create_conda_forge({ "python=3.11", "numba<0.56" });
|
||||
return *solver;
|
||||
}
|
||||
|
||||
class Problem : public testing::TestWithParam<decltype(&create_basic_conflict)>
|
||||
{
|
||||
};
|
||||
|
@ -420,27 +454,6 @@ namespace mamba
|
|||
);
|
||||
};
|
||||
|
||||
auto has_problem_type(const ProblemsGraph::node_t& node) -> bool
|
||||
{
|
||||
return std::visit(
|
||||
[](const auto& n) -> bool
|
||||
{
|
||||
using Node = std::remove_const_t<std::remove_reference_t<decltype(n)>>;
|
||||
if constexpr (std::is_same_v<Node, ProblemsGraph::RootNode>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if constexpr (std::is_same_v<Node, ProblemsGraph::PackageNode>)
|
||||
{
|
||||
return n.problem_type.has_value();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
node
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
TEST_P(Problem, constructor)
|
||||
{
|
||||
auto& solver = std::invoke(GetParam());
|
||||
|
@ -450,33 +463,34 @@ namespace mamba
|
|||
const auto& g = pbs.graph();
|
||||
|
||||
EXPECT_GE(g.number_of_nodes(), 1);
|
||||
for (std::size_t id = 0; id < g.number_of_nodes(); ++id)
|
||||
{
|
||||
const auto& node = g.node(id);
|
||||
if (is_virtual_package(node))
|
||||
g.for_each_node_id(
|
||||
[&](auto id)
|
||||
{
|
||||
const auto& node = g.node(id);
|
||||
// Currently we do not make assumption about virtual package since
|
||||
// we are not sure we are including them the same way than they would be in practice
|
||||
break;
|
||||
// we are not sure we are including them the same way than they would be in
|
||||
// practice
|
||||
if (!is_virtual_package(node))
|
||||
{
|
||||
if (g.in_degree(id) == 0)
|
||||
{
|
||||
// Only one root node
|
||||
EXPECT_EQ(id, pbs.root_node());
|
||||
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::RootNode>(node));
|
||||
}
|
||||
else if (g.out_degree(id) == 0)
|
||||
{
|
||||
EXPECT_FALSE(std::holds_alternative<ProblemsGraph::RootNode>(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::PackageNode>(node));
|
||||
}
|
||||
// All nodes reachable from the root
|
||||
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
|
||||
}
|
||||
}
|
||||
else if (g.in_degree(id) == 0)
|
||||
{
|
||||
// Only one root node
|
||||
EXPECT_EQ(id, pbs.root_node());
|
||||
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::RootNode>(node));
|
||||
}
|
||||
else if (g.out_degree(id) == 0)
|
||||
{
|
||||
EXPECT_FALSE(std::holds_alternative<ProblemsGraph::RootNode>(node));
|
||||
EXPECT_TRUE(has_problem_type(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(std::holds_alternative<ProblemsGraph::PackageNode>(node));
|
||||
}
|
||||
// All nodes reachable from the root
|
||||
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
|
||||
}
|
||||
);
|
||||
|
||||
const auto& conflicts = pbs.conflicts();
|
||||
for (const auto& [n, _] : conflicts)
|
||||
|
@ -488,6 +502,34 @@ namespace mamba
|
|||
}
|
||||
}
|
||||
|
||||
TEST_P(Problem, simplify_conflicts)
|
||||
{
|
||||
auto& solver = std::invoke(GetParam());
|
||||
const auto solved = solver.try_solve();
|
||||
ASSERT_FALSE(solved);
|
||||
const auto& pbs = ProblemsGraph::from_solver(solver, solver.pool());
|
||||
const auto& pbs_simplified = simplify_conflicts(pbs);
|
||||
const auto& graph_simplified = pbs_simplified.graph();
|
||||
|
||||
EXPECT_GE(graph_simplified.number_of_nodes(), 1);
|
||||
EXPECT_LE(graph_simplified.number_of_nodes(), pbs.graph().number_of_nodes());
|
||||
|
||||
for (const auto& [id, _] : pbs_simplified.conflicts())
|
||||
{
|
||||
const auto& node = graph_simplified.node(id);
|
||||
// Currently we do not make assumption about virtual package since
|
||||
// we are not sure we are including them the same way than they would be in
|
||||
// practice
|
||||
if (!is_virtual_package(node))
|
||||
{
|
||||
EXPECT_TRUE(graph_simplified.has_node(id));
|
||||
// Unfortunately not all conflicts are on leaves
|
||||
// EXPECT_EQ(graph_simplified.out_degree(id), 0);
|
||||
EXPECT_TRUE(is_reachable(graph_simplified, pbs_simplified.root_node(), id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(satifiability_error, NamedList)
|
||||
{
|
||||
auto l = CompressedProblemsGraph::PackageListNode();
|
||||
|
@ -528,37 +570,38 @@ namespace mamba
|
|||
const auto solved = solver.try_solve();
|
||||
ASSERT_FALSE(solved);
|
||||
const auto pbs = ProblemsGraph::from_solver(solver, solver.pool());
|
||||
const auto cp_pbs = CpPbGr::from_problems_graph(pbs);
|
||||
const auto cp_pbs = CpPbGr::from_problems_graph(simplify_conflicts(pbs));
|
||||
const auto& cp_g = cp_pbs.graph();
|
||||
|
||||
EXPECT_GE(pbs.graph().number_of_nodes(), cp_g.number_of_nodes());
|
||||
EXPECT_GE(cp_g.number_of_nodes(), 1);
|
||||
for (std::size_t id = 0; id < cp_g.number_of_nodes(); ++id)
|
||||
{
|
||||
const auto& node = cp_g.node(id);
|
||||
if (is_virtual_package(node))
|
||||
cp_g.for_each_node_id(
|
||||
[&](auto id)
|
||||
{
|
||||
const auto& node = cp_g.node(id);
|
||||
// Currently we do not make assumption about virtual package since
|
||||
// we are not sure we are including them the same way than they would be in
|
||||
break;
|
||||
if (!is_virtual_package(node))
|
||||
{
|
||||
if (cp_g.in_degree(id) == 0)
|
||||
{
|
||||
// Only one root node
|
||||
EXPECT_EQ(id, pbs.root_node());
|
||||
EXPECT_TRUE(std::holds_alternative<CpPbGr::RootNode>(node));
|
||||
}
|
||||
else if (cp_g.out_degree(id) == 0)
|
||||
{
|
||||
EXPECT_FALSE(std::holds_alternative<CpPbGr::RootNode>(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(std::holds_alternative<CpPbGr::PackageListNode>(node));
|
||||
}
|
||||
// All nodes reachable from the root
|
||||
EXPECT_TRUE(is_reachable(cp_g, cp_pbs.root_node(), id));
|
||||
}
|
||||
}
|
||||
else if (cp_g.in_degree(id) == 0)
|
||||
{
|
||||
// Only one root node
|
||||
EXPECT_EQ(id, pbs.root_node());
|
||||
EXPECT_TRUE(std::holds_alternative<CpPbGr::RootNode>(node));
|
||||
}
|
||||
else if (cp_g.out_degree(id) == 0)
|
||||
{
|
||||
EXPECT_FALSE(std::holds_alternative<CpPbGr::RootNode>(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(std::holds_alternative<CpPbGr::PackageListNode>(node));
|
||||
}
|
||||
// All nodes reachable from the root
|
||||
EXPECT_TRUE(is_reachable(pbs.graph(), pbs.root_node(), id));
|
||||
}
|
||||
);
|
||||
|
||||
const auto& conflicts = cp_pbs.conflicts();
|
||||
for (const auto& [n, _] : conflicts)
|
||||
|
@ -572,26 +615,30 @@ namespace mamba
|
|||
|
||||
TEST_P(Problem, problem_tree_str)
|
||||
{
|
||||
using CpPbGr = CompressedProblemsGraph;
|
||||
|
||||
auto& solver = std::invoke(GetParam());
|
||||
const auto solved = solver.try_solve();
|
||||
ASSERT_FALSE(solved);
|
||||
const auto pbs = ProblemsGraph::from_solver(solver, solver.pool());
|
||||
const auto cp_pbs = CompressedProblemsGraph::from_problems_graph(pbs);
|
||||
const auto cp_pbs = CpPbGr::from_problems_graph(simplify_conflicts(pbs));
|
||||
const auto message = problem_tree_msg(cp_pbs);
|
||||
|
||||
auto message_contains = [&](const auto& node)
|
||||
auto message_contains = [&message](const auto& node)
|
||||
{
|
||||
using Node = std::remove_cv_t<std::remove_reference_t<decltype(node)>>;
|
||||
if constexpr (!std::is_same_v<Node, CompressedProblemsGraph::RootNode>)
|
||||
if constexpr (!std::is_same_v<Node, CpPbGr::RootNode>)
|
||||
{
|
||||
EXPECT_TRUE(contains(message, node.name()));
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& node : cp_pbs.graph().nodes())
|
||||
{
|
||||
std::visit(message_contains, node);
|
||||
}
|
||||
cp_pbs.graph().for_each_node_id(
|
||||
[&message_contains, &g = cp_pbs.graph()](auto id)
|
||||
{
|
||||
std::visit(message_contains, g.node(id)); //
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
@ -609,7 +656,8 @@ namespace mamba
|
|||
create_r_base,
|
||||
create_scip,
|
||||
create_jupyterlab,
|
||||
create_double_python
|
||||
create_double_python,
|
||||
create_numba
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace mamba
|
|||
EXPECT_NE(vector_set<int>({ 2 }), vector_set<int>({}));
|
||||
}
|
||||
|
||||
TEST(vector_set, insertion)
|
||||
TEST(vector_set, insert)
|
||||
{
|
||||
auto s = vector_set<int>();
|
||||
s.insert(33);
|
||||
|
@ -53,6 +53,19 @@ namespace mamba
|
|||
EXPECT_EQ(s, vector_set<int>({ 0, 17, 22, 33 }));
|
||||
}
|
||||
|
||||
TEST(vector_set, erase)
|
||||
{
|
||||
auto s = vector_set<int>{ 4, 3, 2, 1 };
|
||||
EXPECT_EQ(s.erase(4), 1);
|
||||
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
|
||||
EXPECT_EQ(s.erase(4), 0);
|
||||
EXPECT_EQ(s, vector_set<int>({ 1, 2, 3 }));
|
||||
|
||||
const auto it = s.erase(s.begin());
|
||||
EXPECT_EQ(it, s.begin());
|
||||
EXPECT_EQ(s, vector_set<int>({ 2, 3 }));
|
||||
}
|
||||
|
||||
TEST(vector_set, contains)
|
||||
{
|
||||
const auto s = vector_set<int>({ 1, 3, 4, 5 });
|
||||
|
@ -170,11 +183,16 @@ namespace mamba
|
|||
TEST(graph, build_simple)
|
||||
{
|
||||
const auto g = build_graph();
|
||||
using node_list = decltype(g)::node_list;
|
||||
using node_map = decltype(g)::node_map;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
EXPECT_EQ(g.number_of_nodes(), 7ul);
|
||||
EXPECT_EQ(g.number_of_edges(), 7ul);
|
||||
EXPECT_EQ(g.nodes(), node_list({ 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5 }));
|
||||
EXPECT_EQ(
|
||||
g.nodes(),
|
||||
node_map(
|
||||
{ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 }, { 3, 3.5 }, { 4, 4.5 }, { 5, 5.5 }, { 6, 6.5 } }
|
||||
)
|
||||
);
|
||||
EXPECT_EQ(g.successors(0u), node_id_list({ 1u, 2u }));
|
||||
EXPECT_EQ(g.successors(1u), node_id_list({ 3u, 4u }));
|
||||
EXPECT_EQ(g.successors(2u), node_id_list({ 3u, 5u }));
|
||||
|
@ -188,11 +206,11 @@ namespace mamba
|
|||
TEST(graph, build_edge_data)
|
||||
{
|
||||
const auto g = build_edge_data_graph();
|
||||
using node_list = decltype(g)::node_list;
|
||||
using node_map = decltype(g)::node_map;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
EXPECT_EQ(g.number_of_nodes(), 3ul);
|
||||
EXPECT_EQ(g.number_of_edges(), 2ul);
|
||||
EXPECT_EQ(g.nodes(), node_list({ 0.5, 1.5, 2.5 }));
|
||||
EXPECT_EQ(g.nodes(), node_map({ { 0, 0.5 }, { 1, 1.5 }, { 2, 2.5 } }));
|
||||
EXPECT_EQ(g.successors(0ul), node_id_list({ 1ul }));
|
||||
EXPECT_EQ(g.successors(1ul), node_id_list({ 2ul }));
|
||||
EXPECT_EQ(g.successors(2ul), node_id_list());
|
||||
|
@ -233,6 +251,63 @@ namespace mamba
|
|||
EXPECT_EQ(g.edge(0ul, 1ul), new_edge_val);
|
||||
}
|
||||
|
||||
TEST(graph, remove_edge)
|
||||
{
|
||||
auto g = build_edge_data_graph();
|
||||
const auto n_edges_init = g.number_of_edges();
|
||||
|
||||
ASSERT_FALSE(g.has_edge(1, 0));
|
||||
ASSERT_TRUE(g.has_edge(0, 1));
|
||||
EXPECT_FALSE(g.remove_edge(1, 0));
|
||||
EXPECT_EQ(g.number_of_edges(), n_edges_init);
|
||||
EXPECT_FALSE(g.has_edge(1, 0));
|
||||
EXPECT_TRUE(g.has_edge(0, 1));
|
||||
|
||||
ASSERT_TRUE(g.has_edge(0, 1));
|
||||
EXPECT_TRUE(g.remove_edge(0, 1));
|
||||
EXPECT_EQ(g.number_of_edges(), n_edges_init - 1u);
|
||||
EXPECT_FALSE(g.has_edge(0, 1));
|
||||
EXPECT_EQ(g.edges().count({ 0, 1 }), 0);
|
||||
}
|
||||
|
||||
TEST(graph, remove_node)
|
||||
{
|
||||
auto g = build_edge_data_graph();
|
||||
|
||||
ASSERT_TRUE(g.has_node(0));
|
||||
ASSERT_TRUE(g.has_node(1));
|
||||
ASSERT_TRUE(g.has_node(2));
|
||||
ASSERT_TRUE(g.has_edge(0, 1));
|
||||
ASSERT_TRUE(g.has_edge(1, 2));
|
||||
|
||||
const auto n_edges_init = g.number_of_edges();
|
||||
const auto n_nodes_init = g.number_of_nodes();
|
||||
const auto node_1_degree = g.in_degree(1) + g.out_degree(1);
|
||||
|
||||
EXPECT_TRUE(g.remove_node(1));
|
||||
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
|
||||
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
|
||||
EXPECT_EQ(g.number_of_edges(), g.edges().size());
|
||||
EXPECT_TRUE(g.has_node(0));
|
||||
EXPECT_FALSE(g.has_node(1));
|
||||
EXPECT_TRUE(g.has_node(2));
|
||||
EXPECT_EQ(g.in_degree(1), 0);
|
||||
EXPECT_EQ(g.out_degree(1), 0);
|
||||
EXPECT_FALSE(g.has_edge(0, 1));
|
||||
EXPECT_FALSE(g.has_edge(1, 2));
|
||||
g.for_each_node_id([&](auto id) { EXPECT_TRUE(g.has_node(id)); });
|
||||
|
||||
EXPECT_FALSE(g.remove_node(1));
|
||||
EXPECT_EQ(g.number_of_nodes(), n_nodes_init - 1u);
|
||||
EXPECT_EQ(g.number_of_edges(), n_edges_init - node_1_degree);
|
||||
EXPECT_EQ(g.number_of_edges(), g.edges().size());
|
||||
|
||||
const auto new_id = g.add_node(.7);
|
||||
EXPECT_EQ(new_id, n_nodes_init); // Ids are not invalidated so new id is used
|
||||
EXPECT_FALSE(g.has_node(1)); // Old id is not being confused
|
||||
EXPECT_EQ(g.number_of_nodes(), n_nodes_init);
|
||||
}
|
||||
|
||||
TEST(graph, degree)
|
||||
{
|
||||
const auto g = build_graph();
|
||||
|
@ -244,12 +319,27 @@ namespace mamba
|
|||
EXPECT_EQ(g.in_degree(6), 1);
|
||||
}
|
||||
|
||||
TEST(graph, for_each_node)
|
||||
{
|
||||
const auto g = build_graph();
|
||||
using node_id = decltype(g)::node_id;
|
||||
std::size_t n_nodes = 0;
|
||||
g.for_each_node_id(
|
||||
[&](node_id id)
|
||||
{
|
||||
EXPECT_TRUE(g.has_node(id));
|
||||
++n_nodes;
|
||||
}
|
||||
);
|
||||
EXPECT_EQ(n_nodes, g.number_of_nodes());
|
||||
}
|
||||
|
||||
TEST(graph, for_each_edge)
|
||||
{
|
||||
const auto g = build_graph();
|
||||
using node_id = decltype(g)::node_id;
|
||||
std::size_t n_edges = 0;
|
||||
g.for_each_edge(
|
||||
g.for_each_edge_id(
|
||||
[&g, &n_edges](node_id from, node_id to)
|
||||
{
|
||||
EXPECT_TRUE(g.has_edge(from, to));
|
||||
|
@ -265,7 +355,7 @@ namespace mamba
|
|||
using node_id = decltype(g)::node_id;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
auto leaves = node_id_list();
|
||||
g.for_each_leaf([&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
g.for_each_leaf_id([&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
EXPECT_EQ(leaves, node_id_list({ 4ul, 5ul, 6ul }));
|
||||
}
|
||||
|
||||
|
@ -275,7 +365,7 @@ namespace mamba
|
|||
using node_id = decltype(g)::node_id;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
auto leaves = node_id_list();
|
||||
g.for_each_leaf_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
g.for_each_leaf_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
EXPECT_EQ(leaves, node_id_list({ 5ul, 6ul }));
|
||||
}
|
||||
|
||||
|
@ -285,7 +375,7 @@ namespace mamba
|
|||
using node_id = decltype(g)::node_id;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
auto roots = node_id_list();
|
||||
g.for_each_root([&roots](node_id root) { roots.insert(root); });
|
||||
g.for_each_root_id([&roots](node_id root) { roots.insert(root); });
|
||||
EXPECT_EQ(roots, node_id_list({ 0ul }));
|
||||
}
|
||||
|
||||
|
@ -295,7 +385,7 @@ namespace mamba
|
|||
using node_id = decltype(g)::node_id;
|
||||
using node_id_list = decltype(g)::node_id_list;
|
||||
auto leaves = node_id_list();
|
||||
g.for_each_root_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
g.for_each_root_id_from(2ul, [&leaves](node_id leaf) { leaves.insert(leaf); });
|
||||
EXPECT_EQ(leaves, node_id_list({ 0ul }));
|
||||
}
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ __all__ = [
|
|||
"get_virtual_packages",
|
||||
"ostream_redirect",
|
||||
"sign",
|
||||
"simplify_conflicts",
|
||||
"transmute",
|
||||
]
|
||||
|
||||
|
@ -219,7 +220,7 @@ class CompressedProblemsGraph:
|
|||
def __init__(self) -> None: ...
|
||||
def __iter__(self) -> typing.Iterator: ...
|
||||
def __len__(self) -> int: ...
|
||||
def add(self, arg0: int, arg1: int) -> None: ...
|
||||
def add(self, arg0: int, arg1: int) -> bool: ...
|
||||
def clear(self) -> None: ...
|
||||
def conflicts(self, arg0: int) -> typing.Set[int]: ...
|
||||
def has_conflict(self, arg0: int) -> bool: ...
|
||||
|
@ -257,7 +258,7 @@ class CompressedProblemsGraph:
|
|||
) -> typing.Tuple[str, int]: ...
|
||||
pass
|
||||
|
||||
class DependencyListList:
|
||||
class DependencyList:
|
||||
def __bool__(self) -> bool: ...
|
||||
def __init__(self) -> None: ...
|
||||
def __iter__(self) -> typing.Iterator: ...
|
||||
|
@ -365,18 +366,18 @@ class CompressedProblemsGraph:
|
|||
def graph(
|
||||
self,
|
||||
) -> typing.Tuple[
|
||||
typing.List[
|
||||
typing.Dict[
|
||||
int,
|
||||
typing.Union[
|
||||
ProblemsGraph.RootNode,
|
||||
CompressedProblemsGraph.PackageListNode,
|
||||
CompressedProblemsGraph.UnresolvedDependencyListNode,
|
||||
CompressedProblemsGraph.ConstraintListNode,
|
||||
]
|
||||
],
|
||||
],
|
||||
typing.Dict[typing.Tuple[int, int], CompressedProblemsGraph.DependencyListList],
|
||||
typing.Dict[typing.Tuple[int, int], CompressedProblemsGraph.DependencyList],
|
||||
]: ...
|
||||
def root_node(self) -> int: ...
|
||||
def summary_message(self) -> str: ...
|
||||
def tree_message(self) -> str: ...
|
||||
pass
|
||||
|
||||
|
@ -1043,7 +1044,6 @@ class ProblemsGraph:
|
|||
pass
|
||||
|
||||
class ConstraintNode(MatchSpec):
|
||||
problem_type: libmambapy.bindings.SolverRuleinfo # value = <SolverRuleinfo.SOLVER_RULE_PKG_CONSTRAINS: 267>
|
||||
pass
|
||||
|
||||
class PackageNode(PackageInfo):
|
||||
|
@ -1053,14 +1053,6 @@ class ProblemsGraph:
|
|||
pass
|
||||
|
||||
class UnresolvedDependencyNode(MatchSpec):
|
||||
@property
|
||||
def problem_type(self) -> SolverRuleinfo:
|
||||
"""
|
||||
:type: SolverRuleinfo
|
||||
"""
|
||||
@problem_type.setter
|
||||
def problem_type(self, arg0: SolverRuleinfo) -> None:
|
||||
pass
|
||||
pass
|
||||
def conflicts(self) -> ProblemsGraph.ConflictMap: ...
|
||||
@staticmethod
|
||||
|
@ -1068,13 +1060,14 @@ class ProblemsGraph:
|
|||
def graph(
|
||||
self,
|
||||
) -> typing.Tuple[
|
||||
typing.List[
|
||||
typing.Dict[
|
||||
int,
|
||||
typing.Union[
|
||||
ProblemsGraph.RootNode,
|
||||
ProblemsGraph.PackageNode,
|
||||
ProblemsGraph.UnresolvedDependencyNode,
|
||||
ProblemsGraph.ConstraintNode,
|
||||
]
|
||||
],
|
||||
],
|
||||
typing.Dict[typing.Tuple[int, int], MatchSpec],
|
||||
]: ...
|
||||
|
@ -1451,6 +1444,9 @@ def get_virtual_packages() -> typing.List[PackageInfo]:
|
|||
def sign(data: str, secret_key: str) -> str:
|
||||
pass
|
||||
|
||||
def simplify_conflicts(arg0: ProblemsGraph) -> ProblemsGraph:
|
||||
pass
|
||||
|
||||
def transmute(
|
||||
source_package: Path,
|
||||
destination_package: Path,
|
||||
|
|
|
@ -263,10 +263,8 @@ PYBIND11_MODULE(bindings, m)
|
|||
|
||||
py::class_<PbGraph::RootNode>(pyPbGraph, "RootNode").def(py::init<>());
|
||||
py::class_<PbGraph::PackageNode, PackageInfo>(pyPbGraph, "PackageNode");
|
||||
py::class_<PbGraph::UnresolvedDependencyNode, MatchSpec>(pyPbGraph, "UnresolvedDependencyNode")
|
||||
.def_readwrite("problem_type", &PbGraph::UnresolvedDependencyNode::problem_type);
|
||||
py::class_<PbGraph::ConstraintNode, MatchSpec>(pyPbGraph, "ConstraintNode")
|
||||
.def_readonly_static("problem_type", &PbGraph::ConstraintNode::problem_type);
|
||||
py::class_<PbGraph::UnresolvedDependencyNode, MatchSpec>(pyPbGraph, "UnresolvedDependencyNode");
|
||||
py::class_<PbGraph::ConstraintNode, MatchSpec>(pyPbGraph, "ConstraintNode");
|
||||
|
||||
py::class_<PbGraph::conflicts_t>(pyPbGraph, "ConflictMap")
|
||||
.def(py::init([]() { return PbGraph::conflicts_t(); }))
|
||||
|
@ -297,6 +295,8 @@ PYBIND11_MODULE(bindings, m)
|
|||
}
|
||||
);
|
||||
|
||||
m.def("simplify_conflicts", &simplify_conflicts);
|
||||
|
||||
using CpPbGraph = CompressedProblemsGraph;
|
||||
auto pyCpPbGraph = py::class_<CpPbGraph>(m, "CompressedProblemsGraph");
|
||||
|
||||
|
@ -309,7 +309,7 @@ PYBIND11_MODULE(bindings, m)
|
|||
py::class_<CpPbGraph::UnresolvedDependencyListNode>(pyCpPbGraph, "UnresolvedDependencyListNode")
|
||||
);
|
||||
bind_NamedList(py::class_<CpPbGraph::ConstraintListNode>(pyCpPbGraph, "ConstraintListNode"));
|
||||
bind_NamedList(py::class_<CpPbGraph::edge_t>(pyCpPbGraph, "DependencyListList"));
|
||||
bind_NamedList(py::class_<CpPbGraph::edge_t>(pyCpPbGraph, "DependencyList"));
|
||||
pyCpPbGraph.def_property_readonly_static(
|
||||
"ConflictMap",
|
||||
[](py::handle) { return py::type::of<PbGraph::conflicts_t>(); }
|
||||
|
@ -330,7 +330,6 @@ PYBIND11_MODULE(bindings, m)
|
|||
return std::pair(g.nodes(), g.edges());
|
||||
}
|
||||
)
|
||||
.def("summary_message", [](const CpPbGraph& self) { return problem_summary_msg(self); })
|
||||
.def("tree_message", [](const CpPbGraph& self) { return problem_tree_msg(self); });
|
||||
|
||||
py::class_<History>(m, "History")
|
||||
|
|
Loading…
Reference in New Issue