From 4c07199d8201fcf267e90be0d24b76799d03cea6 Mon Sep 17 00:00:00 2001 From: PulkoMandy Date: Thu, 28 Dec 2023 14:09:14 +0100 Subject: [PATCH] Add a script to draw a dependency graph from a set of hpkg files Useful for understanding dependency chains. Change-Id: I64113ff87fe3cef2f45796b064749e0c6a5bf880 --- 3rdparty/pulkomandy/pkggraph.py | 82 +++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 3rdparty/pulkomandy/pkggraph.py diff --git a/3rdparty/pulkomandy/pkggraph.py b/3rdparty/pulkomandy/pkggraph.py new file mode 100755 index 0000000000..4ed1769ced --- /dev/null +++ b/3rdparty/pulkomandy/pkggraph.py @@ -0,0 +1,82 @@ +#!python3 +# +# Copyright 2019-2023 Adrien Destugues +# +# Distributed under terms of the MIT license. + +from os import listdir +from os.path import isfile, join +import subprocess +import re +import sys + +""" +Generate a graph of dependencies for a set of packages packages in an Haiku system. + +Usage: +- Without arguments: generate a graph of all packages in /system/packages. This can be quite busy + and hard to understand. It will also take a while to generate the graph, as dot tries to route + thousands of edges. +- With arguments: the arguments are a list of packages to analyze. This allows to print a subset + of the packages for a better view. + +Dependencies are resolved: if a package has a specific string in its REQUIRES and another has the +same string in its PROVIDES, a BLUE edge is drawn between the two package. +If a package has a REQUIRES that is not matched by any other package in the set, this REQUIRE entry +is drawn as a node, and the edge pointing to it is RED (so you can easily see missing dependencies +in a package subset). If you use the complete /system/packages hierarchy, there should be no red +edges, all dependencies are satisfied. + +The output of the script can be saved to a file for manual analysis (for example, you can search +packages that nothing points to, and see if you want to uninstall them), or piped into dot for +rendering as a PNG, for example: + + cd /system/packages + pkggraph.py qt* gst_plugins_ba* | dot -Tpng -o /tmp/packages.png + ShowImage /tmp/packages.png +""" + +path = "/system/packages" +if len(sys.argv) > 1: + packages = sys.argv[1:] +else: + packages = [join(path, f) for f in listdir(path) if(isfile(join(path, f)))] + +print('strict digraph {\nrankdir="LR"\nsplines=ortho\nnode [ fontname="Noto", fontsize=10];') + +pmap = {} +rmap = {} + +for p in packages: + pkgtool = subprocess.Popen(['package', 'list', '-i', p], stdout = subprocess.PIPE) + infos, stderr = pkgtool.communicate() + + provides = [] + requires = [] + + for line in infos.split(b'\n'): + if line.startswith(b"\tprovides:"): + provides.append(line.split(b' ')[1]) + if line.startswith(b"\trequires:"): + line = line.split(b' ')[1] + if b'>' in line: + line = line.split(b'>')[0] + if b'=' in line: + line = line.split(b'=')[0] + if line != b'haiku' and line != b'haiku_x86': + requires.append(line) + + for pro in provides: + pmap[pro] = provides[0] + if len(requires) > 0: + rmap[provides[0]] = requires + +for k,v in rmap.items(): + for dep in v: + color = "red" + if dep in pmap: + dep = pmap[dep] + color = "blue" + print('"%s" -> "%s" [color=%s]' % (k.decode('utf-8'), dep.decode('utf-8'), color)) + +print("}")