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 12067 2010-11-09 20:31:14Z 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  import cStringIO 
 48   
 49  import roslib.msgs  
 50  import roslib.names  
 51  import roslib.packages  
 52  import roslib.srvs  
 53   
 54  # name of the Header type as gentools knows it 
 55  _header_type_name = 'std_msgs/Header' 
 56   
57 -def _add_msgs_depends(spec, deps, package_context):
58 """ 59 Add the list of message types that spec depends on to depends. 60 @param spec: message to compute dependencies for 61 @type spec: roslib.msgs.MsgSpec/roslib.srvs.SrvSpec 62 @param deps [str]: list of dependencies. This list will be updated 63 with the dependencies of spec when the method completes 64 @type deps: [str] 65 """ 66 for t in spec.types: 67 t = roslib.msgs.base_msg_type(t) 68 if not roslib.msgs.is_builtin(t): 69 # special mapping for header 70 if t == roslib.msgs.HEADER: 71 # have to re-names Header 72 deps.append(_header_type_name) 73 if roslib.msgs.is_registered(t): 74 depspec = roslib.msgs.get_registered(t) 75 if t != roslib.msgs.HEADER: 76 if '/' in t: 77 deps.append(t) 78 else: 79 deps.append(package_context+'/'+t) 80 else: 81 #lazy-load 82 key, depspec = roslib.msgs.load_by_type(t, package_context) 83 if t != roslib.msgs.HEADER: 84 deps.append(key) 85 roslib.msgs.register(key, depspec) 86 _add_msgs_depends(depspec, deps, package_context)
87
88 -def compute_md5_text(get_deps_dict, spec):
89 """ 90 Compute the text used for md5 calculation. MD5 spec states that we 91 removes comments and non-meaningful whitespace. We also strip 92 packages names from type names. For convenience sake, constants are 93 reordered ahead of other declarations, in the order that they were 94 originally defined. 95 96 @return: text for ROS MD5-processing 97 @rtype: str 98 """ 99 uniquedeps = get_deps_dict['uniquedeps'] 100 package = get_deps_dict['package'] 101 # #1554: need to suppress computation of files in dynamic generation case 102 compute_files = 'files' in get_deps_dict 103 104 buff = cStringIO.StringIO() 105 106 for c in spec.constants: 107 buff.write("%s %s=%s\n"%(c.type, c.name, c.val_text)) 108 for type_, name in zip(spec.types, spec.names): 109 base_msg_type = roslib.msgs.base_msg_type(type_) 110 # md5 spec strips package names 111 if roslib.msgs.is_builtin(base_msg_type): 112 buff.write("%s %s\n"%(type_, name)) 113 else: 114 # recursively generate md5 for subtype. have to build up 115 # dependency representation for subtype in order to 116 # generate md5 117 118 # - ugly special-case handling of Header 119 if base_msg_type == roslib.msgs.HEADER: 120 base_msg_type = _header_type_name 121 122 sub_pkg, _ = roslib.names.package_resource_name(base_msg_type) 123 sub_pkg = sub_pkg or package 124 sub_spec = roslib.msgs.get_registered(base_msg_type, package) 125 sub_deps = get_dependencies(sub_spec, sub_pkg, compute_files=compute_files) 126 sub_md5 = compute_md5(sub_deps) 127 buff.write("%s %s\n"%(sub_md5, name)) 128 129 return buff.getvalue().strip() # remove trailing new line
130
131 -def _compute_hash(get_deps_dict, hash):
132 """ 133 subroutine of compute_md5() 134 @param get_deps_dict: dictionary returned by get_dependencies call 135 @type get_deps_dict: dict 136 @param hash: hash instance 137 @type hash: hash instance 138 """ 139 # accumulate the hash 140 # - root file 141 from roslib.msgs import MsgSpec 142 from roslib.srvs import SrvSpec 143 spec = get_deps_dict['spec'] 144 if isinstance(spec, MsgSpec): 145 hash.update(compute_md5_text(get_deps_dict, spec)) 146 elif isinstance(spec, SrvSpec): 147 hash.update(compute_md5_text(get_deps_dict, spec.request)) 148 hash.update(compute_md5_text(get_deps_dict, spec.response)) 149 else: 150 raise Exception("[%s] is not a message or service"%spec) 151 return hash.hexdigest()
152
153 -def _compute_hash_v1(get_deps_dict, hash):
154 """ 155 subroutine of compute_md5_v1() 156 @param get_deps_dict: dictionary returned by get_dependencies call 157 @type get_deps_dict: dict 158 @param hash: hash instance 159 @type hash: hash instance 160 """ 161 uniquedeps = get_deps_dict['uniquedeps'] 162 spec = get_deps_dict['spec'] 163 # accumulate the hash 164 # - root file 165 hash.update(spec.text) 166 # - dependencies 167 for d in uniquedeps: 168 hash.update(roslib.msgs.get_registered(d).text) 169 return hash.hexdigest()
170
171 -def compute_md5_v1(get_deps_dict):
172 """ 173 Compute original V1 md5 hash for message/service. This was replaced with V2 in ROS 0.6. 174 @param get_deps_dict: dictionary returned by get_dependencies call 175 @type get_deps_dict: dict 176 @return: md5 hash 177 @rtype: str 178 """ 179 try: 180 # md5 is deprecated in Python 2.6 in favor of hashlib, but hashlib is 181 # unavailable in Python 2.4 182 import hashlib 183 return _compute_hash_v1(get_deps_dict, hashlib.md5()) 184 except ImportError: 185 import md5 186 return _compute_hash_v1(get_deps_dict, md5.new())
187
188 -def compute_md5(get_deps_dict):
189 """ 190 Compute md5 hash for message/service 191 @param get_deps_dict dict: dictionary returned by get_dependencies call 192 @type get_deps_dict: dict 193 @return: md5 hash 194 @rtype: str 195 """ 196 try: 197 # md5 is deprecated in Python 2.6 in favor of hashlib, but hashlib is 198 # unavailable in Python 2.4 199 import hashlib 200 return _compute_hash(get_deps_dict, hashlib.md5()) 201 except ImportError: 202 import md5 203 return _compute_hash(get_deps_dict, md5.new())
204 205 ## alias 206 compute_md5_v2 = compute_md5 207
208 -def compute_full_text(get_deps_dict):
209 """ 210 Compute full text of message/service, including text of embedded 211 types. The text of the main msg/srv is listed first. Embedded 212 msg/srv files are denoted first by an 80-character '=' separator, 213 followed by a type declaration line,'MSG: pkg/type', followed by 214 the text of the embedded type. 215 216 @param get_deps_dict dict: dictionary returned by get_dependencies call 217 @type get_deps_dict: dict 218 @return: concatenated text for msg/srv file and embedded msg/srv types. 219 @rtype: str 220 """ 221 buff = cStringIO.StringIO() 222 sep = '='*80+'\n' 223 224 # write the text of the top-level type 225 buff.write(get_deps_dict['spec'].text) 226 buff.write('\n') 227 # append the text of the dependencies (embedded types) 228 for d in get_deps_dict['uniquedeps']: 229 buff.write(sep) 230 buff.write("MSG: %s\n"%d) 231 buff.write(roslib.msgs.get_registered(d).text) 232 buff.write('\n') 233 # #1168: remove the trailing \n separator that is added by the concatenation logic 234 return buff.getvalue()[:-1]
235
236 -def get_file_dependencies(f, stdout=sys.stdout, stderr=sys.stderr):
237 """ 238 Compute dependencies of the specified message/service file 239 @param f: message or service file to get dependencies for 240 @type f: str 241 @param stdout pipe: stdout pipe 242 @type stdout: file 243 @param stderr pipe: stderr pipe 244 @type stderr: file 245 @return: 'files': list of files that \a file depends on, 246 'deps': list of dependencies by type, 'spec': Msgs/Srvs 247 instance. 248 @rtype: dict 249 """ 250 _, package = roslib.packages.get_dir_pkg(f) 251 spec = None 252 if f.endswith(roslib.msgs.EXT): 253 _, spec = roslib.msgs.load_from_file(f) 254 elif f.endswith(roslib.srvs.EXT): 255 _, spec = roslib.srvs.load_from_file(f) 256 else: 257 raise Exception("[%s] does not appear to be a message or service"%spec) 258 return get_dependencies(spec, package, stdout, stderr)
259
260 -def get_dependencies(spec, package, compute_files=True, stdout=sys.stdout, stderr=sys.stderr):
261 """ 262 Compute dependencies of the specified Msgs/Srvs 263 @param spec: message or service instance 264 @type spec: L{roslib.msgs.MsgSpec}/L{roslib.srvs.SrvSpec} 265 @param package: package name 266 @type package: str 267 @param stdout: (optional) stdout pipe 268 @type stdout: file 269 @param stderr: (optional) stderr pipe 270 @type stderr: file 271 @param compute_files: (optional, default=True) compute file 272 dependencies of message ('files' key in return value) 273 @type compute_files: bool 274 @return: dict: 275 * 'files': list of files that \a file depends on 276 * 'deps': list of dependencies by type 277 * 'spec': Msgs/Srvs instance. 278 * 'uniquedeps': list of dependencies with duplicates removed, 279 * 'package': package that dependencies were generated relative to. 280 @rtype: dict 281 """ 282 283 # #518: as a performance optimization, we're going to manually control the loading 284 # of msgs instead of doing package-wide loads. 285 286 #we're going to manipulate internal apis of msgs, so have to 287 #manually init 288 roslib.msgs._init() 289 290 deps = [] 291 if isinstance(spec, roslib.msgs.MsgSpec): 292 _add_msgs_depends(spec, deps, package) 293 elif isinstance(spec, roslib.srvs.SrvSpec): 294 _add_msgs_depends(spec.request, deps, package) 295 _add_msgs_depends(spec.response, deps, package) 296 else: 297 raise Exception("[%s] does not appear to be a message or service"%spec) 298 299 # convert from type names to file names 300 301 if compute_files: 302 files = {} 303 for d in set(deps): 304 d_pkg, t = roslib.names.package_resource_name(d) 305 d_pkg = d_pkg or package # convert '' -> local package 306 files[d] = roslib.msgs.msg_file(d_pkg, t) 307 else: 308 files = None 309 310 # create unique dependency list 311 uniquedeps = [] 312 for d in deps: 313 if not d in uniquedeps: 314 uniquedeps.append(d) 315 316 if compute_files: 317 return { 'files': files, 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps } 318 else: 319 return { 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
320