Blame plugins/leaves.py

Packit Service 27f74b
# leaves.py
Packit Service 27f74b
# DNF plugin for listing installed packages not required by any other
Packit Service 27f74b
# installed package.
Packit Service 27f74b
#
Packit Service 27f74b
# Copyright (C) 2015 Emil Renner Berthing
Packit Service 27f74b
#
Packit Service 27f74b
# This copyrighted material is made available to anyone wishing to use,
Packit Service 27f74b
# modify, copy, or redistribute it subject to the terms and conditions of
Packit Service 27f74b
# the GNU General Public License v.2, or (at your option) any later version.
Packit Service 27f74b
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit Service 27f74b
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit Service 27f74b
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit Service 27f74b
# Public License for more details.
Packit Service 27f74b
#
Packit Service 27f74b
import dnf
Packit Service 27f74b
import dnf.sack
Packit Service 27f74b
import dnf.cli
Packit Service 27f74b
from dnfpluginscore import _
Packit Service 27f74b
Packit Service 27f74b
Packit Service 27f74b
class Leaves(dnf.Plugin):
Packit Service 27f74b
    name = 'leaves'
Packit Service 27f74b
Packit Service 27f74b
    def __init__(self, base, cli):
Packit Service 27f74b
        super(Leaves, self).__init__(base, cli)
Packit Service 27f74b
        if cli:
Packit Service 27f74b
            cli.register_command(LeavesCommand)
Packit Service 27f74b
Packit Service 27f74b
Packit Service 27f74b
class LeavesCommand(dnf.cli.Command):
Packit Service 27f74b
    aliases = ('leaves',)
Packit Service 27f74b
    summary = _('List installed packages not required by any other package')
Packit Service 27f74b
Packit Service 27f74b
    def buildgraph(self):
Packit Service 27f74b
        """
Packit Service 27f74b
        Load the list of installed packages and their dependencies using
Packit Service 27f74b
        hawkey, and build the dependency graph and the graph of reverse
Packit Service 27f74b
        dependencies.
Packit Service 27f74b
        """
Packit Service 27f74b
        query = dnf.sack._rpmdb_sack(self.base).query().apply()
Packit Service 27f74b
        pkgmap = dict()
Packit Service 27f74b
        packages = []
Packit Service 27f74b
        depends = []
Packit Service 27f74b
        rdepends = []
Packit Service 27f74b
        deps = set()
Packit Service 27f74b
        providers = set()
Packit Service 27f74b
Packit Service 27f74b
        for i, pkg in enumerate(query):
Packit Service 27f74b
            pkgmap[pkg] = i
Packit Service 27f74b
            packages.append(pkg)
Packit Service 27f74b
            rdepends.append([])
Packit Service 27f74b
Packit Service 27f74b
        for i, pkg in enumerate(packages):
Packit Service 27f74b
            for req in pkg.requires:
Packit Service 27f74b
                sreq = str(req)
Packit Service 27f74b
                if sreq.startswith('rpmlib(') or sreq == 'solvable:prereqmarker':
Packit Service 27f74b
                    continue
Packit Service 27f74b
                for dpkg in query.filter(provides=req):
Packit Service 27f74b
                    providers.add(pkgmap[dpkg])
Packit Service 27f74b
                if len(providers) == 1 and i not in providers:
Packit Service 27f74b
                    deps.update(providers)
Packit Service 27f74b
                providers.clear()
Packit Service 27f74b
Packit Service 27f74b
            deplist = list(deps)
Packit Service 27f74b
            deps.clear()
Packit Service 27f74b
            depends.append(deplist)
Packit Service 27f74b
            for j in deplist:
Packit Service 27f74b
                rdepends[j].append(i)
Packit Service 27f74b
Packit Service 27f74b
        return (packages, depends, rdepends)
Packit Service 27f74b
Packit Service 27f74b
    def kosaraju(self, graph, rgraph):
Packit Service 27f74b
        """
Packit Service 27f74b
        Run Kosaraju's algorithm to find strongly connected components
Packit Service 27f74b
        in the graph, and return the list of nodes in the components
Packit Service 27f74b
        without any incoming edges.
Packit Service 27f74b
        """
