Package roslib :: Module gentools
[frames] | no frames]

Source Code for Module roslib.gentools

  1  #! /usr/bin/env python 
  2  # Software License Agreement (BSD License) 
  3  # 
  4  # Copyright (c) 2008, Willow Garage, Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions 
  9  # are met: 
 10  # 
 11  #  * Redistributions of source code must retain the above copyright 
 12  #    notice, this list of conditions and the following disclaimer. 
 13  #  * Redistributions in binary form must reproduce the above 
 14  #    copyright notice, this list of conditions and the following 
 15  #    disclaimer in the documentation and/or other materials provided 
 16  #    with the distribution. 
 17  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 18  #    contributors may be used to endorse or promote products derived 
 19  #    from this software without specific prior written permission. 
 20  # 
 21  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 22  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 23  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 24  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 25  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 26  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 27  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 28  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 29  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 30  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 31  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 32  # POSSIBILITY OF SUCH DAMAGE. 
 33  # 
 34  # Revision $Id: gentools.py 15359 2011-11-14 17:48:11Z kwc $ 
 35  # $Author: kwc $ 
 36   
 37  """ 
 38  Library for supporting message and service generation for all ROS 
 39  client libraries. This is mainly responsible for calculating the 
 40  md5sums and message definitions of classes. 
 41  """ 
 42   
 43  # NOTE: this should not contain any rospy-specific code. The rospy 
 44  # generator library is rospy.genpy. 
 45   
 46  import sys 
 47   
 48  try: 
 49      from cStringIO import StringIO # Python 2.x 
 50  except ImportError: 
 51      from io import StringIO # Python 3.x 
 52   
 53  import roslib.msgs  
 54  from roslib.msgs import MsgSpecException 
 55  import roslib.names  
 56  import roslib.packages  
 57  import roslib.srvs  
 58   
 59  # name of the Header type as gentools knows it 
 60  _header_type_name = 'std_msgs/Header' 
 61   
