1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
44
45
46 import sys
47
48 try:
49 from cStringIO import StringIO
50 except ImportError:
51 from io import StringIO
52
53 import rospkg
54
55 import roslib.msgs
56 from roslib.msgs import MsgSpecException
57 import roslib.names
58 import roslib.srvs
59
60
61 _header_type_name = 'std_msgs/Header'
62
64 """
65 Add the list of message types that spec depends on to depends.
66 @param spec: message to compute dependencies for
67 @type spec: roslib.msgs.MsgSpec/roslib.srvs.SrvSpec
68 @param deps [str]: list of dependencies. This list will be updated
69 with the dependencies of spec when the method completes
70 @type deps: [str]
71 @raise KeyError for invalid dependent types due to missing package dependencies.
72 """
73 valid_packages = ['', package_context]
74 try:
75 valid_packages = valid_packages + rospack.get_depends(package_context, implicit=True)
76 except rospkg.ResourceNotFound:
77
78
79
80 pass
81
82 for t in spec.types:
83 t = roslib.msgs.base_msg_type(t)
84 if not roslib.msgs.is_builtin(t):
85 t_package, t_base = roslib.names.package_resource_name(t)
86
87
88 if t == roslib.msgs.HEADER:
89
90 deps.append(_header_type_name)
91
92 if roslib.msgs.is_registered(t):
93 depspec = roslib.msgs.get_registered(t)
94 if t != roslib.msgs.HEADER:
95 if '/' in t:
96 deps.append(t)
97 else:
98 deps.append(package_context+'/'+t)
99
100 elif t_package in valid_packages:
101
102 key, depspec = roslib.msgs.load_by_type(t, package_context)
103 if t != roslib.msgs.HEADER:
104 deps.append(key)
105 roslib.msgs.register(key, depspec)
106 else:
107
108 raise KeyError(t)
109 _add_msgs_depends(rospack, depspec, deps, package_context)
110
111 -def compute_md5_text(get_deps_dict, spec):
112 """
113 Compute the text used for md5 calculation. MD5 spec states that we
114 removes comments and non-meaningful whitespace. We also strip
115 packages names from type names. For convenience sake, constants are
116 reordered ahead of other declarations, in the order that they were
117 originally defined.
118
119 @return: text for ROS MD5-processing
120 @rtype: str
121 """
122 uniquedeps = get_deps_dict['uniquedeps']
123 package = get_deps_dict['package']
124
125 compute_files = 'files' in get_deps_dict
126
127 buff = StringIO()
128
129 for c in spec.constants:
130 buff.write("%s %s=%s\n"%(c.type, c.name, c.val_text))
131 for type_, name in zip(spec.types, spec.names):
132 base_msg_type = roslib.msgs.base_msg_type(type_)
133
134 if roslib.msgs.is_builtin(base_msg_type):
135 buff.write("%s %s\n"%(type_, name))
136 else:
137
138
139
140
141
142 if base_msg_type == roslib.msgs.HEADER:
143 base_msg_type = _header_type_name
144
145 sub_pkg, _ = roslib.names.package_resource_name(base_msg_type)
146 sub_pkg = sub_pkg or package
147 sub_spec = roslib.msgs.get_registered(base_msg_type, package)
148 sub_deps = get_dependencies(sub_spec, sub_pkg, compute_files=compute_files)
149 sub_md5 = compute_md5(sub_deps)
150 buff.write("%s %s\n"%(sub_md5, name))
151
152 return buff.getvalue().strip()
153
155 """
156 subroutine of compute_md5()
157 @param get_deps_dict: dictionary returned by get_dependencies call
158 @type get_deps_dict: dict
159 @param hash: hash instance
160 @type hash: hash instance
161 """
162
163
164 from roslib.msgs import MsgSpec
165 from roslib.srvs import SrvSpec
166 spec = get_deps_dict['spec']
167 if isinstance(spec, MsgSpec):
168 hash.update(compute_md5_text(get_deps_dict, spec))
169 elif isinstance(spec, SrvSpec):
170 hash.update(compute_md5_text(get_deps_dict, spec.request))
171 hash.update(compute_md5_text(get_deps_dict, spec.response))
172 else:
173 raise Exception("[%s] is not a message or service"%spec)
174 return hash.hexdigest()
175
177 """
178 subroutine of compute_md5_v1()
179 @param get_deps_dict: dictionary returned by get_dependencies call
180 @type get_deps_dict: dict
181 @param hash: hash instance
182 @type hash: hash instance
183 """
184 uniquedeps = get_deps_dict['uniquedeps']
185 spec = get_deps_dict['spec']
186
187
188 hash.update(spec.text)
189
190 for d in uniquedeps:
191 hash.update(roslib.msgs.get_registered(d).text)
192 return hash.hexdigest()
193
195 """
196 Compute original V1 md5 hash for message/service. This was replaced with V2 in ROS 0.6.
197 @param get_deps_dict: dictionary returned by get_dependencies call
198 @type get_deps_dict: dict
199 @return: md5 hash
200 @rtype: str
201 """
202 import hashlib
203 return _compute_hash_v1(get_deps_dict, hashlib.md5())
204
206 """
207 Compute md5 hash for message/service
208 @param get_deps_dict dict: dictionary returned by get_dependencies call
209 @type get_deps_dict: dict
210 @return: md5 hash
211 @rtype: str
212 """
213 try:
214
215
216 import hashlib
217 return _compute_hash(get_deps_dict, hashlib.md5())
218 except ImportError:
219 import md5
220 return _compute_hash(get_deps_dict, md5.new())
221
222
223 compute_md5_v2 = compute_md5
224
225 -def compute_full_text(get_deps_dict):
226 """
227 Compute full text of message/service, including text of embedded
228 types. The text of the main msg/srv is listed first. Embedded
229 msg/srv files are denoted first by an 80-character '=' separator,
230 followed by a type declaration line,'MSG: pkg/type', followed by
231 the text of the embedded type.
232
233 @param get_deps_dict dict: dictionary returned by get_dependencies call
234 @type get_deps_dict: dict
235 @return: concatenated text for msg/srv file and embedded msg/srv types.
236 @rtype: str
237 """
238 buff = StringIO()
239 sep = '='*80+'\n'
240
241
242 buff.write(get_deps_dict['spec'].text)
243 buff.write('\n')
244
245 for d in get_deps_dict['uniquedeps']:
246 buff.write(sep)
247 buff.write("MSG: %s\n"%d)
248 buff.write(roslib.msgs.get_registered(d).text)
249 buff.write('\n')
250
251 return buff.getvalue()[:-1]
252
254 """
255 Compute dependencies of the specified message/service file
256 @param f: message or service file to get dependencies for
257 @type f: str
258 @param stdout pipe: stdout pipe
259 @type stdout: file
260 @param stderr pipe: stderr pipe
261 @type stderr: file
262 @return: 'files': list of files that \a file depends on,
263 'deps': list of dependencies by type, 'spec': Msgs/Srvs
264 instance.
265 @rtype: dict
266 """
267 package = rospkg.get_package_name(f)
268 spec = None
269 if f.endswith(roslib.msgs.EXT):
270 _, spec = roslib.msgs.load_from_file(f)
271 elif f.endswith(roslib.srvs.EXT):
272 _, spec = roslib.srvs.load_from_file(f)
273 else:
274 raise Exception("[%s] does not appear to be a message or service"%spec)
275 return get_dependencies(spec, package, stdout, stderr)
276
277 -def get_dependencies(spec, package, compute_files=True, stdout=sys.stdout, stderr=sys.stderr):
278 """
279 Compute dependencies of the specified Msgs/Srvs
280 @param spec: message or service instance
281 @type spec: L{roslib.msgs.MsgSpec}/L{roslib.srvs.SrvSpec}
282 @param package: package name
283 @type package: str
284 @param stdout: (optional) stdout pipe
285 @type stdout: file
286 @param stderr: (optional) stderr pipe
287 @type stderr: file
288 @param compute_files: (optional, default=True) compute file
289 dependencies of message ('files' key in return value)
290 @type compute_files: bool
291 @return: dict:
292 * 'files': list of files that \a file depends on
293 * 'deps': list of dependencies by type
294 * 'spec': Msgs/Srvs instance.
295 * 'uniquedeps': list of dependencies with duplicates removed,
296 * 'package': package that dependencies were generated relative to.
297 @rtype: dict
298 """
299
300
301
302
303
304
305 roslib.msgs._init()
306
307 deps = []
308 try:
309 rospack = rospkg.RosPack()
310 if isinstance(spec, roslib.msgs.MsgSpec):
311 _add_msgs_depends(rospack, spec, deps, package)
312 elif isinstance(spec, roslib.srvs.SrvSpec):
313 _add_msgs_depends(rospack, spec.request, deps, package)
314 _add_msgs_depends(rospack, spec.response, deps, package)
315 else:
316 raise MsgSpecException("spec does not appear to be a message or service")
317 except KeyError, e:
318 raise MsgSpecException("Cannot load type %s. Perhaps the package is missing a dependency."%(str(e)))
319
320
321
322 if compute_files:
323 files = {}
324 for d in set(deps):
325 d_pkg, t = roslib.names.package_resource_name(d)
326 d_pkg = d_pkg or package
327 files[d] = roslib.msgs.msg_file(d_pkg, t)
328 else:
329 files = None
330
331
332 uniquedeps = []
333 for d in deps:
334 if not d in uniquedeps:
335 uniquedeps.append(d)
336
337 if compute_files:
338 return { 'files': files, 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
339 else:
340 return { 'deps': deps, 'spec': spec, 'package': package, 'uniquedeps': uniquedeps }
341