[gimp] tools: Add tool to construct a dependency graph between modules
- From: Martin Nordholts <martinn src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [gimp] tools: Add tool to construct a dependency graph between modules
- Date: Mon, 4 Jan 2010 16:03:13 +0000 (UTC)
commit c116363731e6b9eb1ba42adec6c4fe4e4b4d608f
Author: Martin Nordholts <martinn src gnome org>
Date: Mon Jan 4 16:58:52 2010 +0100
tools: Add tool to construct a dependency graph between modules
Add Python tool to construct a dependency graph between GIMP library
and core modules using graphviz and PyGraphViz.
tools/module-dependencies.py | 228 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 228 insertions(+), 0 deletions(-)
---
diff --git a/tools/module-dependencies.py b/tools/module-dependencies.py
new file mode 100755
index 0000000..54f5975
--- /dev/null
+++ b/tools/module-dependencies.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+
+"""
+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 <http://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")
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]