62 -def _add_msgs_depends(spec, deps, package_context):
63 """ 64 Add the list of message types that spec depends on to depends. 65 @param spec: message to compute dependencies for 66 @type spec: roslib.msgs.MsgSpec/roslib.srvs.SrvSpec 67 @param deps [str]: list of dependencies. This list will be updated 68 with the dependencies of spec when the method completes 69 @type deps: [str] 70 @raise KeyError for invalid dependent types due to missing package dependencies. 71 """ 72 valid_packages = ['', package_context] + roslib.rospack.rospack_depends(package_context) 73 for t in spec.types: 74 t = roslib.msgs.base_msg_type(t) 75 if not roslib.msgs.is_builtin(t): 76 t_package, t_base = roslib.names.package_resource_name(t) 77 78 # special mapping for header 79 if t == roslib.msgs.HEADER: 80 # have to re-names Header 81 deps.append(_header_type_name) 82 83 if roslib.msgs.is_registered(t): 84 depspec = roslib.msgs.get_registered(t) 85 if t != roslib.msgs.HEADER: 86 if '/' in t: 87 deps.append(t) 88 else: 89 deps.append(package_context+'/'+t) 90 91 elif t_package in valid_packages: 92 # if we are allowed to load the message, load it. 93 key, depspec = roslib.msgs.load_by_type(t, package_context) 94 if t != roslib.msgs.HEADER: 95 deps.append(key) 96 roslib.msgs.register(key, depspec) 97 else: 98 # not allowed to load the message, so error. 99 raise KeyError(t) 100 _add_msgs_depends(depspec, deps, package_context)
101
102 -def compute_md5_text(get_deps_dict, spec):
103 """ 104 Compute the text used for md5 calculation. MD5 spec states that we 105 removes comments and non-meaningful whitespace. We also strip 106 packages names from type names. For convenience sake, constants are 107 reordered ahead of other declarations, in the order that they were 108 originally defined. 109 110 @return: text for ROS MD5-processing 111 @rtype: str 112 """ 113 uniquedeps = get_deps_dict['uniquedeps'] 114 package = get_deps_dict['package'] 115 # #1554: need to suppress computation of files in dynamic generation case 116 compute_files = 'files' in get_deps_dict 117 118 buff = StringIO() 119 120 for c in spec.constants: 121 buff.write("%s %s=%s\n"%(c.type, c.name, c.val_text)) 122 for type_, name in zip(spec.types, spec.names): 123 base_msg_type = roslib.msgs.base_msg_type(type_) 124 # md5 spec strips package names 125 if roslib.msgs.is_builtin(base_msg_type): 126 buff.write("%s %s\n"%(type_, name)) 127 else: 128 # recursively generate md5 for subtype. have to build up 129 # dependency representation for subtype in order to 130 # generate md5 131 132 # - ugly special-case handling of Header 133 if base_msg_type == roslib.msgs.HEADER: 134 base_msg_type = _header_type_name 135 136 sub_pkg, _ = roslib.names.package_resource_name(base_msg_type) 137 sub_pkg = sub_pkg or package 138 sub_spec = roslib.msgs.get_registered(base_msg_type, package) 139 sub_deps = get_dependencies(sub_spec, sub_pkg, compute_files=compute_files) 140 sub_md5 = compute_md5(sub_deps) 141 buff.write("%s %s\n"%(sub_md5, name)) 142 143 return buff.getvalue().strip() # remove trailing new line
144
145 -def _compute_hash(get_deps_dict, hash):
146 """ 147 subroutine of compute_md5() 148 @param get_deps_dict: dictionary returned by get_dependencies call 149 @type get_deps_dict: dict 150 @param hash: hash instance 151 @type hash: hash instance 152 """ 153 # accumulate the hash 154 # - root file 155 from roslib.msgs import MsgSpec 156 from roslib.srvs import SrvSpec 157 spec = get_deps_dict['spec'] 158 if isinstance(spec, MsgSpec): 159 hash.update(compute_md5_text(get_deps_dict, spec)) 160 elif isinstance(spec, SrvSpec): 161 hash.update(compute_md5_text(get_deps_dict, spec.request)) 162 hash.update(compute_md5_text(get_deps_dict, spec.response)) 163 else: 164 raise Exception("[%s] is not a message or service"%spec) 165 return hash.hexdigest()
166
167 -def _compute_hash_v1(get_deps_dict, hash):
168 """ 169 subroutine of compute_md5_v1() 170 @param get_deps_dict: dictionary returned by get_dependencies call 171 @type get_deps_dict: dict 172 @param hash: hash instance 173 @type hash: hash instance 174 """ 175 uniquedeps = get_deps_dict['uniquedeps'] 176 spec = get_deps_dict['spec'] 177 # accumulate the hash 178 # - root file 179 hash.update(spec.text) 180 # - dependencies 181 for d in uniquedeps: 182 hash.update(roslib.msgs.get_registered(d).text) 183 return hash.hexdigest()
184
185 -def compute_md5_v1(get_deps_dict):
186 """ 187 Compute original V1 md5 hash for message/service. This was replaced with V2 in ROS 0.6. 188 @param get_deps_dict: dictionary returned by get_dependencies call 189 @type get_deps_dict: dict 190 @return: md5 hash 191 @rtype: str 192 """ 193 import hashlib 194 return _compute_hash_v1(get_deps_dict, hashlib.md5())
195
196 -def compute_md5(get_deps_dict):
197 """ 198 Compute md5 hash for message/service 199 @param get_deps_dict dict: dictionary returned by get_dependencies call 200 @type get_deps_dict: dict 201 @return: md5 hash 202 @rtype: str 203 """ 204 try: 205 # md5 is deprecated in Python 2.6 in favor of hashlib, but hashlib is 206 # unavailable in Python 2.4 207 import hashlib 208 return _compute_hash(get_deps_dict, hashlib.md5()) 209 except ImportError: 210 import md5 211 return _compute_hash(get_deps_dict, md5.new())
212 213 ## alias 214 compute_md5_v2 = compute_md5 215
216 -def compute_full_text(get_deps_dict):
217 """ 218 Compute full text of message/service, including text of embedded 219 types. The text of the main msg/srv is listed first. Embedded 220 msg/srv files are denoted first by an 80-character '=' separator, 221 followed by a type declaration line,'MSG: pkg/type', followed by 222 the text of the embedded type. 223 224 @param get_deps_dict dict: dictionary returned by get_dependencies call 225 @type get_deps_dict: dict 226 @return: concatenated text for msg/srv file and embedded msg/srv types. 227 @rtype: str 228 """ 229 buff = StringIO() 230 sep = '='*80+'\n' 231 232 # write the text of the top-level type 233 buff.write(get_deps_dict['spec'].text) 234 buff.write('\n') 235 # append the text of the dependencies (embedded types) 236 for d in get_deps_dict['uniquedeps']: 237 buff.write(sep) 238 buff.write("MSG: %s\n"%d) 239 buff.write(roslib.msgs.get_registered(d).text) 240 buff.write('\n') 241 # #1168: remove the trailing \n separator that is added by the concatenation logic 242 return buff.getvalue()[:-1]
243
244 -def get_file_dependencies(f, stdout=sys.stdout, stderr=sys.stderr):
245 """ 246 Compute dependencies of the specified message/service file 247 @param f: message or service file to get dependencies for 248 @type f: str 249 @param stdout pipe: stdout pipe 250 @type stdout: file 251 @param stderr pipe: stderr pipe 252 @type stderr: file 253 @return: 'files': list of files that \a file depends on, 254 'deps': list of dependencies by type, 'spec': Msgs/Srvs 255 instance. 256 @rtype: dict 257 """ 258 _, package = roslib.packages.get_dir_pkg(f) 259 spec = None 260 if f.endswith(roslib.msgs.EXT): 261 _, spec = roslib.msgs.load_from_file(f) 262 elif f.endswith(roslib.srvs.EXT): 263 _, spec = roslib.srvs.load_from_file(f) 264 else: 265 raise Exception("[%s] does not appear to be a message or service"%spec) 266 return get_dependencies(spec, package, stdout, stderr)
267
268 -def get_dependencies(spec, package, compute_files=True, stdout=sys.stdout, stderr=sys.stderr):
269 """ 270 Compute dependencies of the specified Msgs/Srvs 271 @param spec: message or service instance 272 @type spec: L{roslib.msgs.MsgSpec}/L{roslib.srvs.SrvSpec} 273 @param package: package name 274 @type package: str 275 @param stdout: (optional) stdout pipe 276 @type stdout: file 277 @param stderr: (optional) stderr pipe 278 @type stderr: file 279 @param compute_files: (optional, default=True) compute file 280 dependencies of message ('files' key in return value) 281 @type compute_files: bool 282 @return: dict: 283 * 'files': list of files that \a file depends on 284 * 'deps': list of dependencies by type 285 * 'spec': Msgs/Srvs instance. 286 * 'uniquedeps': list of dependencies with duplicates removed, 287 * 'package': package that dependencies were generated relative to. 288 @rtype: dict 289 """ 290 291 # #518: as a performance optimization, we're going to manually control the loading 292 # of msgs instead of doing package-wide loads. 293 294 #we're going to manipulate internal apis of msgs, so have to 295 #manually init 296 roslib.msgs._init() 297 298 deps = [] 299 try: 300 if isinstance(spec, roslib.msgs.MsgSpec): 301 _add_msgs_depends(spec, deps, package) 302 elif isinstance(spec, roslib.srvs.SrvSpec): 303 _add_msgs_depends(spec.request, deps, package) 304 _add_msgs_depends(spec.response, deps, package) 305 else: 306 raise MsgSpecException("spec does not appear to be a message or service") 307 except KeyError as e: 308 raise MsgSpecException("Cannot load type %s. Perhaps the package is missing a dependency."%(str(e))) 309 310 # convert from type names to file names 311 312 if compute_files: 313 files = {} 314 for d in set(deps): 315 d_pkg, t = roslib.names.package_resource_name(d) 316 d_pkg = d_pkg or package # convert '' -> local package 317 files[d] = roslib.msgs.msg_file(d_pkg, t) 318 else: 319 files = None 320 321 # create unique dependency list 322 uniquedeps = [] 323 for d in deps: 324 if not d in uniquedeps: 325 uniquedeps.append(d) 326 327 if compute_files: 328 return { 'files': files, 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps } 329 else: 330 return { 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
331