85ca2a88d88634cb2e14d6adacfb96785165102b
[matrix.git] / matrix / graph.py
1 # Copyright (C) 2006-2008 Movial Oy
2 # Authors: Kalle Vahlman <kalle.vahlman@movial.fi>
3
4 import os
5 import sys
6
7 import config
8 import matrix
9
10
11 class Option:
12         short_opt = None
13         long_opt = None
14         takes_arg = False
15         help = None
16         argument = None
17
18         def __init__(self, short_opt, long_opt, takes_arg, help):
19                 self.short_opt = short_opt
20                 self.long_opt = long_opt
21                 self.takes_arg = takes_arg
22                 self.help = help
23
24
25 class Graph:
26         all_targets = []
27         all_deps = []
28         maxdepth = -1
29
30         options = {}
31         args = []
32         roots = None
33
34         def __init__(self, arguments):
35                 self.args.append(Option("h", "help", False,
36                         "Show this help"))
37                 self.args.append(Option("c", "components", True,
38                         "Read components from specified file (default: 'components.all')"))
39                 self.args.append(Option("i", "input", True,
40                         "Read active components from specified file"))
41                 self.args.append(Option("o", "output", True,
42                         "Write graph to specified file (type autodetected)"))
43                 self.args.append(Option("r", "root", True,
44                         "Start graphing from specified component"))
45                 self.args.append(Option("d", "depth", True,
46                         "Limit graph to specified depth"))
47
48                 self.parse_args(arguments)
49
50                 try:
51                         if self.options["help"]:
52                                 print "\nCopyright (C) 2007 Movial Oy\n"
53                                 print "Usage: %s [options]\n" % sys.argv[0]
54
55                                 for opt in self.args:
56                                         argstr = ""
57                                         if opt.takes_arg:
58                                                 argstr = "[arg]"
59
60                                         print " %-25s %s" % ("-" + opt.short_opt + ", --" + opt.long_opt + " " + argstr, opt.help)
61                         print
62                         sys.exit()
63                 except KeyError:
64                         pass
65
66                 try:
67                         matrix.parse_config(self.options["components"].argument)
68                 except KeyError:
69                         # Default to components.all
70                         matrix.parse_config('components/components.all')
71                         print "huuhaaaaaa"
72
73                 matrix.update_components()
74
75                 try:
76                         self.maxdepth = int(self.options["depth"].argument)
77                 except KeyError:
78                         self.maxdepth = -1
79
80                 roots = self.get_roots()
81
82                 depth = 1
83                 for component in config.components.values():
84                         if roots is None or component.name in roots:
85
86                                 self.add_component(component, self.maxdepth, depth)
87
88         def add_component(self, component, maxdepth, depth):
89                 for dep in component.active_depends:
90                         dependancy = self.depends(component, dep)
91                         if dependancy not in self.all_deps:
92                                 self.all_deps.append(dependancy)
93                         if maxdepth < 0 or depth < maxdepth:
94                                 self.add_component(dep, maxdepth, depth + 1)
95
96         def parse_args(self, arguments):
97                 arg = None
98                 argarg = None
99                 for argument in arguments:
100                         if arg is not None and arg.takes_arg:
101                                 if not argument.startswith("-"):
102                                         arg.argument = argument
103                                         arg = None
104                                         continue
105                                 else:
106                                         arg = None
107
108                         for opt in self.args:
109                                 if argument.startswith("-") or argument.startswith("--"):
110                                         if argument[1:] == opt.short_opt or argument[2:] == opt.long_opt:
111                                                 self.options[opt.long_opt] = opt
112                                                 arg = opt
113                                                 break
114
115                         if arg is None:
116                                 raise RuntimeError, "Unkown argument '" + argument + "'"
117
118         def read_roots(self, active_components_file="components.active"):
119                 f = open(active_components_file, "r")
120                 self.roots = []
121                 for root in f.readlines():
122                         if len(root.strip()) > 0:
123                                 self.roots.append(root.strip())
124                 f.close()
125                 print "Active components read from", active_components_file
126
127         def get_roots(self):
128                 if self.roots is not None:
129                         return self.roots
130
131                 try:
132                         active_components_file = self.options["input"].argument
133                         self.read_roots(active_components_file)
134                 except KeyError:
135                         try:
136                                 self.read_roots()
137                         except IOError:
138                                 try:
139                                         self.roots = [self.options["root"].argument]
140                                         print "Active component: %s" % self.roots[0]
141                                 except KeyError:
142                                         print "Including all known components as active"
143
144                 return self.roots
145
146         def depends(self, component, dependancy):
147                 raise NotImplementedError, 'Method depends not implemented by derived class'
148
149         def output(self):
150                 raise NotImplementedError, 'Method output not implemented by derived class'
151
152
153 class DotGraph(Graph):
154         def __init__(self, arguments):
155                 self.args.append(Option("a", "aspect-ratio", True,
156                         "Dot: Aspect ratio for the graph"))
157                 self.args.append(Option("s", "size", True,
158                         "Dot: Size of the graph ('xx.x,xx.x', in inches)"))
159                 Graph.__init__(self, arguments)
160
161         def depends(self, component, dependancy):
162                 return "\t\"" + component.name + "\" -> \"" + dependancy.name + "\""
163
164         def output(self):
165                 try:
166                         fname = self.options["output"].argument
167                         otype = self.options["output"].argument[self.options["output"].argument.rindex(".")+1:]
168                 except KeyError:
169                         fname = "graph.svg"
170                         otype = "svg"
171
172                 try:
173                         ratio = self.options["aspect"].argument
174                 except KeyError:
175                         ratio = "0.7" # default to landscape A4
176
177                 try:
178                         size = "-Gsize=" + self.options["size"].argument
179                 except KeyError:
180                         size = '' # default to native size
181
182                 f = open("graph.dot", "w")
183                 f.write("digraph {\n")
184                 for depstr in self.all_deps:
185                         f.write(depstr + "\n")
186                 f.write("}\n")
187                 f.close()
188
189                 print "Writing", fname
190                 os.system("dot graph.dot -Grankdir=LR -Gsplines=true -Nrotate=90 -Gratio=" + ratio + " -T" + otype + " " + size + " -o " + fname)
191                 os.remove("graph.dot")
192
193 class ComponentGraph(Graph):
194         def __init__(self, arguments):
195                 Graph.__init__(self, arguments)
196
197                 if self.roots is not None:
198                         for component in config.components.values():
199                                 if component.name in self.roots:
200                                         self.all_deps.append(self.depends(None, component))
201                                         break
202
203         def depends(self, component, dependancy):
204                 tagstr = ""
205                 flagstr = ""
206                 if dependancy.tags[None] != "matrix":
207                         tagstr = ",\ttag='" + dependancy.tags[None] + "'"
208                 if len(dependancy.flags) > 0:
209                         flagstr = ",\tflags=["
210                         for flag in dependancy.flags:
211                                 flagstr += "'" + flag + "',"
212                         flagstr = flagstr[:-1] + "]"
213
214                 return "Component('" + dependancy.name + "'" + tagstr + flagstr + ")"
215
216         def output(self):
217                 try:
218                         fname = self.options["output"].argument
219                 except KeyError:
220                         fname = "components"
221
222                 print "Writing", fname
223                 f = open(fname, "w")
224                 for depstr in self.all_deps:
225                         f.write(depstr + "\n")
226                 f.close()