Package roshlaunch :: Module depends
[frames] | no frames]

Source Code for Module roshlaunch.depends

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id: __init__.py 3803 2009-02-11 02:04:39Z rob_wheeler $ 
 34   
 35  """ 
 36  Utility module of roslaunch that extracts dependency information from 
 37  roslaunch files, including calculating missing package dependencies. 
 38  """ 
 39   
 40  import roslib; roslib.load_manifest('roslaunch') 
 41   
 42  import os 
 43  import sys 
 44   
 45  import roslib.manifest 
 46  import roslib.packages 
 47  # from roslib.substitution_args import resolve_args, SubstitutionException 
 48   
 49  from xml.dom.minidom import parse, parseString 
 50  from xml.dom import Node as DomNode 
 51   
 52  NAME="roslaunch-deps" 
 53   
54 -class RoslaunchDepsException(Exception):
55 """ 56 Base exception of roslaunch.depends errors. 57 """ 58 pass
59
60 -class RoslaunchDeps(object):
61 """ 62 Represents dependencies of a roslaunch file. 63 """
64 - def __init__(self):
65 self.nodes = [] 66 self.includes = [] 67 self.pkgs = []
68
69 -def _parse_launch(tags, launch_file, file_deps, verbose):
70 _, launch_file_pkg = roslib.packages.get_dir_pkg(os.path.dirname(os.path.abspath(launch_file))) 71 72 # process group, include, node, and test tags from launch file 73 for tag in [t for t in tags if t.nodeType == DomNode.ELEMENT_NODE]: 74 75 if tag.tagName == 'group': 76 77 #descend group tags as they can contain node tags 78 _parse_launch(tag.childNodes, launch_file, file_deps, verbose) 79 80 elif tag.tagName == 'include': 81 try: 82 sub_launch_file = resolve_args(tag.attributes['file'].value) 83 except KeyError, e: 84 raise RoslaunchDepsException("Cannot load roslaunch <%s> tag: missing required attribute %s.\nXML is %s"%(tag.tagName, str(e), tag.toxml())) 85 86 if verbose: 87 print "processing included launch %s"%sub_launch_file 88 89 # determine package dependency for included file 90 _, sub_pkg = roslib.packages.get_dir_pkg(os.path.dirname(os.path.abspath(sub_launch_file))) 91 if sub_pkg is None: 92 print >> sys.stderr, "ERROR: cannot determine package for [%s]"%sub_launch_file 93 94 file_deps[launch_file].includes.append(sub_launch_file) 95 if launch_file_pkg != sub_pkg: 96 file_deps[launch_file].pkgs.append(sub_pkg) 97 98 # recurse 99 file_deps[sub_launch_file] = RoslaunchDeps() 100 try: 101 dom = parse(sub_launch_file).getElementsByTagName('launch') 102 if not len(dom): 103 print >> sys.stderr, "ERROR: %s is not a valid roslaunch file"%sub_launch_file 104 else: 105 launch_tag = dom[0] 106 _parse_launch(launch_tag.childNodes, sub_launch_file, file_deps, verbose) 107 except IOError, e: 108 raise RoslaunchDepsException("Cannot load roslaunch include '%s' in '%s'"%(sub_launch_file, launch_file)) 109 110 elif tag.tagName in ['node', 'test']: 111 try: 112 pkg, type = [resolve_args(tag.attributes[a].value) for a in ['pkg', 'type']] 113 except KeyError, e: 114 raise RoslaunchDepsException("Cannot load roslaunch <%s> tag: missing required attribute %s.\nXML is %s"%(tag.tagName, str(e), tag.toxml())) 115 file_deps[launch_file].nodes.append((pkg, type)) 116 # we actually want to include the package itself if that's referenced 117 #if launch_file_pkg != pkg: 118 file_deps[launch_file].pkgs.append(pkg)
119
120 -def parse_launch(launch_file, file_deps, verbose):
121 if verbose: 122 print "processing launch %s"%launch_file 123 124 dom = parse(launch_file).getElementsByTagName('launch') 125 if not len(dom): 126 print >> sys.stderr, "ignoring %s as it is not a roslaunch file"%launch_file 127 return 128 129 file_deps[launch_file] = RoslaunchDeps() 130 launch_tag = dom[0] 131 _parse_launch(launch_tag.childNodes, launch_file, file_deps, verbose)
132
133 -def rl_file_deps(file_deps, launch_file, verbose=False):
134 """ 135 Generate file_deps file dependency info for the specified 136 roslaunch file and its dependencies. 137 @param file_deps: dictionary mapping roslaunch filenames to 138 roslaunch dependency information. This dictionary will be 139 updated with dependency information. 140 @type file_deps: { str : RoslaunchDeps } 141 @param verbose: if True, print verbose output 142 @type verbose: bool 143 @param launch_file: name of roslaunch file 144 @type launch_file: str 145 """ 146 parse_launch(launch_file, file_deps, verbose)
147
148 -def fullusage():
149 name = NAME 150 print """Usage: 151 \t%(name)s [options] <file-or-package> 152 """%locals()
153 176
177 -def calculate_missing(base_pkg, missing, file_deps):
178 """ 179 Calculate missing package dependencies in the manifest. This is 180 mainly used as a subroutine of roslaunch_deps(). 181 182 @param base_pkg: name of package where initial walk begins (unused). 183 @type base_pkg: str 184 @param missing: dictionary mapping package names to set of missing package dependencies. 185 @type missing: { str: set(str) } 186 @param file_deps: dictionary mapping launch file names to RoslaunchDeps of each file 187 @type file_deps: { str: RoslaunchDeps} 188 @return: missing (see parameter) 189 @rtype: { str: set(str) } 190 """ 191 for launch_file in file_deps.iterkeys(): 192 pkg_dir, pkg = roslib.packages.get_dir_pkg(os.path.dirname(os.path.abspath(launch_file))) 193 194 if pkg is None: #cannot determine package 195 print >> sys.stderr, "ERROR: cannot determine package for [%s]"%pkg 196 continue 197 m_file = roslib.manifest.manifest_file(pkg) 198 m = roslib.manifest.parse_file(m_file) 199 d_pkgs = set([d.package for d in m.depends]) 200 # make sure we don't count ourselves as a dep 201 d_pkgs.add(pkg) 202 203 204 diff = list(set(file_deps[launch_file].pkgs) - d_pkgs) 205 if not pkg in missing: 206 missing[pkg] = set() 207 missing[pkg].update(diff) 208 return missing
209 210
211 -def roslaunch_deps(files, verbose=False):
212 """ 213 @param packages: list of packages to check 214 @type packages: [str] 215 @param files [str]: list of roslaunch files to check. Must be in 216 same package. 217 @type files: [str] 218 @return: base_pkg, file_deps, missing. 219 base_pkg is the package of all files 220 file_deps is a { filename : RoslaunchDeps } dictionary of 221 roslaunch dependency information to update, indexed by roslaunch 222 file name. 223 missing is a { package : [packages] } dictionary of missing 224 manifest dependencies, indexed by package. 225 @rtype: str, dict, dict 226 """ 227 file_deps = {} 228 missing = {} 229 base_pkg = None 230 231 for launch_file in files: 232 if not os.path.exists(launch_file): 233 raise RoslaunchDepsException("roslaunch file [%s] does not exist"%launch_file) 234 235 pkg_dir, pkg = roslib.packages.get_dir_pkg(os.path.dirname(os.path.abspath(launch_file))) 236 if base_pkg and pkg != base_pkg: 237 raise RoslaunchDepsException("roslaunch files must be in the same package: %s vs. %s"%(base_pkg, pkg)) 238 base_pkg = pkg 239 rl_file_deps(file_deps, launch_file, verbose) 240 241 calculate_missing(base_pkg, missing, file_deps) 242 return base_pkg, file_deps, missing
243
244 -def roslaunch_deps_main(argv=sys.argv):
245 from optparse import OptionParser 246 parser = OptionParser(usage="usage: %prog [options] <file(s)...>", prog=NAME) 247 parser.add_option("--verbose", "-v", action="store_true", 248 dest="verbose", default=False, 249 help="Verbose output") 250 parser.add_option("--warn", "-w", action="store_true", 251 dest="warn", default=False, 252 help="Warn about missing manifest dependencies") 253 254 (options, args) = parser.parse_args(argv[1:]) 255 if not args: 256 parser.error('please specify a launch file') 257 258 files = [arg for arg in args if os.path.exists(arg)] 259 packages = [arg for arg in args if not os.path.exists(arg)] 260 if packages: 261 parser.error("cannot located %s"%','.join(packages)) 262 try: 263 base_pkg, file_deps, missing = roslaunch_deps(files, verbose=options.verbose) 264 except RoslaunchDepsException, e: 265 print >> sys.stderr, str(e) 266 sys.exit(1) 267 268 if options.warn: 269 print "Dependencies:" 270 271 print_deps(base_pkg, file_deps, options.verbose) 272 273 if options.warn: 274 print '\nMissing declarations:' 275 for pkg, miss in missing.iteritems(): 276 if miss: 277 print "%s/manifest.xml:"%pkg 278 for m in miss: 279 print ' <depend package="%s" />'%m
280 281 if __name__ == '__main__': 282 main() 283