00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 from __future__ import with_statement
00031
00032 import os
00033 import re
00034 import distutils.version
00035 import sys, string
00036 import subprocess
00037 import getopt
00038 import roslib
00039 import roslib.rospack
00040 import roslib.rosenv
00041 import random
00042 import tempfile
00043 import shutil
00044
00045 from math import sqrt
00046 from optparse import OptionParser
00047
00048
00049 license_map = {
00050 "bsd": "green",
00051 "zlib": "green",
00052 "apache": "green",
00053 "free": "green",
00054 "bsl1.0": "green",
00055 "lgpl": "dodgerblue",
00056 "mit": "dodgerblue",
00057 "gpl": "orange",
00058 "research-only": "red",
00059 }
00060
00061 status_map = {
00062 "doc reviewed": "green",
00063 "api cleared": "dodgerblue",
00064 "api conditionally cleared": "orange",
00065 "proposal cleared": "pink",
00066 "unreviewed": "red",
00067 "experimental": "yellow",
00068 "3rdparty": "white",
00069 "3rdparty doc reviewed": "green",
00070 "na": "gray85",
00071 "test": "gray85",
00072 "deprecated": "magenta",
00073 "ROS_BUILD_BLACKLIST": "black"
00074 }
00075
00076
00077 class PackageCharacteristics:
00078 def __init__(self):
00079 self.color_by = None
00080
00081 self._file_colors = None
00082 self._status_colors = {}
00083 self._rosmake_result_colors = {}
00084 self._license_color = {}
00085 self._license = {}
00086 self._review_status = {}
00087 self._review_notes = {}
00088
00089
00090 def set_color_by(self, method, option=None):
00091 self.color_by = method
00092 if self.color_by == "file":
00093 self._color_filename = option
00094
00095 def load_color_from_file(self):
00096 if self._file_colors:
00097 return True
00098 with open(self._color_filename,'r') as color_file:
00099 self._file_colors = {}
00100 for line in color_file:
00101 try:
00102 k, v = line.split()
00103 self.file_colors[k] = v
00104 except:
00105 print "ERROR: badly formatted color line: %s"%line
00106
00107 def get_file_color(self, pkg):
00108 self.load_color_from_file()
00109 return self._file_colors.get(pkg, 'purple')
00110
00111 def get_license(self, pkg):
00112 if pkg in self._license:
00113 return self._license[pkg]
00114 self._license[pkg] = roslib.manifest.parse_file(roslib.manifest.manifest_file(pkg)).license.lower()
00115 return self._license[pkg]
00116
00117 def get_license_color(self, pkg):
00118 if pkg in self._license_color:
00119 return self._license_color[pkg]
00120
00121 license = self.get_license(pkg)
00122 self._license_color[pkg] = license_map.get(license, 'purple')
00123
00124 if not license in license_map:
00125 print "Unknown: ", pkg, license
00126
00127 return self._license_color[pkg]
00128
00129 def get_review_status(self, pkg):
00130 if pkg in self._review_status:
00131 return self._review_status[pkg]
00132 f = roslib.manifest.manifest_file(pkg)
00133 try:
00134 self._review_status[pkg] = roslib.manifest.parse_file(f).status.lower()
00135 except:
00136 print "error parsing manifest '%s'"%f
00137
00138
00139 if os.path.exists(os.path.join(os.path.dirname(f), "ROS_BUILD_BLACKLIST")):
00140
00141 self._review_status[pkg] = "ROS_BUILD_BLACKLIST"
00142 return self._review_status[pkg]
00143
00144 def get_review_notes(self, pkg):
00145 if pkg in self._review_notes:
00146 return self._review_notes[pkg]
00147 f = roslib.manifest.manifest_file(pkg)
00148 try:
00149 self._review_notes[pkg] = roslib.manifest.parse_file(f).notes
00150 except:
00151 print "error parsing manifest '%s'"%f
00152
00153
00154 if os.path.exists(os.path.join(os.path.dirname(f), "ROS_BUILD_BLACKLIST")):
00155
00156 self._review_notes[pkg] +=" ROS_BUILD_BLACKLIST"
00157
00158 if os.path.exists(os.path.join(os.path.dirname(f), "ROS_NOBUILD")):
00159 print f, "is installed"
00160 self._review_notes[pkg] +=" ROS_NOBUILD"
00161 return self._review_notes[pkg]
00162
00163 def get_review_color(self, pkg):
00164 status = self.get_review_status(pkg)
00165 return status_map.get(status, 'purple')
00166
00167
00168 def get_color(self, pkg):
00169 if self.color_by == "file":
00170 return self.get_file_color(pkg)
00171 if self.color_by == "review":
00172 return self.get_review_color(pkg)
00173 if self.color_by == "license":
00174 return self.get_license_color(pkg)
00175 else:
00176 return "gainsboro"
00177
00178 def get_color_palate(self):
00179 if self.color_by == "review":
00180 return status_map
00181 if self.color_by == "license":
00182 return license_map
00183 else:
00184 return None
00185
00186 rosmakeall_color_map = { "rosmakeall-testfailures.txt": "orange", "rosmakeall-buildfailures.txt": "red"}
00187
00188 def get_rosmakeall_color():
00189 path = roslib.rosenv.get_ros_root()
00190 color_dict = {}
00191 for f in rosmakeall_color_map:
00192 c = rosmakeall_color_map[f]
00193 try:
00194 with open(path+"/"+f) as input:
00195 for l in input:
00196 color_dict[l.strip()] = c
00197 except IOError:
00198 print >> sys.stderr, "Couldn't find %s"%f
00199 sys.exit(1)
00200 return color_dict
00201
00202
00203
00204 class HelperMethods:
00205 def __init__(self):
00206 self._stack_of = {}
00207 self._packages_of = {}
00208 self._deps = {}
00209 self._deps1 = {}
00210 self._stack_deps = {}
00211 self._stack_deps1 = {}
00212 self._deps_on = {}
00213 self._deps_on1 = {}
00214 self.verbose = False
00215 self.display_test_packages = True
00216
00217 def get_stack_of(self, pkg):
00218 if pkg not in self._stack_of:
00219 stack = roslib.stacks.stack_of(pkg)
00220 if not stack:
00221 stack = "None"
00222 self._stack_of[pkg] = stack
00223
00224 return self._stack_of[pkg]
00225
00226 def get_packages_of(self, stack):
00227 if stack == "None":
00228 return set()
00229 if stack not in self._packages_of:
00230 packages = roslib.stacks.packages_of(stack)
00231 self._packages_of[stack] = packages
00232
00233 return self._packages_of[stack]
00234
00235 def get_deps(self, pkg):
00236 if pkg not in self._deps:
00237 stack = roslib.rospack.rospack_depends(pkg)
00238 self._deps[pkg] = stack
00239
00240 return self._deps[pkg]
00241
00242 def get_deps1(self, pkg):
00243 if pkg not in self._deps1:
00244 stack = roslib.rospack.rospack_depends_1(pkg)
00245 self._deps1[pkg] = stack
00246
00247 return self._deps1[pkg]
00248
00249 def get_stack_deps(self, stack):
00250 if stack not in self._stack_deps:
00251 deps = roslib.rospack.rosstack_depends(stack)
00252 self._stack_deps[stack] = deps
00253
00254 return self._stack_deps[stack]
00255
00256 def get_stack_deps1(self, stack):
00257 if stack not in self._stack_deps1:
00258 deps = roslib.rospack.rosstack_depends_1(stack)
00259 self._stack_deps1[stack] = deps
00260
00261 return self._stack_deps1[stack]
00262
00263 def get_deps_on(self, pkg):
00264 if pkg not in self._deps_on:
00265 stack = roslib.rospack.rospack_depends_on(pkg)
00266 self._deps_on[pkg] = stack
00267
00268 return self._deps_on[pkg]
00269
00270 def get_deps_on1(self, pkg):
00271 if pkg not in self._deps_on1:
00272 stack = roslib.rospack.rospack_depends_on_1(pkg)
00273 self._deps_on1[pkg] = stack
00274
00275 return self._deps_on1[pkg]
00276
00277
00278 def get_child_deps(self, pkg):
00279 accum = set()
00280 for dep in self.get_deps1(pkg):
00281 accum.update(self.get_deps(dep))
00282 return accum
00283
00284
00285 def get_child_stack_deps(self, stack):
00286 accum = set()
00287 for dep in self.get_stack_deps1(stack):
00288 accum.update(self.get_stack_deps(dep))
00289 return accum
00290
00291 def get_internal_child_deps(self, pkg, stack = None):
00292 if stack == "None":
00293 return set()
00294 if not stack:
00295 stack = self.get_stack_of(pkg)
00296 if not stack or stack == "None":
00297 return set()
00298
00299 internal_deps = self.get_internal_pkg_dependencies(pkg, stack)
00300 accum = set()
00301 for dep in internal_deps:
00302 accum.update(self.get_deps1(dep))
00303
00304 return accum
00305
00306 def get_depth(self, all_pkgs, pkg, depth):
00307 for pkg_dep in (set(self.get_deps1(pkg)) & all_pkgs):
00308 depth = max(depth, self.get_depth(all_pkgs, pkg_dep, depth + 1))
00309 return depth
00310
00311 def build_rank(self, all_pkgs):
00312 rank = {}
00313 for pkg in all_pkgs:
00314 depth = self.get_depth(all_pkgs, pkg, 0)
00315 if depth in rank:
00316 rank[depth].append(pkg)
00317 else:
00318 rank[depth] = [pkg]
00319
00320 return rank
00321
00322 def get_external_pkg_dependencies(self, pkg, stack=None):
00323 if not stack:
00324 stack = self.get_stack_of(pkg)
00325 dependent_pkgs = self.get_deps1(pkg)
00326 return [ext for ext in dependent_pkgs if not self.get_stack_of(ext) == stack]
00327
00328 def get_internal_pkg_dependencies(self, pkg, stack = None):
00329 if not stack:
00330 stack = self.get_stack_of(pkg)
00331 dependent_pkgs = self.get_deps1(pkg)
00332 return [ext for ext in dependent_pkgs if self.get_stack_of(ext) == stack]
00333
00334
00335 def group_pkgs_by_stack(self, pkgs):
00336 stacks = {}
00337 for p in pkgs:
00338 stack = self.get_stack_of(p)
00339 if not stack:
00340 continue
00341 if stack in stacks:
00342 stacks[stack].add(p)
00343 else:
00344 stacks[stack] = set([p])
00345 return stacks
00346
00347 def get_stack_depth(self, pkg, depth):
00348 for stack_dep in self.get_deps1(pkg):
00349 depth = max(depth, get_stack_depth(stack_dep, depth + 1))
00350 return depth
00351
00352 def compute_stack_ranks(self):
00353 rank = {}
00354 for stack in roslib.stacks.list_stacks():
00355 depth = self.get_stack_depth(stack, 0)
00356 if depth in rank:
00357 rank[depth].append(stack)
00358 else:
00359 rank[depth] = [stack]
00360
00361 return rank
00362
00363
00364
00365 def build_stack_list(self, stack):
00366 if stack == "None":
00367 stack_contents = set()
00368 else:
00369 stack_contents = set(roslib.stacks.packages_of(stack))
00370
00371 external_dependencies = set()
00372 for pkg in stack_contents:
00373 external_dependencies.update(self.get_external_pkg_dependencies(pkg))
00374
00375 external_stack_dependencies = self.group_pkgs_by_stack(external_dependencies)
00376
00377 return stack_contents, external_stack_dependencies
00378
00379
00380 def dict_to_set(self, dict):
00381 accum = set()
00382 for key, val in dict.iteritems():
00383 accum.update(val)
00384 return accum
00385
00386 def generate_stack(self, stack, type, pkg_characterists):
00387
00388 print "Generating Graphviz Intermediate"
00389 outfile = tempfile.NamedTemporaryFile()
00390 outfile.write( """digraph ros {
00391 //size="11,8";
00392 //size="100,40";
00393 //clusterrank=global;
00394 node [color=gainsboro, style=filled];
00395 ranksep = 2.0;
00396 compound=true;
00397
00398 """)
00399 colors = ['red', 'blue', 'green', 'yellow', 'gold', 'orange','chocolate', 'gray44', 'deeppink', 'black']
00400
00401 color_palate = pkg_characterists.get_color_palate()
00402 if False and color_palate:
00403 outfile.write(' subgraph cluster__legend { style=bold; color=black; label = "Color Legend"; ')
00404 for type in color_palate:
00405 text_color="black"
00406 bg_color = color_palate[type]
00407 if bg_color == "black":
00408 text_color = "white"
00409 outfile.write('"%s" [color="%s", fontcolor="%s"];\n '%(type, bg_color, text_color))
00410 outfile.write('}\n')
00411 base_color = colors[random.randrange(0, len(colors))]
00412 try:
00413 stack_dir = roslib.stacks.get_stack_dir(stack)
00414 except roslib.exceptions.ROSLibException, ex:
00415 stack_dir = "None"
00416 outfile.write(' subgraph cluster__%s { style=bold; color=%s; label = "%s \\n (%s)"; '%(stack, base_color, stack, stack_dir))
00417 internal, external = self.build_stack_list(stack)
00418 if not self.display_test_packages:
00419 internal = set([p for p in internal if not p.startswith("test_")])
00420 for s in external:
00421 external[s] = set([p for p in external[s] if not p.startswith("test_")])
00422
00423
00424 for pkg in internal:
00425 outfile.write(' "%s" ;'%pkg)
00426 for s in external:
00427 try:
00428 stack_dir = roslib.stacks.get_stack_dir(s)
00429 except roslib.exceptions.ROSLibException, ex:
00430 stack_dir = "None"
00431 outfile.write(' subgraph cluster__%s_%s { style=bold; color=%s; label = "Stack: %s \\n (%s)"; '%(stack, s, base_color, s, stack_dir))
00432 for p in external[s]:
00433 outfile.write(' "%s.%s.%s" [ label = "%s"];'%(stack, s, p, p))
00434 outfile.write('}\n')
00435 outfile.write('}\n')
00436
00437 external_deps_connected = set()
00438 for pkg in internal:
00439 deps = self.get_deps1(pkg)
00440 node_args = []
00441
00442 color = pkg_characterists.get_color(pkg)
00443 node_args.append("color=%s"%color)
00444 if color == 'black':
00445 node_args.append("fontcolor=white")
00446
00447 notes = pkg_characterists.get_review_notes(pkg)
00448 if len(notes) > 0:
00449 node_args.append('label="%s\\n(%s)"' % (pkg, notes))
00450
00451 if pkg in internal:
00452 outfile.write(' "%s" [%s];\n'%(pkg, ', '.join(node_args)))
00453
00454
00455
00456
00457
00458 for dep in deps:
00459 if not self.verbose and dep in (self.get_internal_child_deps(pkg)):
00460
00461 continue
00462
00463 if dep in (internal | self.dict_to_set(external)):
00464 local_stack = self.get_stack_of(pkg)
00465 dependent_stack = self.get_stack_of(dep)
00466
00467
00468 if dependent_stack == local_stack:
00469 outfile.write( ' "%s" -> "%s";\n' % (pkg, dep))
00470 elif dependent_stack:
00471 intermediate = "%s.%s.%s"%(local_stack, dependent_stack, dep)
00472 outfile.write( ' "%s" -> "%s"[color="blue", style="dashed"];\n' % (pkg, intermediate))
00473
00474
00475 if False:
00476 rank = self.build_rank(internal)
00477 print rank
00478 for key in rank:
00479 if len(rank[key]) > 1:
00480 outfile.write('{ rank = same;')
00481 print "key", key
00482 for pkg_rank in rank[key]:
00483
00484 outfile.write(' "%s" ;'%pkg_rank)
00485 outfile.write('}\n')
00486
00487 outfile.write( '}\n')
00488 outfile.flush()
00489
00490
00491
00492
00493 try:
00494 output_filename = "stack_%s.%s"%(stack, type)
00495 print "Generating output %s"%output_filename
00496 subprocess.check_call(["dot", "-T%s"%type, outfile.name, "-o", output_filename])
00497 print "%s generated"%output_filename
00498 except subprocess.CalledProcessError:
00499 print >> sys.stderr, "failed to generate %s"%output_filename
00500
00501 return output_filename
00502
00503 def generate_composite(self, stacks, type, pkg_characterists, display_image = True):
00504 stack_files = {}
00505 if display_image:
00506 for s in stacks:
00507 stack_files[s] = self.generate_stack(s, type, pkg_characterists)
00508
00509 print "Generating Graphviz Intermediate"
00510 outfile = tempfile.NamedTemporaryFile()
00511 outfile.write( """digraph ros {
00512 //size="11,8";
00513 //size="100,40";
00514 //clusterrank=global;
00515 node [color=gainsboro, style=filled, shape=box];
00516 ranksep = 2.0;
00517 compound=true;
00518
00519 """)
00520 colors = ['red', 'blue', 'green', 'yellow', 'gold', 'orange','chocolate', 'gray44', 'deeppink', 'black']
00521
00522 color_palate = pkg_characterists.get_color_palate()
00523 if False and color_palate:
00524 outfile.write(' subgraph cluster__legend { style=bold; color=black; label = "Color Legend"; ')
00525 for type in color_palate:
00526 text_color="black"
00527 bg_color = color_palate[type]
00528 if bg_color == "black":
00529 text_color = "white"
00530 outfile.write('"%s" [color="%s", fontcolor="%s"];\n '%(type, bg_color, text_color))
00531 outfile.write('}\n')
00532
00533
00534
00535
00536
00537
00538 if False:
00539 outfile.write(' subgraph cluster__%s { style=bold; color=%s; label = "%s \\n (%s)"; '%(stack, base_color, stack, stack_dir))
00540 internal, external = self.build_stack_list(stack)
00541 for pkg in internal:
00542 outfile.write(' "%s" ;'%pkg)
00543 for s in external:
00544 try:
00545 stack_dir = roslib.stacks.get_stack_dir(s)
00546 except roslib.exceptions.ROSLibException, ex:
00547 stack_dir = "None"
00548 outfile.write(' subgraph cluster__%s_%s { style=bold; color=%s; label = "Stack: %s \\n (%s)"; '%(stack, s, base_color, s, stack_dir))
00549 for p in external[s]:
00550 outfile.write(' "%s.%s.%s" [ label = "%s"];'%(stack, s, p, p))
00551 outfile.write('}\n')
00552 outfile.write('}\n')
00553
00554 for stack in stacks:
00555
00556 deps = roslib.rospack.rosstack_depends_1(stack)
00557 node_args = []
00558
00559
00560 if stack in stack_files and display_image:
00561 node_args.append('image="%s"'%stack_files[stack])
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574 outfile.write(' "%s" [%s];\n'%(stack, ', '.join(node_args)))
00575
00576
00577
00578
00579
00580 for dep in deps:
00581 if not self.verbose and dep in self.get_child_stack_deps(stack):
00582
00583 continue
00584 if dep in stacks:
00585 outfile.write( ' "%s" -> "%s";\n' % (stack, dep))
00586 if False:
00587 local_stack = self.get_stack_of(pkg)
00588 dependent_stack = self.get_stack_of(dep)
00589
00590
00591 if dependent_stack == local_stack:
00592 outfile.write( ' "%s" -> "%s";\n' % (pkg, dep))
00593 elif dependent_stack:
00594 intermediate = "%s.%s.%s"%(local_stack, dependent_stack, dep)
00595 outfile.write( ' "%s" -> "%s"[color="blue", style="dashed"];\n' % (pkg, intermediate))
00596
00597
00598
00599
00600 outfile.write( '}\n')
00601 outfile.flush()
00602
00603 try:
00604 output_filename = "%s_deps.%s"%("composite", type)
00605 print "Generating output %s"%output_filename
00606 subprocess.check_call(["dot", "-T%s"%type, outfile.name, "-o", output_filename])
00607 print "%s generated"%output_filename
00608 except subprocess.CalledProcessError:
00609 print >> sys.stderr, "failed to generate %s"%output_filename
00610
00611 return output_filename
00612
00613
00614 def vdmain():
00615 parser = OptionParser(usage="usage: %prog [options]", prog='rxdeps')
00616
00617
00618 parser.add_option("-s", "--review", dest="review_status", default=False,
00619 action="store_true", help="color by review status")
00620 parser.add_option("--size", dest="size_by_deps", default=False,
00621 action="store_true", help="whether to size by number of depends on 1")
00622 parser.add_option("-l", "--license", dest="license", default=False,
00623 action="store_true", help="color by license type")
00624 parser.add_option("-v", dest="verbose", default=False,
00625 action="store_true", help="don't deduplicate dependencies")
00626 parser.add_option("--one", dest="target1", default=False,
00627 action="store_true", help="only draw one away from target")
00628 parser.add_option("-q", "--quiet", dest="quiet", default=False,
00629 action="store_true", help="quiet (remove links to common packages)")
00630 parser.add_option("--hide_single", dest="hide", default=False,
00631 action="store_true", help="hide (remove packages without links)")
00632 parser.add_option("--rank", dest="rank", default=False,
00633 action="store_true", help="layer by maximum dependency depth")
00634 parser.add_option("--cluster", dest="cluster", default=False,
00635 action="store_true", help="group by main subdir")
00636 parser.add_option("--color", metavar="FILENAME",
00637 dest="color_file", default=None,
00638 type="string", help="use color file")
00639 parser.add_option("-o", metavar="FILENAME",
00640 dest="output_filename", default=None,
00641 type="string", help="output_filename")
00642 parser.add_option("--graphviz-output", metavar="FILENAME",
00643 dest="graphviz_output_filename", default=None,
00644 type="string", help="Save the intermediate output to this filename.")
00645 parser.add_option("--target", metavar="PKG_NAMES_LIST,COMMA_SEPERATED",
00646 dest="targets", default='',
00647 type="string", help="target packages")
00648 parser.add_option("--stack", metavar="STACK_NAMES_LIST,COMMA_SEPERATED",
00649 dest="stacks", default='',
00650 type="string", help="target stacks")
00651 parser.add_option("--exclude", metavar="PKG_NAMES_LIST,COMMA_SEPERATED",
00652 dest="exclude", default='',
00653 type="string", help="exclude packages")
00654 parser.add_option("--message", metavar="full_message_name",
00655 dest="message", default='',
00656 type="string", help="target_message")
00657 parser.add_option("-T", metavar="File Type",
00658 dest="output_type", default="png",
00659 type="string", help="output file type")
00660 parser.add_option("--no-stack-details",
00661 dest="display_image", default=True,
00662 action="store_false", help="Do Not display stack details")
00663 parser.add_option("--new",
00664 dest="use_new_version", default=False,
00665 action="store_true", help="Use the new version")
00666
00667
00668
00669 options, args = parser.parse_args()
00670 if options.exclude:
00671 exclude = options.exclude.split(',')
00672 else:
00673 exclude = []
00674 if options.targets:
00675 targets = options.targets.split(',')
00676 all_pkgs = roslib.packages.list_pkgs()
00677 invalidtarget = [t for t in targets if t not in all_pkgs]
00678 if invalidtarget:
00679 parser.error("Invalid packages set as targets, %s"%invalidtarget)
00680 else:
00681 targets = []
00682
00683 if options.stacks:
00684 stacks = options.stacks.split(',')
00685 else:
00686 stacks = []
00687 if options.quiet:
00688 exclude.extend(['roscpp','std_msgs', 'rospy', 'std_srvs', 'robot_msgs', 'robot_srvs', 'rosconsole', 'tf'])
00689
00690 pkg_characterists = PackageCharacteristics()
00691 if options.review_status:
00692 pkg_characterists.set_color_by("review")
00693 elif options.license:
00694 pkg_characterists.set_color_by("license")
00695 elif options.color_file:
00696 pkg_characterists.set_color_by("file", options.color_file)
00697
00698
00699 if False:
00700 if options.rosmake:
00701 print "Coloring by rosmakeall results"
00702 color_dict = get_rosmakeall_color()
00703 color_palate = rosmakeall_color_map
00704
00705 if options.output_filename:
00706 output_filename = os.path.realpath(options.output_filename)
00707 else:
00708 output_filename = "deps.pdf"
00709
00710 helper = HelperMethods()
00711 helper.verbose = options.verbose
00712 helper.display_test_packages = not options.quiet
00713
00714 all_stacks = roslib.stacks.list_stacks()
00715 invalid_args = set([stack for stack in args if stack not in all_stacks])
00716 if invalid_args:
00717 parser.error("Invalid stacks passed as arguments %s"%invalid_args)
00718
00719 if not args and options.use_new_version:
00720 helper.generate_composite(roslib.stacks.list_stacks(), options.output_type, pkg_characterists, options.display_image)
00721 elif options.use_new_version:
00722 helper.generate_composite(args, options.output_type, pkg_characterists, options.display_image)
00723
00724 all_pkgs = set(targets)
00725 args = set(args)
00726 args.update([helper.get_stack_of(p) for p in targets])
00727 if not args and options.cluster:
00728 parser.error("Cluster option requires stacks as arguments or --target options")
00729 for s in args:
00730 all_pkgs.update(helper.get_packages_of(s))
00731 all_pkgs_plus_1 = set()
00732 for p in all_pkgs:
00733 all_pkgs_plus_1.update(helper.get_deps1(p))
00734
00735
00736 print "Generating Graphviz Intermediate"
00737 outfile = tempfile.NamedTemporaryFile()
00738 outfile.write( """digraph ros {
00739 //size="11,8";
00740 size="100,40";
00741 //clusterrank=global;
00742 node [color=gainsboro, style=filled];
00743 ranksep = 2.0;
00744 compound=true;
00745
00746 """)
00747 colors = ['red', 'blue', 'green', 'yellow', 'gold', 'orange','chocolate', 'gray44', 'deeppink', 'black']
00748
00749 color_palate = pkg_characterists.get_color_palate()
00750 if color_palate:
00751 outfile.write(' subgraph cluster__legend { style=bold; color=black; label = "Color Legend"; ')
00752 for type in color_palate:
00753 text_color="black"
00754 bg_color = color_palate[type]
00755 if bg_color == "black":
00756 text_color = "white"
00757 outfile.write('"%s" [color="%s", fontcolor="%s"];\n '%(type, bg_color, text_color))
00758 outfile.write('}\n')
00759 for cl in args:
00760 if options.cluster:
00761 base_color = colors[random.randrange(0, len(colors))]
00762 try:
00763 stack_dir = roslib.stacks.get_stack_dir(cl)
00764 except roslib.exceptions.ROSLibException, ex:
00765 stack_dir = "None"
00766 outfile.write(' subgraph cluster__%s { style=bold; color=%s; label = "%s \\n (%s)"; '%(cl, base_color, cl, stack_dir))
00767 internal, external = helper.build_stack_list(cl)
00768 for pkg in internal:
00769 outfile.write(' "%s" ;'%pkg)
00770 for s in external:
00771 try:
00772 stack_dir = roslib.stacks.get_stack_dir(s)
00773 except roslib.exceptions.ROSLibException, ex:
00774 stack_dir = "None"
00775 outfile.write(' subgraph cluster__%s_%s { style=bold; color=%s; label = "Stack: %s \\n (%s)"; '%(cl, s, base_color, s, stack_dir))
00776 for p in external[s]:
00777 outfile.write(' "%s.%s.%s" [ label = "%s"];'%(cl, s, p, p))
00778 outfile.write('}\n')
00779 outfile.write('}\n')
00780
00781 external_deps_connected = set()
00782 for pkg in all_pkgs:
00783 deps = helper.get_deps1(pkg)
00784 node_args = []
00785
00786 color = pkg_characterists.get_color(pkg)
00787 node_args.append("color=%s"%color)
00788 if color == 'black':
00789 node_args.append("fontcolor=white")
00790
00791
00792 if pkg in exclude:
00793 node_args.append("shape=box")
00794
00795
00796 if options.size_by_deps:
00797 num_deps = len(roslib.rospack.rospack_depends_on(pkg))
00798 node_args.append("width=%s,height=%s"%(.75 + .1 * sqrt(num_deps), .5 + .1* sqrt(num_deps)))
00799
00800
00801 if pkg in targets:
00802 node_args.append('peripheries=6, style=bold')
00803 elif pkg in exclude:
00804 node_args.append('peripheries=3, style=dashed')
00805
00806 notes = pkg_characterists.get_review_notes(pkg)
00807 if len(notes) > 0:
00808 node_args.append('label="%s\\n(%s)"' % (pkg, notes))
00809
00810 if options.hide:
00811 if len(roslib.rospack.rospack_depends_on(pkg)) == 0 and len(helper.get_deps(pkg)) == 0:
00812 print "Hiding unattached package %s"%pkg
00813 continue
00814
00815 if pkg in all_pkgs:
00816 outfile.write(' "%s" [%s];\n'%(pkg, ', '.join(node_args)))
00817
00818
00819
00820
00821
00822 for dep in deps:
00823 if not options.verbose and dep in helper.get_internal_child_deps(pkg):
00824 continue
00825 if dep in all_pkgs_plus_1:
00826 local_stack = helper.get_stack_of(pkg)
00827 dependent_stack = helper.get_stack_of(dep)
00828 if local_stack not in args or not local_stack:
00829 continue
00830 if not (options.cluster and not dependent_stack == local_stack):
00831 outfile.write( ' "%s" -> "%s";\n' % (pkg, dep))
00832 elif options.cluster and dependent_stack:
00833 intermediate = "%s.%s.%s"%(local_stack, dependent_stack, dep)
00834 deduplication = "%s.%s"%(local_stack, dependent_stack)
00835 outfile.write( ' "%s" -> "%s"[color="blue", style="dashed"];\n' % (pkg, intermediate))
00836 if not deduplication in external_deps_connected and dependent_stack in args:
00837 outfile.write( ' "%s" -> "%s"[color="red", style="dashed", ltail=cluster__%s_%s, lhead=cluster__%s];\n' % (intermediate, dep, local_stack, dependent_stack, dependent_stack))
00838 external_deps_connected.add(deduplication)
00839
00840
00841 if options.rank:
00842 rank = helper.build_rank(all_pkgs)
00843 for key in rank:
00844 if len(rank[key]) > 1:
00845 outfile.write('{ rank = same;')
00846 for pkg_rank in rank[key]:
00847 if pkg_rank in all_pkgs:
00848 outfile.write(' "%s" ;'%pkg_rank)
00849 outfile.write('}\n')
00850 outfile.write( '}\n')
00851 outfile.flush()
00852
00853 if options.graphviz_output_filename:
00854 graphviz_output_filename = os.path.realpath(options.graphviz_output_filename)
00855 shutil.copyfile(outfile.name, graphviz_output_filename)
00856
00857 try:
00858 print "Generating output %s"%output_filename
00859
00860 args = ["dot", "-V"]
00861 vstr = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[1]
00862 v = distutils.version.StrictVersion('2.16')
00863 r = re.compile(".*version ([^ ]*).*")
00864 print vstr
00865 m = r.search(vstr)
00866 if not m or not m.group(1):
00867 print 'Warning: failed to determine your version of dot. Assuming v2.16'
00868 else:
00869 version = distutils.version.StrictVersion(m.group(1))
00870 print 'Detected dot version %s' % (version)
00871 if version > distutils.version.StrictVersion('2.8'):
00872 subprocess.check_call(["dot", "-Tpdf", outfile.name, "-o", output_filename])
00873 print "%s generated"%output_filename
00874 else:
00875 orig = output_filename
00876 if output_filename.lower().endswith('.pdf'):
00877 output_filename = output_filename[:-3]+'ps'
00878 else:
00879 print "ERROR", output_filename
00880 subprocess.check_call(["dot", "-Tps2", outfile.name, "-o", output_filename])
00881 print "%s generated"%output_filename
00882
00883 print "Trying to create %s..."%orig
00884 subprocess.check_call(["ps2pdf", output_filename, orig])
00885 print "%s generated"%orig
00886 except subprocess.CalledProcessError:
00887 print >> sys.stderr, "failed to generate %s"%output_filename
00888
00889 if __name__ == '__main__':
00890 random.seed()
00891 vdmain()