mirror of
https://abf.rosa.ru/djam/repo-analyzer.git
synced 2025-02-23 10:02:54 +00:00
Add cycles detection
This commit is contained in:
parent
6bdb49da9d
commit
183ad8fef6
1 changed files with 111 additions and 1 deletions
|
@ -6,7 +6,6 @@ import sys
|
|||
import gettext
|
||||
import argparse
|
||||
import sqlite3
|
||||
import string
|
||||
import rpm
|
||||
import re
|
||||
|
||||
|
@ -591,6 +590,116 @@ SELECT srp.name, sp.nvra, tp.nvra, tp.repodir_id FROM packages sp, rpm_provides
|
|||
return (result, len(result))
|
||||
|
||||
|
||||
class cycles(query_output):
|
||||
title = 'Dependency cycles'
|
||||
|
||||
|
||||
def find_all_cycles(self, graph):
|
||||
"""
|
||||
Find all cycles in the given graph.
|
||||
|
||||
This function will return a list of lists of nodes, which form cycles
|
||||
in the graph or an empty list if no cycle exists.
|
||||
"""
|
||||
|
||||
def dfs(node):
|
||||
"""
|
||||
Depth-first search subfunction.
|
||||
"""
|
||||
|
||||
def find_cycle_to_ancestor(spanning_tree, node, ancestor):
|
||||
"""
|
||||
Find a cycle containing both node and ancestor.
|
||||
"""
|
||||
path = []
|
||||
while (node != ancestor):
|
||||
if node is None:
|
||||
return []
|
||||
path.append(node)
|
||||
node = spanning_tree[node]
|
||||
path.append(node)
|
||||
path.reverse()
|
||||
return path
|
||||
|
||||
visited.add(node)
|
||||
# Explore recursively the connected component
|
||||
if node not in graph:
|
||||
return
|
||||
for each in graph[node]:
|
||||
if each not in visited:
|
||||
spanning_tree[each] = node
|
||||
dfs(each)
|
||||
else:
|
||||
if (spanning_tree[node] != each):
|
||||
cycle = find_cycle_to_ancestor(spanning_tree, node,
|
||||
each)
|
||||
if cycle:
|
||||
cycles.append(cycle)
|
||||
|
||||
visited = set() # List for marking visited and non-visited nodes
|
||||
spanning_tree = {} # Spanning tree
|
||||
cycles = []
|
||||
|
||||
# Algorithm outer-loop
|
||||
for each in graph:
|
||||
# Select a non-visited node
|
||||
if each not in visited:
|
||||
spanning_tree[each] = None
|
||||
# Explore node's connected component
|
||||
dfs(each)
|
||||
return cycles
|
||||
|
||||
def get_data(self, repodir_id):
|
||||
rows = self.dbc.execute("""
|
||||
SELECT sp.id, tp.id, sp.nvra, tp.nvra, req.name
|
||||
FROM packages sp
|
||||
CROSS JOIN package_requires_res pqr ON sp.id = pqr.package_id
|
||||
CROSS JOIN rpm_requires req ON pqr.requires_id = req.id
|
||||
CROSS JOIN packages tp ON pqr.dep_package_id = tp.id
|
||||
WHERE sp.repodir_id = ? AND tp.repodir_id = sp.repodir_id AND sp.id <> tp.id
|
||||
ORDER BY sp.id, tp.id
|
||||
""", [repodir_id]).fetchall()
|
||||
pre_pkg_id = None
|
||||
pre_dep_pkg_id = None
|
||||
deps = []
|
||||
graph = {}
|
||||
pkg_names = {}
|
||||
req_links = {}
|
||||
for row in rows:
|
||||
(pkg_id, dep_pkg_id) = (row[0], row[1])
|
||||
pkg_names[row[0]] = row[2]
|
||||
pkg_names[row[1]] = row[3]
|
||||
if pre_pkg_id is not None and pre_pkg_id != pkg_id:
|
||||
graph[pre_pkg_id] = deps
|
||||
deps = []
|
||||
if (pkg_id, dep_pkg_id) not in req_links:
|
||||
req_links[(pkg_id, dep_pkg_id)] = []
|
||||
req_links[(pkg_id, dep_pkg_id)].append(row[4])
|
||||
if pkg_id == pre_pkg_id and dep_pkg_id == pre_dep_pkg_id:
|
||||
continue
|
||||
deps.append(dep_pkg_id)
|
||||
pre_pkg_id = pkg_id
|
||||
pre_dep_pkg_id = dep_pkg_id
|
||||
if pre_pkg_id is not None:
|
||||
graph[pre_pkg_id] = deps
|
||||
|
||||
scc = self.find_all_cycles(graph)
|
||||
result = []
|
||||
if scc is not None:
|
||||
for comp in scc:
|
||||
if len(comp) < 2:
|
||||
continue
|
||||
cycle_str = pkg_names[comp[0]]
|
||||
for n0 in xrange(0, len(comp)):
|
||||
n1 = n0 + 1 if n0 + 1 < len(comp) else 0
|
||||
if (comp[n0], comp[n1]) in req_links:
|
||||
pass
|
||||
cycle_str += ' =(%s)=> %s' % (
|
||||
';'.join(req_links[(comp[n0], comp[n1])]),
|
||||
pkg_names[comp[n1]])
|
||||
result.append(cycle_str)
|
||||
return (result, len(result))
|
||||
|
||||
def main(args):
|
||||
options = parseargs()
|
||||
|
||||
|
@ -610,6 +719,7 @@ def main(args):
|
|||
symbols_not_resolved(dbc).print_text()
|
||||
file_conflicts(dbc).print_text()
|
||||
provides_conflicts(dbc).print_text()
|
||||
cycles(dbc).print_text()
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Add table
Reference in a new issue