View previous topic :: View next topic |
Author |
Message |
nick_0189 n00b

Joined: 28 Jul 2022 Posts: 15
|
Posted: Fri Apr 11, 2025 2:01 am Post subject: Package Reverse Dependency Chain Including USE Flags |
|
|
Hello all,
I would like to map out the reverse dependency chain which causes a given package to be installed on a system, including use flags that would trigger its install. Has this been done before?
I wrote a short python script that can map the reverse dependency chain for an input package, but I am looking at a big time sink for finding if there are any use flags that triggered the inclusion of the package.
Here is the script:
Code: |
#!/usr/bin/env python
class DependencyMap:
def __init__(self, filename):
file = open(filename)
text = file.read()
lines = text.split("\n")
self.lines = self.clean_lines(lines)
self.parents = self.find_parents()
def clean_lines(self, lines):
start_text = "pulled in by:"
end_text = ">>>"
start_flag = False
end_flag = False
for line in lines:
if not start_flag:
start_flag = start_text in line
if not start_flag:
continue
if start_flag:
end_flag = end_text in line
if end_flag:
break
yield(line)
def find_parents(self):
parents = {}
parent = (0, "",)
for line in self.lines:
if not line.strip():
continue
indent = len(line) - len(line.lstrip())
pkg = line.strip().split(" ")[0]
parent_indent, parent_pkg = parent
if not parent_indent or indent <= parent_indent:
parent = (indent, pkg,)
continue
parents.setdefault(parent_pkg, []).append(pkg)
return(parents)
def get_input_pkg(self, opts=[]):
for i,opt in enumerate(opts):
print(i,opt)
user_input = input(">>> ")
if opts:
return list(opts)[int(user_input)]
for pkg in self.parents:
if user_input in pkg:
opts.append(pkg)
opts = sorted(list(opts))
if len(opts) == 1:
return opts[0]
return self.get_input_pkg(opts)
def get_rdep_chain(self, pkg=None, _seen=[], _level=0):
if pkg is None:
pkg = self.get_input_pkg()
yield((_level,pkg,))
for pkg in self.parents.get(pkg,[]):
if pkg in _seen:
continue
if pkg != "@selected":
_seen.append(pkg)
yield from self.get_rdep_chain(pkg, _seen=_seen, _level=_level+1)
def main():
# emerge --pretend --verbose --emptytree --depclean > emerge_deps.txt
# portageq metadata / ebuild <PKG> DEPEND
depMap = DependencyMap("emerge_deps.txt")
pkg = depMap.get_input_pkg()
rdepChain = depMap.get_rdep_chain(pkg)
for level,pkg in rdepChain:
print(" "*level,pkg)
if __name__ == '__main__':
main()
|
It expects an "emerge_deps.txt" file, which can be generated as follows:
Code: |
emerge --pretend --verbose --emptytree --depclean > emerge_deps.txt
|
It can generate the following example output:
Code: |
./rdepends.py
>>> brotli
app-arch/brotli-1.1.0
app-text/dvisvgm-3.4
dev-texlive/texlive-binextra-2024_p72338-r1
app-text/texlive-2024
@selected
media-libs/libjxl-0.11.1
dev-libs/hyprgraphics-0.1.1
gui-wm/hyprland-0.46.2-r1
@selected
media-libs/woff2-1.0.2-r6
net-libs/libsoup-3.4.4
gnome-base/gvfs-1.52.3-r1
xfce-base/thunar-4.20.1
@selected
sys-apps/osinfo-db-tools-1.11.0
sys-apps/osinfo-db-20240701
sys-libs/libosinfo-1.11.0
app-emulation/virt-manager-5.0.0-r1
@selected
net-libs/nodejs-22.13.1
@selected
sys-apps/yarn-1.22.22
@selected
www-client/chromium-134.0.6998.117
@selected
www-client/firefox-128.8.0
@selected
|
This is nice, I can see why a package is required all the way up to the "@selected" packages in the world file, but I would additionally like to know if the package is pulled in by a USE flag or if it is a hard dependency for the package. The USE flag rules for a package can found as follows:
Code: |
portageq metadata / ebuild $(qlist -Iv <PKG>) DEPEND
|
Example output:
Code: |
portageq metadata / ebuild $(qlist -Iv grub) DEPEND
app-arch/xz-utils >=sys-libs/ncurses-5.2-r5:0= grub_platforms_emu? ( sdl? ( media-libs/libsdl2 ) ) device-mapper? ( >=sys-fs/lvm2-2.02.45 ) libzfs? ( sys-fs/zfs:= ) mount? ( sys-fs/fuse:3= ) truetype? ( media-libs/freetype:2= ) ppc? ( >=sys-apps/ibm-powerpc-utils-1.3.5 ) ppc64? ( >=sys-apps/ibm-powerpc-utils-1.3.5 )
|
Portage parses this somehow when determining package dependencies. Is this functionality somewhat independent from Portage so I can pass the active use flags for a given package to a parser to find the triggered dependencies?
For example, I would like to to know that the bluez package is only included under pipewire because the bluetooth USE flag was enabled for pipewire, which was enabled by the desktop profile.
Code: |
net-wireless/bluez-5.79
media-video/pipewire-1.2.7-r2
(bluetooth? ( dev-libs/glib media-libs/fdk-aac media-libs/libldac media-libs/libfreeaptx media-libs/opus media-libs/sbc >=net-wireless/bluez-4.101:= virtual/libusb:1 )
/var/db/repos/gentoo/profiles/targets/desktop/make.defaults
media-video/wireplumber-0.5.8-r2
sys-apps/xdg-desktop-portal-1.18.4-r1
x11-base/xwayland-24.1.6
gui-wm/hyprland-0.46.2-r1
@selected
|
Thank you for your help, please let me know if I can clarify anything. |
|
Back to top |
|
 |
Ralphred l33t

Joined: 31 Dec 2013 Posts: 768
|
Posted: Fri Apr 11, 2025 1:14 pm Post subject: Re: Package Reverse Dependency Chain Including USE Flags |
|
|
nick_0189 wrote: | Has this been done before? | Not that I'm aware of. There is the equery depgraph command but it's not so tailored as to be able to pass it different use flags and see how a flag switch would affect the whole dependency tree, it does show if something was triggered by a use flag (I think). IIRC it's written in perl(?). I still have a pyqt based frontend for eix* (and equery depgraph) laying around, but the depgraph function was so non-intuitive I shelved it for the "published" version; if you get this working I'd happily use it** as the core of a "show me the dependencies" GUI tab with check boxes for the optional use flags, or even a "compare the dependencies" for multiple packages selected from the search UI.
nick_0189 wrote: | Example output: Code: | portageq metadata / ebuild $(qlist -Iv grub) DEPEND
app-arch/xz-utils >=sys-libs/ncurses-5.2-r5:0= grub_platforms_emu? ( sdl? ( media-libs/libsdl2 ) ) device-mapper? ( >=sys-fs/lvm2-2.02.45 ) libzfs? ( sys-fs/zfs:= ) mount? ( sys-fs/fuse:3= ) truetype? ( media-libs/freetype:2= ) ppc? ( >=sys-apps/ibm-powerpc-utils-1.3.5 ) ppc64? ( >=sys-apps/ibm-powerpc-utils-1.3.5 ) |
| This doesn't look the easiest to parse, but it's certainly doable. This nesting looks particularly cumbersome to try and convey in an output... Code: | grub_platforms_emu? ( sdl? ( media-libs/libsdl2 ) ) |
*eix is extremely well written software, hats off to the dev(s) O7
**with your permission, OFC. |
|
Back to top |
|
 |
nick_0189 n00b

Joined: 28 Jul 2022 Posts: 15
|
Posted: Sun Apr 13, 2025 9:27 pm Post subject: |
|
|
Thanks for your response. I looked at equery and quse and came to the same conclusion that they could not be used for this. I'll try my hand at writing a parser for the ebuild dependencies and see how it goes, though I've never written a parser before. |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|