$search
00001 #!/usr/bin/env python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2009, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of Willow Garage, Inc. nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 # 00034 00035 ## ROS message source code generation for C++ 00036 ## 00037 ## Converts ROS .msg files in a package into C++ source code implementations. 00038 00039 from rosidl import log, plog 00040 00041 import genmsg_cpp 00042 00043 import sys 00044 import os 00045 import traceback 00046 00047 # roslib.msgs contains the utilities for parsing .msg specifications. It is meant to have no rospy-specific knowledge 00048 import rosidl.srvs 00049 #import roslib.packages 00050 import rosidl.gentools 00051 00052 from cStringIO import StringIO 00053 00054 def write_begin(s, spec, file): 00055 """ 00056 Writes the beginning of the header file: a comment saying it's auto-generated and the include guards 00057 00058 @param s: The stream to write to 00059 @type s: stream 00060 @param spec: The spec 00061 @type spec: roslib.srvs.SrvSpec 00062 @param file: The file this service is being generated for 00063 @type file: str 00064 """ 00065 s.write("/* Auto-generated by genmsg_cpp for file %s */\n"%(file)) 00066 s.write('#ifndef %s_SERVICE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00067 s.write('#define %s_SERVICE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00068 00069 def write_end(s, spec): 00070 """ 00071 Writes the end of the header file: the ending of the include guards 00072 00073 @param s: The stream to write to 00074 @type s: stream 00075 @param spec: The spec 00076 @type spec: roslib.srvs.SrvSpec 00077 """ 00078 s.write('#endif // %s_SERVICE_%s_H\n'%(spec.package.upper(), spec.short_name.upper())) 00079 00080 def write_generic_includes(s): 00081 """ 00082 Writes the includes that all service need 00083 00084 @param s: The stream to write to 00085 @type s: stream 00086 """ 00087 s.write('#include "ros/service_traits.h"\n\n') 00088 00089 def write_trait_char_class(s, class_name, cpp_msg, value): 00090 """ 00091 Writes a class trait for traits which have static value() members that return const char* 00092 00093 e.g. write_trait_char_class(s, "MD5Sum", "std_srvs::Empty", "hello") yields: 00094 template<> 00095 struct MD5Sum<std_srvs::Empty> 00096 { 00097 static const char* value() { return "hello"; } 00098 static const char* value(const std_srvs::Empty&) { return value(); } 00099 }; 00100 00101 @param s: The stream to write to 00102 @type s: stream 00103 @param class_name: The name of the trait class 00104 @type class_name: str 00105 @param cpp_msg: The C++ message declaration, e.g. "std_srvs::Empty" 00106 @type cpp_msg: str 00107 @param value: The string value to return from the value() function 00108 @type value: str 00109 """ 00110 s.write('template<>\nstruct %s<%s> {\n'%(class_name, cpp_msg)) 00111 s.write(' static const char* value() \n {\n return "%s";\n }\n\n'%(value)) 00112 s.write(' static const char* value(const %s&) { return value(); } \n'%(cpp_msg)) 00113 s.write('};\n\n') 00114 00115 def write_traits(s, spec, cpp_name_prefix, includepath): 00116 """ 00117 Write all the service traits for a message 00118 00119 @param s: The stream to write to 00120 @type s: stream 00121 @param spec: The service spec 00122 @type spec: roslib.srvs.SrvSpec 00123 @param cpp_name_prefix: The C++ prefix to prepend when referencing the service, e.g. "std_srvs::" 00124 @type cpp_name_prefix: str 00125 """ 00126 gendeps_dict = rosidl.gentools.get_dependencies(spec, spec.package, includepath) 00127 md5sum = rosidl.gentools.compute_md5(gendeps_dict, includepath) 00128 00129 s.write('namespace ros\n{\n') 00130 s.write('namespace service_traits\n{\n') 00131 00132 request_with_allocator = '%s%s_<ContainerAllocator> '%(cpp_name_prefix, spec.request.short_name) 00133 response_with_allocator = '%s%s_<ContainerAllocator> '%(cpp_name_prefix, spec.response.short_name) 00134 00135 write_trait_char_class(s, 'MD5Sum', '%s%s'%(cpp_name_prefix, spec.short_name), md5sum); 00136 write_trait_char_class(s, 'DataType', '%s%s'%(cpp_name_prefix, spec.short_name), spec.full_name); 00137 genmsg_cpp.write_trait_char_class(s, 'MD5Sum', request_with_allocator, md5sum) 00138 genmsg_cpp.write_trait_char_class(s, 'DataType', request_with_allocator, spec.full_name) 00139 genmsg_cpp.write_trait_char_class(s, 'MD5Sum', response_with_allocator, md5sum) 00140 genmsg_cpp.write_trait_char_class(s, 'DataType', response_with_allocator, spec.full_name) 00141 s.write('} // namespace service_traits\n') 00142 s.write('} // namespace ros\n\n') 00143 00144 def generate(srv_path, options): 00145 """ 00146 Generate a service 00147 00148 @param srv_path: the path to the .srv file 00149 @type srv_path: str 00150 """ 00151 # (package_dir, package) = roslib.packages.get_dir_pkg(srv_path) 00152 (_, spec) = rosidl.srvs.load_from_file(srv_path, options.package) 00153 00154 s = StringIO() 00155 cpp_prefix = '%s::'%(options.package) 00156 write_begin(s, spec, srv_path) 00157 genmsg_cpp.write_generic_includes(s) 00158 write_generic_includes(s) 00159 genmsg_cpp.write_includes(s, spec.request) 00160 s.write('\n') 00161 genmsg_cpp.write_includes(s, spec.response) 00162 00163 gendeps_dict = rosidl.gentools.get_dependencies(spec, spec.package, 00164 options.includepath, 00165 compute_files=False) 00166 00167 md5sum = rosidl.gentools.compute_md5(gendeps_dict, options.includepath) 00168 00169 s.write('namespace %s\n{\n'%(options.package)) 00170 genmsg_cpp.write_struct(s, spec.request, cpp_prefix, {'ServerMD5Sum': md5sum}) 00171 s.write('\n') 00172 genmsg_cpp.write_struct(s, spec.response, cpp_prefix, {'ServerMD5Sum': md5sum}) 00173 s.write('struct %s\n{\n'%(spec.short_name)) 00174 s.write('\n') 00175 s.write('typedef %s Request;\n'%(spec.request.short_name)) 00176 s.write('typedef %s Response;\n'%(spec.response.short_name)) 00177 s.write('Request request;\n') 00178 s.write('Response response;\n\n') 00179 s.write('typedef Request RequestType;\n') 00180 s.write('typedef Response ResponseType;\n') 00181 s.write('}; // struct %s\n'%(spec.short_name)) 00182 s.write('} // namespace %s\n\n'%(options.package)) 00183 00184 request_cpp_name = "Request" 00185 response_cpp_name = "Response" 00186 log("options.includepath", str(options.includepath)) 00187 genmsg_cpp.write_traits(s, spec.request, cpp_prefix, options.includepath) 00188 s.write('\n') 00189 genmsg_cpp.write_traits(s, spec.response, cpp_prefix, options.includepath) 00190 genmsg_cpp.write_serialization(s, spec.request, cpp_prefix) 00191 s.write('\n') 00192 genmsg_cpp.write_serialization(s, spec.response, cpp_prefix) 00193 00194 write_traits(s, spec, cpp_prefix, options.includepath) 00195 00196 write_end(s, spec) 00197 00198 if 'ROS_BUILD' in os.environ: 00199 package_dir = os.environ['ROS_BUILD'] 00200 output_dir = os.path.join(options.outdir, options.package) 00201 if (not os.path.exists(output_dir)): 00202 # if we're being run concurrently, the above test can report false but os.makedirs can still fail if 00203 # another copy just created the directory 00204 try: 00205 os.makedirs(output_dir) 00206 except OSError, e: 00207 pass 00208 00209 ofile = os.path.join(output_dir, spec.short_name + ".h") 00210 f = open(ofile, 'w') 00211 print "Writing to", ofile 00212 print >> f, s.getvalue() 00213 00214 s.close() 00215 00216 def generate_services(argv): 00217 # print argv 00218 from optparse import OptionParser 00219 parser = OptionParser("generates c++ message serialization code") 00220 parser.add_option("-p", dest='package', 00221 help="package name") 00222 00223 parser.add_option("-o", dest='outdir', 00224 help="directory in which to place output files") 00225 00226 parser.add_option("-I", dest='includepath', 00227 help="include path to search for messages", 00228 action="append") 00229 (options, argv) = parser.parse_args(argv) 00230 00231 assert len(argv) == 2 00232 00233 generate(argv[1], options) 00234 00235 if __name__ == "__main__": 00236 generate_services(sys.argv) 00237