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 from roslib.msgs import MsgSpecException
55 import roslib.names
56 import roslib.packages
57 import roslib.srvs
58
59
60 _header_type_name = 'std_msgs/Header'
61
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
79 if t == roslib.msgs.HEADER:
80
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
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
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
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
125 if roslib.msgs.is_builtin(base_msg_type):
126 buff.write("%s %s\n"%(type_, name))
127 else:
128
129
130
131
132
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()
144
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
154
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
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
178
179 hash.update(spec.text)
180
181 for d in uniquedeps:
182 hash.update(roslib.msgs.get_registered(d).text)
183 return hash.hexdigest()
184
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
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
206
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
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
233 buff.write(get_deps_dict['spec'].text)
234 buff.write('\n')
235
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
242 return buff.getvalue()[:-1]
243
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
292
293
294
295
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
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
317 files[d] = roslib.msgs.msg_file(d_pkg, t)
318 else:
319 files = None
320
321
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