Get .asd-dep and package file generation working

This commit is contained in:
Bhaskara Marthi 2010-10-12 17:58:55 +00:00
parent 8c493fc1ba
commit 23cfb855b8
2 changed files with 80 additions and 366 deletions

View File

@ -90,12 +90,15 @@ def field_type(f):
else:
return elt_type
def parse_msg_type(f):
if f.base_type == 'Header':
return ('roslib', 'Header')
else:
return f.base_type.split('/')
# t2 no need for is_array
def msg_type(f):
if f.base_type == 'Header':
(pkg, msg) = ('roslib', 'Header')
else:
(pkg, msg) = f.base_type.split('/')
(pkg, msg) = parse_msg_type(f)
return '%s-msg:<%s>'%(pkg, msg)
def lisp_type(t):
@ -201,28 +204,16 @@ class Indent():
self.writer.dec_indent(self.inc)
def write_begin(s, spec, path):
"""
Writes the beginning of the file: a comment saying it's auto-generated and the in-package form
"Writes the beginning of the file: a comment saying it's auto-generated and the in-package form"
@param s: The stream to write to
@type s: stream
@param spec: The spec
@type spec: roslib.msgs.MsgSpec
@param path: The file this message is being generated for
@type path: str
"""
s.write('; Auto-generated. Do not edit!\n\n\n', newline=False)
s.write('(in-package %s-msg)\n\n\n'%spec.package, newline=False)
s.write(';//! \\htmlinclude %s.msg.html\n'%spec.short_name, newline=False) # t2
def write_slot_definition(s, field):
"""
Write the definition of a slot corresponding to a single message field
"""
"Write the definition of a slot corresponding to a single message field"
s.write('(%s'%field.name)
with Indent(s, 1):
@ -252,352 +243,10 @@ def write_defclass(s, spec, pkg):
def message_class(spec):
"""
Return the CLOS class name for this message type
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
"""
return '<%s>'%spec.short_name
def write_end(s, spec):
"""
Writes the end of the header file: the ending of the include guards
@param s: The stream to write to
@type s: stream
@param spec: The spec
@type spec: roslib.msgs.MsgSpec
"""
s.write('#endif // %s_MESSAGE_%s_H\n'%(spec.package.upper(), spec.short_name.upper()))
def write_generic_includes(s):
"""
Writes the includes that all messages need
@param s: The stream to write to
@type s: stream
"""
s.write('#include <string>\n')
s.write('#include <vector>\n')
s.write('#include <ostream>\n')
s.write('#include "ros/serialization.h"\n')
s.write('#include "ros/builtin_message_traits.h"\n')
s.write('#include "ros/message_operations.h"\n')
s.write('#include "ros/message.h"\n')
s.write('#include "ros/time.h"\n\n')
def write_includes(s, spec):
"""
Writes the message-specific includes
@param s: The stream to write to
@type s: stream
@param spec: The message spec to iterate over
@type spec: roslib.msgs.MsgSpec
"""
for field in spec.parsed_fields():
if (not field.is_builtin):
if (field.is_header):
s.write('#include "roslib/Header.h"\n')
else:
(pkg, name) = roslib.names.package_resource_name(field.base_type)
pkg = pkg or spec.package # convert '' to package
s.write('#include "%s/%s.h"\n'%(pkg, name))
s.write('\n')
def write_struct(s, spec, cpp_name_prefix):
"""
Writes the entire message struct: declaration, constructors, members, constants and (deprecated) member functions
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
@param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::"
@type cpp_name_prefix: str
"""
msg = spec.short_name
s.write('template <class ContainerAllocator>\n')
s.write('struct %s_ : public ros::Message\n{\n'%(msg))
s.write(' typedef %s_<ContainerAllocator> Type;\n\n'%(msg))
write_constructors(s, spec, cpp_name_prefix)
write_members(s, spec)
write_constant_declarations(s, spec)
write_deprecated_member_functions(s, spec)
(cpp_msg_unqualified, cpp_msg_with_alloc, cpp_msg_base) = cpp_message_declarations(cpp_name_prefix, msg)
s.write(' typedef boost::shared_ptr<%s> Ptr;\n'%(cpp_msg_with_alloc))
s.write(' typedef boost::shared_ptr<%s const> ConstPtr;\n'%(cpp_msg_with_alloc))
s.write('}; // struct %s\n'%(msg))
s.write('typedef %s_<std::allocator<void> > %s;\n\n'%(cpp_msg_base, msg))
s.write('typedef boost::shared_ptr<%s> %sPtr;\n'%(cpp_msg_base, msg))
s.write('typedef boost::shared_ptr<%s const> %sConstPtr;\n\n'%(cpp_msg_base, msg))
def default_value(type):
"""
Returns the value to initialize a message member with. 0 for integer types, 0.0 for floating point, false for bool,
empty string for everything else
@param type: The type
@type type: str
"""
if type in ['byte', 'int8', 'int16', 'int32', 'int64',
'char', 'uint8', 'uint16', 'uint32', 'uint64']:
return '0'
elif type in ['float32', 'float64']:
return '0.0'
elif type == 'bool':
return 'false'
return ""
def takes_allocator(type):
"""
Returns whether or not a type can take an allocator in its constructor. False for all builtin types except string.
True for all others.
@param type: The type
@type: str
"""
return not type in ['byte', 'int8', 'int16', 'int32', 'int64',
'char', 'uint8', 'uint16', 'uint32', 'uint64',
'float32', 'float64', 'bool', 'time', 'duration']
def write_initializer_list(s, spec, container_gets_allocator):
"""
Writes the initializer list for a constructor
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
@param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string)
should have the allocator passed to its constructor. Assumes the allocator is named _alloc.
@type container_gets_allocator: bool
"""
i = 0
for field in spec.parsed_fields():
if (i == 0):
s.write(' : ')
else:
s.write(' , ')
val = default_value(field.base_type)
use_alloc = takes_allocator(field.base_type)
if (field.is_array):
if (field.array_len is None and container_gets_allocator):
s.write('%s(_alloc)\n'%(field.name))
else:
s.write('%s()\n'%(field.name))
else:
if (container_gets_allocator and use_alloc):
s.write('%s(_alloc)\n'%(field.name))
else:
s.write('%s(%s)\n'%(field.name, val))
i = i + 1
def write_fixed_length_assigns(s, spec, container_gets_allocator, cpp_name_prefix):
"""
Initialize any fixed-length arrays
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
@param container_gets_allocator: Whether or not a container type (whether it's another message, a vector, array or string)
should have the allocator passed to its constructor. Assumes the allocator is named _alloc.
@type container_gets_allocator: bool
@param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::"
@type cpp_name_prefix: str
"""
# Assign all fixed-length arrays their default values
for field in spec.parsed_fields():
if (not field.is_array or field.array_len is None):
continue
val = default_value(field.base_type)
if (container_gets_allocator and takes_allocator(field.base_type)):
(cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, field.base_type)
s.write(' %s.assign(%s(_alloc));\n'%(field.name, cpp_msg_with_alloc))
elif (len(val) > 0):
s.write(' %s.assign(%s);\n'%(field.name, val))
def write_constructors(s, spec, cpp_name_prefix):
"""
Writes any necessary constructors for the message
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
@param cpp_name_prefix: The C++ prefix to use when referring to the message, e.g. "std_msgs::"
@type cpp_name_prefix: str
"""
msg = spec.short_name
# Default constructor
s.write(' %s_()\n'%(msg))
write_initializer_list(s, spec, False)
s.write(' {\n')
write_fixed_length_assigns(s, spec, False, cpp_name_prefix)
s.write(' }\n\n')
# Constructor that takes an allocator constructor
s.write(' %s_(const ContainerAllocator& _alloc)\n'%(msg))
write_initializer_list(s, spec, True)
s.write(' {\n')
write_fixed_length_assigns(s, spec, True, cpp_name_prefix)
s.write(' }\n\n')
def write_members(s, spec):
"""
Write all the member declarations
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
"""
[write_member(s, field) for field in spec.parsed_fields()]
def write_constant_declaration(s, constant):
"""
Write a constant value as a static member
@param s: The stream to write to
@type s: stream
@param constant: The constant
@type constant: roslib.msgs.Constant
"""
# integral types get their declarations as enums to allow use at compile time
if (constant.type in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64']):
s.write(' enum { %s = %s };\n'%(constant.name, constant.val))
else:
s.write(' static const %s %s;\n'%(msg_type_to_cpp(constant.type), constant.name))
def write_constant_declarations(s, spec):
"""
Write all the constants from a spec as static members
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
"""
[write_constant_declaration(s, constant) for constant in spec.constants]
s.write('\n')
def write_constant_definition(s, spec, constant):
"""
Write a constant value as a static member
@param s: The stream to write to
@type s: stream
@param constant: The constant
@type constant: roslib.msgs.Constant
"""
# integral types do not need a definition, since they've been defined where they are declared
if (constant.type not in ['byte', 'int8', 'int16', 'int32', 'int64', 'char', 'uint8', 'uint16', 'uint32', 'uint64', 'string']):
s.write('template<typename ContainerAllocator> const %s %s_<ContainerAllocator>::%s = %s;\n'%(msg_type_to_cpp(constant.type), spec.short_name, constant.name, constant.val))
elif (constant.type == 'string'):
s.write('template<typename ContainerAllocator> const %s %s_<ContainerAllocator>::%s = "%s";\n'%(msg_type_to_cpp(constant.type), spec.short_name, constant.name, escape_string(constant.val)))
def write_constant_definitions(s, spec):
"""
Write all the constants from a spec as static members
@param s: The stream to write to
@type s: stream
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
"""
[write_constant_definition(s, spec, constant) for constant in spec.constants]
s.write('\n')
def is_fixed_length(spec):
"""
Returns whether or not the message is fixed-length
@param spec: The message spec
@type spec: roslib.msgs.MsgSpec
@param package: The package of the
@type package: str
"""
types = []
for field in spec.parsed_fields():
if (field.is_array and field.array_len is None):
return False
if (field.base_type == 'string'):
return False
if (not field.is_builtin):
types.append(field.base_type)
types = set(types)
for type in types:
type = roslib.msgs.resolve_type(type, spec.package)
(_, new_spec) = roslib.msgs.load_by_type(type, spec.package)
if (not is_fixed_length(new_spec)):
return False
return True
def is_hex_string(str):
for c in str:
if c not in '0123456789abcdefABCDEF':
return False
return True
def write_operations(s, spec, cpp_name_prefix):
(cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name)
s.write('namespace ros\n{\n')
s.write('namespace message_operations\n{\n')
# Write the Printer operation
s.write('\ntemplate<class ContainerAllocator>\nstruct Printer<%s>\n{\n'%(cpp_msg_with_alloc))
s.write(' template<typename Stream> static void stream(Stream& s, const std::string& indent, const %s& v) \n {\n'%cpp_msg_with_alloc)
for field in spec.parsed_fields():
cpp_type = msg_type_to_cpp(field.base_type)
if (field.is_array):
s.write(' s << indent << "%s[]" << std::endl;\n'%(field.name))
s.write(' for (size_t i = 0; i < v.%s.size(); ++i)\n {\n'%(field.name))
s.write(' s << indent << " %s[" << i << "]: ";\n'%field.name)
indent_increment = ' '
if (not field.is_builtin):
s.write(' s << std::endl;\n')
s.write(' s << indent;\n')
indent_increment = ' ';
s.write(' Printer<%s>::stream(s, indent + "%s", v.%s[i]);\n'%(cpp_type, indent_increment, field.name))
s.write(' }\n')
else:
s.write(' s << indent << "%s: ";\n'%field.name)
indent_increment = ' '
if (not field.is_builtin or field.is_array):
s.write('s << std::endl;\n')
s.write(' Printer<%s>::stream(s, indent + "%s", v.%s);\n'%(cpp_type, indent_increment, field.name))
s.write(' }\n')
s.write('};\n\n')
s.write('\n')
s.write('} // namespace message_operations\n')
s.write('} // namespace ros\n\n')
def write_ostream_operator(s, spec, cpp_name_prefix):
(cpp_msg_unqualified, cpp_msg_with_alloc, _) = cpp_message_declarations(cpp_name_prefix, spec.short_name)
s.write('template<typename ContainerAllocator>\nstd::ostream& operator<<(std::ostream& s, const %s& v)\n{\n'%(cpp_msg_with_alloc))
s.write(' ros::message_operations::Printer<%s>::stream(s, "", v);\n return s;}\n\n'%(cpp_msg_with_alloc))
def write_serialize_length(s, v, is_array=False):
#t2
var = '__ros_arr_len' if is_array else '__ros_str_len'
@ -697,7 +346,6 @@ def write_deserialize_bits(s, v, num_bytes):
def write_deserialize_builtin(s, f, v):
# t0 possibly write setf once
if f.base_type == 'string':
write_deserialize_length(s)
with Indent(s):
@ -774,6 +422,28 @@ def write_deserialize(s, spec):
s.write('msg')
s.write(')')
def write_asd_deps(s, spec):
"""
Write the depended-upon packages
"""
pkgs = set()
for f in spec.parsed_fields():
if not f.is_builtin:
(pkg, _) = parse_msg_type(f)
pkgs.add('%s-msg'%pkg)
for p in pkgs:
s.write('%s\n'%p)
def write_package_exports(s, spec):
"Write the package exports for this message"
s.write('(in-package %s-msg)'%spec.package, indent=False)
s.write('(export \'(')
with Indent(s, inc=10, indent_first=False):
for f in spec.parsed_fields():
accessor = '%s-val'%f.name
s.write('%s'%accessor.upper())
s.write('))')
def write_ros_datatype(s, spec):
c = message_class(spec)
s.write('(defmethod ros-datatype ((msg (eql \'%s)))'%c)
@ -869,6 +539,10 @@ def generate(msg_path):
"""
(package_dir, package) = roslib.packages.get_dir_pkg(msg_path)
(_, spec) = roslib.msgs.load_from_file(msg_path, package)
########################################
# 1. Write the .lisp file
########################################
io = StringIO()
s = IndentedWriter(io)
@ -890,12 +564,33 @@ def generate(msg_path):
os.makedirs(output_dir)
except OSError, e:
pass
f = open('%s/%s.lisp'%(output_dir, spec.short_name), 'w')
print >> f, io.getvalue()
with open('%s/%s.lisp'%(output_dir, spec.short_name), 'w') as f:
print >> f, io.getvalue()
io.close()
########################################
# 2. Write the asd-deps file
########################################
s = StringIO()
write_asd_deps(s, spec)
with open('%s/.%s.asd-dep'%(output_dir, spec.short_name), 'w') as f:
f.write(s.getvalue())
s.close()
########################################
# 3. Write the _package file
########################################
io = StringIO()
s = IndentedWriter(io)
write_package_exports(s, spec)
with open('%s/_package_%s.lisp'%(output_dir, spec.short_name), 'w') as f:
f.write(io.getvalue())
io.close()
if __name__ == "__main__":
roslib.msgs.set_verbose(False)
generate(sys.argv[1])

View File

@ -8,7 +8,22 @@ then
FILENAME=$PKG/msg/$2.msg
rosrun genmsg_cpp genmsg_lisp $FILENAME
rosrun roslisp genmsg_lisp.py $FILENAME
diff -w $PKG/msg/lisp/$1/$2.lisp $PKG/msg_gen/lisp/$2.lisp | head -n 50
NAME="$2.lisp"
echo " $NAME"
diff -w $PKG/msg/lisp/$1/$NAME $PKG/msg_gen/lisp/$NAME | head -n 50
NAME="_package_$2.lisp"
echo " $NAME"
diff -w $PKG/msg/lisp/$1/$NAME $PKG/msg_gen/lisp/$NAME | head -n 50
NAME=".$2.asd-dep"
echo " $NAME"
F1=`mktemp`
F2=`mktemp`
sort $PKG/msg/lisp/$1/.$2.asd-dep > $F1
sort $PKG/msg_gen/lisp/.$2.asd-dep > $F2
diff -w $F1 $F2 | head -n 50
elif (( $# == 1 ))
then
@ -17,6 +32,10 @@ then
echo $p
$0 $args
done
PKG=`rospack find $1`
# diff -w $PKG/msg/lisp/$1/_package.lisp $PKG/msg_gen/lisp/_package.lisp | head -n 50
# diff -w $PKG/msg/lisp/$1/$1-msg.asd $PKG/msg_gen/lisp/$1-msg.asd | head -n 50
else
echo "Usage: test-genmsg-lisp PKG MSG or test-genmsg-lisp PKG"
exit 1