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