Packit Service 27f74b
        N = len(graph)
Packit Service 27f74b
        rstack = []
Packit Service 27f74b
        stack = []
Packit Service 27f74b
        idx = []
Packit Service 27f74b
        tag = [False] * N
Packit Service 27f74b
Packit Service 27f74b
        # do depth-first searches in the graph
Packit Service 27f74b
        # and push nodes to rstack "on the way up"
Packit Service 27f74b
        # until all nodes have been pushed.
Packit Service 27f74b
        # tag nodes so we don't visit them more than once
Packit Service 27f74b
        for u in range(N):
Packit Service 27f74b
            if tag[u]:
Packit Service 27f74b
                continue
Packit Service 27f74b
Packit Service 27f74b
            stack.append(u)
Packit Service 27f74b
            idx.append(len(graph[u]))
Packit Service 27f74b
            tag[u] = True
Packit Service 27f74b
            while stack:
Packit Service 27f74b
                u = stack[-1]
Packit Service 27f74b
                i = idx[-1]
Packit Service 27f74b
Packit Service 27f74b
                if i:
Packit Service 27f74b
                    i -= 1
Packit Service 27f74b
                    idx[-1] = i
Packit Service 27f74b
                    v = graph[u][i]
Packit Service 27f74b
                    if not tag[v]:
Packit Service 27f74b
                        stack.append(v)
Packit Service 27f74b
                        idx.append(len(graph[v]))
Packit Service 27f74b
                        tag[v] = True
Packit Service 27f74b
                else:
Packit Service 27f74b
                    stack.pop()
Packit Service 27f74b
                    idx.pop()
Packit Service 27f74b
                    rstack.append(u)
Packit Service 27f74b
Packit Service 27f74b
        # now searches beginning at nodes popped from
Packit Service 27f74b
        # rstack in the graph with all edges reversed
Packit Service 27f74b
        # will give us the strongly connected components.
Packit Service 27f74b
        # the incoming edges to each component is the
Packit Service 27f74b
        # union of incoming edges to each node in the
Packit Service 27f74b
        # component minus the incoming edges from
Packit Service 27f74b
        # component nodes themselves.
Packit Service 27f74b
        # now all nodes are tagged, so this time let's
Packit Service 27f74b
        # remove the tags as we visit each node.
Packit Service 27f74b
        leaves = []
Packit Service 27f74b
        scc = []
Packit Service 27f74b
        sccredges = set()
Packit Service 27f74b
        while rstack:
Packit Service 27f74b
            v = rstack.pop()
Packit Service 27f74b
            if not tag[v]:
Packit Service 27f74b
                continue
Packit Service 27f74b
Packit Service 27f74b
            stack.append(v)
Packit Service 27f74b
            tag[v] = False
Packit Service 27f74b
            while stack:
Packit Service 27f74b
                v = stack.pop()
Packit Service 27f74b
                redges = rgraph[v]
Packit Service 27f74b
                scc.append(v)
Packit Service 27f74b
                sccredges.update(redges)
Packit Service 27f74b
                for u in redges:
Packit Service 27f74b
                    if tag[u]:
Packit Service 27f74b
                        stack.append(u)
Packit Service 27f74b
                        tag[u] = False
Packit Service 27f74b
Packit Service 27f74b
            sccredges.difference_update(scc)
Packit Service 27f74b
            if not sccredges:
Packit Service 27f74b
                leaves.extend(scc)
Packit Service 27f74b
            del scc[:]
Packit Service 27f74b
            sccredges.clear()
Packit Service 27f74b
Packit Service 27f74b
        return leaves
Packit Service 27f74b
Packit Service 27f74b
    def findleaves(self):
Packit Service 27f74b
        (packages, depends, rdepends) = self.buildgraph()
Packit Service 27f74b
        return [packages[i] for i in self.kosaraju(depends, rdepends)]
Packit Service 27f74b
Packit Service 27f74b
    def run(self):
Packit Service 27f74b
        for pkg in sorted(map(str, self.findleaves())):
Packit Service 27f74b
            print(pkg)