summaryrefslogtreecommitdiff
path: root/Tools/find-includes-cycles.py
blob: c4139cd7a0dbda15685c6eb4061fded6272635c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#! /usr/bin/env python

'''
Run this script from Source/Core/ to find all the #include cycles.
'''

import subprocess

def get_local_includes_for(path):
    lines = open(path).read().split('\n')
    includes = [l.strip() for l in lines if l.strip().startswith('#include')]
    return [i.split()[1][1:-1] for i in includes if '"' in i.split()[1]]

def find_all_files():
    '''Could probably use os.walk, but meh.'''
    f = subprocess.check_output(['find', '.', '-name', '*.h'],
                                universal_newlines=True).strip().split('\n')
    return [p[2:] for p in f]

def make_include_graph():
    return { f: get_local_includes_for(f) for f in find_all_files() }

def strongly_connected_components(graph):
    """
    Tarjan's Algorithm (named for its discoverer, Robert Tarjan) is a graph theory algorithm
    for finding the strongly connected components of a graph.

    Based on: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
    """

    index_counter = [0]
    stack = []
    lowlinks = {}
    index = {}
    result = []

    def strongconnect(node):
        # set the depth index for this node to the smallest unused index
        index[node] = index_counter[0]
        lowlinks[node] = index_counter[0]
        index_counter[0] += 1
        stack.append(node)

        # Consider successors of `node`
        try:
            successors = graph[node]
        except:
            successors = []
        for successor in successors:
            if successor not in lowlinks:
                # Successor has not yet been visited; recurse on it
                strongconnect(successor)
                lowlinks[node] = min(lowlinks[node],lowlinks[successor])
            elif successor in stack:
                # the successor is in the stack and hence in the current strongly connected component (SCC)
                lowlinks[node] = min(lowlinks[node],index[successor])

        # If `node` is a root node, pop the stack and generate an SCC
        if lowlinks[node] == index[node]:
            connected_component = []

            while True:
                successor = stack.pop()
                connected_component.append(successor)
                if successor == node: break
            component = tuple(connected_component)
            # storing the result
            result.append(component)

    for node in graph:
        if node not in lowlinks:
            strongconnect(node)

    return result

if __name__ == '__main__':
    comp = strongly_connected_components(make_include_graph())
    for c in comp:
        if len(c) != 1:
            print(c)