File: module-dependencies.py

package info (click to toggle)
gimp 3.0.4-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 210,076 kB
  • sloc: ansic: 842,287; lisp: 10,761; python: 10,318; cpp: 7,238; perl: 4,355; sh: 1,043; xml: 963; yacc: 609; lex: 348; javascript: 150; makefile: 43
file content (228 lines) | stat: -rwxr-xr-x 7,108 bytes parent folder | download
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env python2

"""
module-dependencies.py -- GIMP library and core module dependency constructor
Copyright (C) 2010  Martin Nordholts <martinn@src.gnome.org>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.



This program uses graphviz (you need PyGraphViz) to construct a graph
with dependencies between GIMP library and core modules. Run it from
the source root. Note that you'll either need the very latest
PyGraphViz binding or use this hack in agraph.py:

--- agraph.py.orig        2010-01-04 16:07:46.000000000 +0100
+++ agraph.py        2010-01-04 16:13:54.000000000 +0100
@@ -1154,7 +1154,8 @@ class AGraph(object):
             raise IOError("".join(errors))

         if len(errors)>0:
-            raise IOError("".join(errors))
+            # Workaround exception throwing due to warning about cycles
+            pass
         return "".join(data)


"""


from __future__ import with_statement
from sets import Set
import os, re, pygraphviz



# First make a sanity check
if not os.path.exists("app") or not os.path.exists("libgimp"):
    print("Must be run in source root!")
    exit(-1);


# This file lives in libgimp and is used by many low-level
# libs. Exclude it in the calculations so the graph become nicer
ignored_interface_files = [
    "libgimp/libgimp-intl.h",
    ]

# List of library modules
libmodules = [
    "libgimp",
    "libgimpbase",
    "libgimpcolor",
    "libgimpconfig",
    "libgimpmath",
    "libgimpmodule",
    "libgimpthumb",
    "libgimpwidgets",
    ]

# List of app modules
# XXX: Maybe group some of these together to simplify graph?
appmodules = [
    "actions",
    "base",
    "composite",
    "config",
    "core",
    "dialogs",
    "display",
    "file",
    "gegl",
    "gui",
    "menus",
    "paint",
    "paint-funcs",
    "pdb",
    "plug-in",
    "tests",
    "text",
    "tools",
    "vectors",
    "widgets",
    "xcf",
    ]

# Bootstrap modules, i.e. modules we assume exist even though we don't
# have the code for them
boostrap_modules = [
    [ "GLib", ["glib.h"] ],
    [ "GTK+", ["gtk/gtk.h"] ],
    [ "GEGL", ["gegl.h"] ],
    [ "Pango", ["pango/pango.h"] ],
    [ "Cairo", ["cairo.h"] ],
    ]

##
# Function to determine if a filename is for an interface file
def is_interface_file(filename):
    return re.search("\.h$", filename)

##
# Function to determine if a filename is for an implementation file,
# i.e. a file that contains references to interface files
def is_implementation_file(filename):
    return re.search("\.c$", filename)

##
# Represents a software module. Think of it as a node in the
# dependency graph
class Module:
    def __init__(self, name, color, interface_files=[]):
        self.name = name
        self.color = color
        self.interface_files = Set(interface_files.__iter__())
        self.interface_file_dependencies = Set()
        self.dependencies = Set()

    def __repr__(self):
        return self.name

    def get_color(self):
        return self.color

    def get_interface_files(self):
        return self.interface_files

    def get_interface_file_dependencies(self):
        return self.interface_file_dependencies

    def get_dependencies(self):
        return self.dependencies

    def add_module_dependency(self, module):
        if self != module:
            self.dependencies.add(module)


# Represents a software module constructed from actual source code
class CodeModule(Module):
    def __init__(self, path, color):
        Module.__init__(self, path, color)

        all_files = os.listdir(path)

        # Collect interfaces this module provides
        for interface_file in filter(is_interface_file, all_files):
            self.interface_files.add(os.path.join(path, interface_file))

        # Collect dependencies to interfaces
        for filename in filter(is_implementation_file, all_files):
            with open(os.path.join(path, filename), 'r') as f:
                for line in f:
                    m = re.search("#include +[\"<](.*)[\">]", line)
                    if m:
                        interface_file = m.group(1)
                        # If the interface file appears to come from a core
                        # module, prepend with "app/"
                        m = re.search ("(.*)/.*", interface_file)
                        if m:
                            dirname = m.group(1)
                            if appmodules.__contains__(dirname):
                                interface_file = "app/" + interface_file

                        self.interface_file_dependencies.add(interface_file)

        for ignored_interface_file in ignored_interface_files:
            self.interface_file_dependencies.discard(ignored_interface_file)



# Initialize the modules to use for the dependency analysis
modules = Set()
for bootstrap_module in boostrap_modules:
    modules.add(Module(bootstrap_module[0], 'lightblue', bootstrap_module[1]))
for module_path in libmodules:
    modules.add(CodeModule(module_path, 'coral1'))
for module_path in appmodules:
    modules.add(CodeModule("app/" + module_path, 'lawngreen'))


# Map the interface files in the modules to the module that hosts them
interface_file_to_module = {}
for module in modules:
    for interface_file in module.get_interface_files():
        interface_file_to_module[interface_file] = module

# Figure out dependencies between modules
unknown_interface_files = Set()
for module in modules:
    interface_files = filter (is_interface_file, module.get_interface_file_dependencies())
    for interface_file in interface_files:
        if interface_file_to_module.has_key(interface_file):
            module.add_module_dependency(interface_file_to_module[interface_file])
        else:
            unknown_interface_files.add(interface_file)
if False:
    print "Unknown interface files:", unknown_interface_files

# Construct a GraphViz graph from the modules
dependency_graph = pygraphviz.AGraph(directed=True)
for module in modules:
    dependency_graph.add_node(module, fillcolor=module.get_color(), style='filled')
for module in modules:
    for depends_on in module.get_dependencies():
        dependency_graph.add_edge(module, depends_on)

# If module A depends on module B, and module B depends on module C, A
# gets C implicitly. Perform a transitive reduction on the graph to
# reflect this
dependency_graph.tred()

# Write result
if True:
    dependency_graph.draw("devel-docs/gimp-module-dependencies.svg", prog="dot")
else:
    dependency_graph.write("devel-docs/gimp-module-dependencies.dot")