2020-08-07 07:35:50 +00:00
|
|
|
from operator import itemgetter
|
2020-08-11 11:40:10 +00:00
|
|
|
from typing import Iterable, List
|
|
|
|
|
2020-08-07 07:35:50 +00:00
|
|
|
from tree_format import format_tree
|
|
|
|
|
2020-08-08 09:59:55 +00:00
|
|
|
from mach_nix.data.nixpkgs import NixpkgsIndex
|
2020-08-07 07:35:50 +00:00
|
|
|
from mach_nix.resolver import ResolvedPkg
|
|
|
|
|
|
|
|
|
|
|
|
class Node:
|
2020-08-08 09:59:55 +00:00
|
|
|
def __init__(self, pkg: ResolvedPkg, name, parent: 'Node' = None):
|
2020-08-07 07:35:50 +00:00
|
|
|
self.pkg = pkg
|
2020-08-08 09:59:55 +00:00
|
|
|
self.name = name
|
2020-08-07 07:35:50 +00:00
|
|
|
self.children = []
|
|
|
|
self.parent = parent
|
|
|
|
if parent:
|
|
|
|
self.parent.children.append(self)
|
|
|
|
|
2020-08-11 11:40:10 +00:00
|
|
|
def all_parents(self) -> List['Node']:
|
|
|
|
if self.parent is None:
|
|
|
|
return []
|
|
|
|
return [self.parent] + self.parent.all_parents()
|
|
|
|
|
2020-08-07 07:35:50 +00:00
|
|
|
|
2020-08-08 09:59:55 +00:00
|
|
|
def make_name(pkg: ResolvedPkg, nixpkgs: NixpkgsIndex):
|
|
|
|
pi = pkg.provider_info
|
2020-08-11 11:40:10 +00:00
|
|
|
extras = f"[{' '.join(pkg.extras_selected)}]" if pkg.extras_selected else ''
|
2020-08-24 07:33:27 +00:00
|
|
|
name = f"{pkg.name}{extras} - {pkg.ver} - {pi.provider.name}"
|
2020-08-08 09:59:55 +00:00
|
|
|
if pi.provider == 'wheel':
|
|
|
|
name += f" - {'-'.join(pi.wheel_fname.split('-')[-3:])[:-4]}"
|
|
|
|
if pi.provider == 'nixpkgs':
|
|
|
|
name += f" (attrs: {' '.join(c.nix_key for c in nixpkgs.get_all_candidates(pkg.name))})"
|
|
|
|
return name
|
|
|
|
|
|
|
|
|
2020-08-11 11:40:10 +00:00
|
|
|
def build_tree(pkgs: dict, root: Node, nixpkgs: NixpkgsIndex) -> List[str]:
|
|
|
|
"""
|
|
|
|
Recursively adds children to given root node.
|
|
|
|
Removes cycles from original graph while processing.
|
|
|
|
Returns list of warnings.
|
|
|
|
"""
|
2020-08-07 07:35:50 +00:00
|
|
|
root_pkg = pkgs[root.pkg.name]
|
2020-08-11 11:40:10 +00:00
|
|
|
warnings = []
|
2020-08-07 07:35:50 +00:00
|
|
|
for name in sorted(root_pkg.build_inputs + root_pkg.prop_build_inputs):
|
|
|
|
child_pkg: ResolvedPkg = pkgs[name]
|
2020-08-11 11:40:10 +00:00
|
|
|
# detect circles
|
|
|
|
if child_pkg in [node.pkg for node in root.all_parents()]:
|
|
|
|
warnings.append(
|
|
|
|
f"WARNING: Circular dependency detected and removed:"
|
|
|
|
f" {root.pkg.name}:{root.pkg.ver} -> {child_pkg.name}:{child_pkg.ver}")
|
|
|
|
root.pkg.build_inputs = [bi for bi in root.pkg.build_inputs if bi != child_pkg.name]
|
|
|
|
root.pkg.prop_build_inputs = [bi for bi in root.pkg.prop_build_inputs if bi != child_pkg.name]
|
|
|
|
root.pkg.removed_circular_deps.append(child_pkg.name)
|
|
|
|
continue
|
2020-08-08 09:59:55 +00:00
|
|
|
child_node = Node(child_pkg, make_name(child_pkg, nixpkgs), root)
|
2020-08-11 11:40:10 +00:00
|
|
|
warnings += build_tree(pkgs, child_node, nixpkgs)
|
|
|
|
return warnings
|
2020-08-07 07:35:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def tree_to_dict(root_node: Node):
|
|
|
|
return root_node.name, [tree_to_dict(child) for child in root_node.children]
|
|
|
|
|
|
|
|
|
|
|
|
def print_tree(root_node):
|
|
|
|
d = tree_to_dict(root_node)
|
|
|
|
print(format_tree(
|
|
|
|
d, format_node=itemgetter(0), get_children=itemgetter(1)))
|
|
|
|
|
|
|
|
|
2020-08-11 11:40:10 +00:00
|
|
|
def remove_circles_and_print(pkgs: Iterable[ResolvedPkg], nixpkgs: NixpkgsIndex):
|
2020-08-07 07:35:50 +00:00
|
|
|
print("\n### Resolved Dependencies ###\n")
|
|
|
|
indexed_pkgs = {p.name: p for p in sorted(pkgs, key=lambda p: p.name)}
|
|
|
|
roots: Iterable[ResolvedPkg] = (p for p in pkgs if p.is_root)
|
|
|
|
for root in roots:
|
2020-08-08 09:59:55 +00:00
|
|
|
root_node = Node(root, make_name(root, nixpkgs))
|
2020-08-11 11:40:10 +00:00
|
|
|
warnings = build_tree(indexed_pkgs, root_node, nixpkgs)
|
2020-08-07 07:35:50 +00:00
|
|
|
print_tree(root_node)
|
2020-08-11 11:40:10 +00:00
|
|
|
if warnings:
|
|
|
|
print(''.join(warnings) + '\n')
|