support/scripts/graph-depends allow for forward and reverse depends on same graph

The current implementation of buildroot depe dependency graphing
either does forward- or reverse-dependency traversal.

This patch enables buildroot to graph forward and reverse dependencies
on the graph for the same package: (Diagram Credit: Yann E. MORIN)

    $ make pkg-d-graph-both-depends

    pkg A -.            .-> pkg E
            \          /
    pkg B ----> pkg D ----> pkg F
            /          \
    pkg C -'            '-> pkg G

In the above example a single graph shows pkg {A,B,C} are needed
by pkg D, and pkg D is a dependency of pkg {E,F,G}.

Makefile help and manual are also updated.

Signed-off-by: Steve Hay <me@stevenhay.com>
[Arnout:
 - remove DEPTH and RDEPTH, their functionality is already covered by
   BR2_GRAPH_DEPS_OPTS;
 - remove --rdepth, it was felt to not add sufficient added value;
 - add the new target to the manual;
 - fix flake8 errors.
]
Signed-off-by: Arnout Vandecappelle <arnout@mind.be>
This commit is contained in:
ʎɐH ǝʌǝʇS
2023-01-24 00:12:23 +01:00
committed by Arnout Vandecappelle
parent 0c2e6050cc
commit ca6c896bdd
4 changed files with 50 additions and 27 deletions

View File

@@ -1171,6 +1171,9 @@ help:
@echo ' - Recursively list packages which have <pkg> as a dependency'
@echo ' <pkg>-graph-depends - Generate a graph of <pkg>'\''s dependencies'
@echo ' <pkg>-graph-rdepends - Generate a graph of <pkg>'\''s reverse dependencies'
@echo ' <pkg>-graph-both-depends'
@echo ' - Generate a graph of both <pkg>'\''s forward and'
@echo ' reverse dependencies.
@echo ' <pkg>-dirclean - Remove <pkg> build directory'
@echo ' <pkg>-reconfigure - Restart the build from the configure step'
@echo ' <pkg>-rebuild - Restart the build from the build step'

View File

@@ -81,6 +81,10 @@ graphs.
dependencies (i.e the packages that depend on it, directly or
indirectly)
| +graph-both-depends+ | Generate a graph of this package in both
directions (i.e the packages that depend on it and on which it
depends, directly or indirectly)
| +dirclean+ | Remove the whole package build directory
| +reinstall+ | Re-run the install commands

View File

@@ -1042,6 +1042,9 @@ $(1)-graph-depends: graph-depends-requirements
$(1)-graph-rdepends: graph-depends-requirements
$(call pkg-graph-depends,$(1),--reverse)
$(1)-graph-both-depends: graph-depends-requirements
$(call pkg-graph-depends,$(1),--direct --reverse)
$(1)-all-source: $(1)-source
$(1)-all-source: $$(foreach p,$$($(2)_FINAL_ALL_DEPENDENCIES),$$(p)-all-source)

View File

