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