implement reverse dependency walker and tree printing

This commit is contained in:
Wolf Vollprecht 2020-05-12 19:43:38 +02:00
parent 289672dc94
commit b29a53f64a
5 changed files with 124 additions and 63 deletions

View File

@ -9,19 +9,17 @@ find_package(Threads REQUIRED)
find_library(LIBSOLV_LIBRARIES NAMES solv)
find_library(LIBSOLVEXT_LIBRARIES NAMES solvext)
find_package(CURL REQUIRED)
find_library(ARCHIVE_LIBRARIES NAMES archive)
find_package(LibArchive REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(tabulate REQUIRED)
set(MAMBA_REQUIRED_LIBS
${LIBSOLV_LIBRARIES}
${LIBSOLVEXT_LIBRARIES}
${ARCHIVE_LIBRARIES}
${LibArchive_LIBRARIES}
${CURL_LIBRARIES}
${OPENSSL_CRYPTO_LIBRARY}
${OPENSSL_LIBRARIES}
nlohmann_json::nlohmann_json
tabulate::tabulate
)
message("Found libraries: ${MAMBA_REQUIRED_LIBS}")

View File

@ -23,7 +23,7 @@ conda install mamba -c conda-forge
Make sure to have the following requirements in your conda environment:
- `conda install pybind11 libsolv libarchive libcurl nlohmann_json pip "cpp-tabulate>=1.2" -c conda-forge`
- `conda install pybind11 libsolv libarchive libcurl nlohmann_json pip -c conda-forge`
If you build mamba in a different environment than base, you must also install conda in
that environment:

View File

@ -4,12 +4,9 @@
#include <functional>
#include <string_view>
#include "output.hpp"
#include "pool.hpp"
namespace tabulate {
class Table;
}
extern "C"
{
#include "solv/repo.h"
@ -21,8 +18,6 @@ extern "C"
namespace mamba
{
std::string cut_repo_name(std::ostream& out, const std::string_view& reponame);
void solvable_to_stream(std::ostream& out, Solvable* s, int row_count,
tabulate::Table& query_result);
void print_dep_graph(std::ostream& out, Solvable* s, const std::string& solv_str, int level, int max_level, bool last, const std::string& prefix);
class Query
@ -32,7 +27,7 @@ namespace mamba
Query(MPool& pool);
std::string find(const std::string& query);
std::string whatrequires(const std::string& query);
std::string whatrequires(const std::string& query, bool tree);
std::string dependencytree(const std::string& query);
private:

View File

@ -598,7 +598,11 @@ def repoquery(args, parser):
repo.set_installed()
repos.append(repo)
if not args.installed:
only_installed = True
if args.subcmd == "search" and args.installed == False:
only_installed = False
if not only_installed:
index = get_index(channel_urls=index_args['channel_urls'],
prepend=index_args['prepend'], platform=None,
use_local=index_args['use_local'], use_cache=index_args['use_cache'],
@ -614,15 +618,15 @@ def repoquery(args, parser):
repos.append(repo)
print("\nExecuting the query %s\n" % args.query)
print("\nExecuting the query %s\n" % args.package_query)
query = api.Query(pool)
if args.whatrequires:
print(query.whatrequires(args.query))
elif args.tree:
print(query.dependencytree(args.query))
else:
print(query.find(args.query))
if args.subcmd == "whoneeds":
print(query.whatrequires(args.package_query, args.tree))
if args.subcmd == "depends":
print(query.dependencytree(args.package_query))
if args.subcmd == "search":
print(query.find(args.package_query))
def do_call(args, parser):
@ -656,42 +660,49 @@ def configure_parser_repoquery(sub_parsers):
example = ("""
Examples:
conda repoquery xtensor>=0.18
mamba repoquery search xtensor>=0.18
mamba repoquery depends xtensor
mamba repoquery whoneeds xtl
""")
import argparse
from argparse import SUPPRESS
p = sub_parsers.add_parser(
'repoquery',
description=descr,
help=help,
epilog=example,
epilog=example
)
p.add_argument(
'query',
action="store",
nargs='?',
help="Package query.",
)
p.add_argument(
"--whatrequires",
subsub_parser = p.add_subparsers(dest='subcmd')
package_cmds = argparse.ArgumentParser(add_help=False)
package_cmds.add_argument('package_query', help='the target package')
package_cmds.add_argument(
"-i", "--installed",
action="store_true",
help=SUPPRESS,
help=SUPPRESS
)
p.add_argument(
"--installed",
action="store_true",
help=SUPPRESS,
view_cmds = argparse.ArgumentParser(add_help=False)
view_cmds.add_argument(
"-t", "--tree",
action="store_true"
)
p.add_argument(
"--tree",
action="store_true",
help=SUPPRESS,
subsub_parser.add_parser('whoneeds',
help='shows packages that depends on this package',
parents=[package_cmds, view_cmds]
)
subsub_parser.add_parser('depends',
help='shows packages that depends on this package',
parents=[package_cmds, view_cmds]
)
subsub_parser.add_parser('search',
help='shows packages that depends on this package',
parents=[package_cmds]
)
from conda.cli import conda_argparse
@ -702,7 +713,6 @@ Examples:
p.set_defaults(func='.main_repoquery.execute')
return p
def _wrapped_main(*args, **kwargs):
if len(args) == 1:
args = args + ('-h',)

View File

@ -11,8 +11,6 @@ extern "C" {
#include <solv/evr.h>
}
#include "tabulate/table.hpp"
namespace mamba
{
namespace printers
@ -133,6 +131,45 @@ namespace mamba
}
}
void reverse_walk_graph(printers::Node<std::string>& parent,
Solvable* s,
std::set<Solvable*>& visited_solvs)
{
if (s)
{
auto* pool = s->repo->pool;
// figure out who requires `s`
Queue solvables;
queue_init(&solvables);
pool_whatmatchesdep(pool, SOLVABLE_REQUIRES, s->name, &solvables, -1);
if (solvables.count != 0)
{
Solvable* rs;
for (int i = 0; i < solvables.count; i++)
{
rs = pool_id2solvable(pool, solvables.elements[i]);
if (visited_solvs.count(rs) == 0)
{
printers::Node<std::string> next_node(concat(pool_id2str(pool, rs->name), " [", pool_id2str(pool, rs->evr), "]"));
visited_solvs.insert(rs);
reverse_walk_graph(next_node, rs, visited_solvs);
parent.add_child(next_node);
}
else
{
parent.add_child(concat("\033[2m", pool_id2str(pool, rs->name), " already visited", "\033[00m"));
}
}
queue_free(&solvables);
}
return;
}
}
/************************
* Query implementation *
************************/
@ -183,7 +220,7 @@ namespace mamba
return out.str();
}
std::string Query::whatrequires(const std::string& query)
std::string Query::whatrequires(const std::string& query, bool tree)
{
Queue job, solvables;
queue_init(&job);
@ -199,26 +236,49 @@ namespace mamba
throw std::runtime_error("Could not generate query for " + query);
}
pool_whatmatchesdep(m_pool.get(), SOLVABLE_REQUIRES, id, &solvables, -1);
std::stringstream out;
if (solvables.count == 0)
if (tree)
{
out << "No entries matching \"" << query << "\" found";
}
printers::Table whatrequires_table_results({"Name", "Version", "Build", "Channel"});
for (int i = 0; i < solvables.count; i++)
{
Solvable* s = pool_id2solvable(m_pool.get(), solvables.elements[i]);
solvable_to_stream(out, s, i + 1, whatrequires_table_results);
}
whatrequires_table_results.print();
selection_solvables(m_pool.get(), &job, &solvables);
if (solvables.count)
{
Solvable* latest = pool_id2solvable(m_pool.get(), solvables.elements[0]);
printers::Node<std::string> root(concat(pool_id2str(m_pool.get(), latest->name), " == ", pool_id2str(m_pool.get(), latest->evr)));
root.set_root(true);
std::set<Solvable*> visited { latest };
reverse_walk_graph(root, latest, visited);
root.print("", false);
}
else
{
std::cout << "No matching package found" << std::endl;
}
}
else
{
pool_whatmatchesdep(m_pool.get(), SOLVABLE_REQUIRES, id, &solvables, -1);
std::stringstream out;
if (solvables.count == 0)
{
out << "No entries matching \"" << query << "\" found";
}
printers::Table whatrequires_table_results({"Name", "Version", "Build", "Channel"});
for (int i = 0; i < solvables.count; i++)
{
Solvable* s = pool_id2solvable(m_pool.get(), solvables.elements[i]);
solvable_to_stream(out, s, i + 1, whatrequires_table_results);
}
whatrequires_table_results.print();
}
queue_free(&job);
queue_free(&solvables);
return out.str();
return "";
// return out.str();
}
std::string Query::dependencytree(const std::string& query)
@ -265,8 +325,6 @@ namespace mamba
}
printers::Node<std::string> root(concat(pool_id2str(m_pool.get(), latest->name), " == ", pool_id2str(m_pool.get(), latest->evr)));
root.set_root(true);
// std::stringstream solv_str;
// solv_str << pool_id2str(m_pool.get(), latest->name) << " == " << ;
std::set<Solvable*> visited { latest };
walk_graph(root, latest, visited);
root.print("", false);