@@ -159,11 +159,11 @@ def check_circular_deps(deps):
# This functions trims down the dependency list of all packages.
# It applies in sequence all the dependency-elimination methods.
def remove_extra_deps(deps, rootpkg, transitive, arrow_dir):
def remove_extra_deps(deps, rootpkg, transitive, direct):
# For the direct dependencies, find and eliminate mandatory
# deps, and add them to the root package. Don't do it for a
# reverse graph, because mandatory deps are only direct deps.
if arrow_dir == "forward":
if direct:
for pkg in list(deps.keys()):
if not pkg == rootpkg:
for d in get_mandatory_deps(pkg, deps):
@@ -199,12 +199,13 @@ def print_attrs(outfile, pkg, pkg_type, pkg_version, depth, colors):
# Print the dependency graph of a package
def print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exclude_list,
arrow_dir, draw_graph, depth, max_depth, pkg, colors, done_deps=None):
direct, draw_graph, depth, max_depth, pkg, colors, done_deps=None):
if done_deps is None:
done_deps = []
if pkg in done_deps:
return
done_deps.append(pkg)
if draw_graph:
print_attrs(outfile, pkg, dict_types[pkg], dict_versions[pkg], depth, colors)
elif depth != 0:
@@ -231,9 +232,12 @@ def print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exc
break
if add:
if draw_graph:
outfile.write("%s -> %s [dir=%s]\n" % (pkg_node_name(pkg), pkg_node_name(d), arrow_dir))
if direct:
outfile.write("%s -> %s [dir=%s]\n" % (pkg_node_name(pkg), pkg_node_name(d), "forward"))
else:
outfile.write("%s -> %s [dir=%s]\n" % (pkg_node_name(d), pkg_node_name(pkg), "forward"))
print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exclude_list,
arrow_dir, draw_graph, depth + 1, max_depth, d, colors, done_deps)
direct, draw_graph, depth + 1, max_depth, d, colors, done_deps)
def parse_args():
@@ -265,9 +269,9 @@ def parse_args():
default=False)
parser.add_argument("--no-transitive", dest="transitive", action='store_false',
help="Draw (do not draw) transitive dependencies")
parser.add_argument("--direct", dest="direct", action='store_true', default=True,
parser.add_argument("--direct", dest="direct", action='store_true', default=False,
help="Draw direct dependencies (the default)")
parser.add_argument("--reverse", dest="direct", action='store_false',
parser.add_argument("--reverse", dest="reverse", action='store_true', default=False,
help="Draw reverse dependencies")
parser.add_argument("--quiet", '-q', dest="quiet", action='store_true',
help="Quiet")
@@ -292,6 +296,9 @@ def main():
sys.exit(1)
outfile = open(args.outfile, "w")
if not args.direct and not args.reverse:
args.direct = True
if args.package is None:
mode = MODE_FULL
rootpkg = 'all'
@@ -312,13 +319,9 @@ def main():
if args.exclude_mandatory:
exclude_list += MANDATORY_DEPS
if args.direct:
arrow_dir = "forward"
else:
if mode == MODE_FULL:
logging.error("--reverse needs a package")
sys.exit(1)
arrow_dir = "back"
if args.reverse and mode == MODE_FULL:
logging.error("--reverse needs a package")
sys.exit(1)
draw_graph = not args.flat_list
@@ -330,23 +333,33 @@ def main():
logging.error("Error: incorrect color list '%s'" % args.colors)
sys.exit(1)
deps, rdeps, dict_types, dict_versions = brpkgutil.get_dependency_tree()
dict_deps = deps if args.direct else rdeps
check_circular_deps(dict_deps)
if check_only:
sys.exit(0)
dict_deps = remove_extra_deps(dict_deps, rootpkg, args.transitive, arrow_dir)
# Start printing the graph data
if draw_graph:
if not check_only and draw_graph:
outfile.write("digraph G {\n")
print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exclude_list,
arrow_dir, draw_graph, 0, args.depth, rootpkg, colors)
deps, rdeps, dict_types, dict_versions = brpkgutil.get_dependency_tree()
if draw_graph:
# forward
if args.direct:
dict_deps = deps
direct = True
check_circular_deps(dict_deps)
if not check_only:
dict_deps = remove_extra_deps(dict_deps, rootpkg, args.transitive, direct)
print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exclude_list,
direct, draw_graph, 0, args.depth, rootpkg, colors)
# reverse
if args.reverse:
dict_deps = rdeps
direct = False
check_circular_deps(dict_deps)
if not check_only:
dict_deps = remove_extra_deps(dict_deps, rootpkg, args.transitive, direct)
print_pkg_deps(outfile, dict_deps, dict_types, dict_versions, stop_list, exclude_list,
direct, draw_graph, 0, args.depth, rootpkg, colors)
if not check_only and draw_graph:
outfile.write("}\n")
else:
outfile.write("\n")