Skip to content

Commit 7cbb116

Browse files
authored
Type Description Codegen and Typesupport (rep2011) (#727)
* Generate code for and expose interface type descriptions * Use the C definitions of type descriptions instead of generating cpp * Add descriptions to srv support * Add a few more description codegen smoke tests * Add generator c package.xml dependencies * Type description functions, not globals * Encode w strings as utf 8 * Add ability to disable C description codegen * Including referenced descriptions * Type hash functions, new getter signatures * Generating hashes in usable form and putting in description function * Pass hash lookup to full_description * Add test for copied sources against installed versions to detect out of date * Generate full type description raw sources Signed-off-by: Emerson Knapp <[email protected]>
1 parent 8b27b67 commit 7cbb116

File tree

60 files changed

+2455
-477
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2455
-477
lines changed

rosidl_cmake/cmake/rosidl_write_generator_arguments.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function(rosidl_write_generator_arguments output_file)
3232
set(OPTIONAL_MULTI_VALUE_KEYWORDS
3333
"ROS_INTERFACE_DEPENDENCIES" # since the dependencies can be empty
3434
"TARGET_DEPENDENCIES"
35-
"TYPE_HASH_TUPLES"
35+
"TYPE_DESCRIPTION_TUPLES"
3636
"INCLUDE_PATHS"
3737
"ADDITIONAL_FILES")
3838

rosidl_generator_c/bin/rosidl_generator_c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ def main(argv=sys.argv[1:]):
2929
'--generator-arguments-file',
3030
required=True,
3131
help='The location of the file containing the generator arguments')
32+
parser.add_argument(
33+
'--disable-description-codegen', action='store_true',
34+
help='If set, disable the generation of static type description '
35+
'code to reduce binary size.')
3236
args = parser.parse_args(argv)
3337

34-
generate_c(args.generator_arguments_file)
38+
generate_c(args.generator_arguments_file, args.disable_description_codegen)
3539

3640

3741
if __name__ == '__main__':

rosidl_generator_c/cmake/rosidl_generator_c_generate_interfaces.cmake

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES})
3333
"${_output_path}/${_parent_folder}/detail/${_header_name}__type_support.h"
3434
)
3535
list(APPEND _generated_sources
36+
"${_output_path}/${_parent_folder}/detail/${_header_name}__description.c"
3637
"${_output_path}/${_parent_folder}/detail/${_header_name}__functions.c"
3738
"${_output_path}/${_parent_folder}/detail/${_header_name}__type_support.c"
3839
)
@@ -54,7 +55,10 @@ set(target_dependencies
5455
${rosidl_generator_c_GENERATOR_FILES}
5556
"${rosidl_generator_c_TEMPLATE_DIR}/action__type_support.h.em"
5657
"${rosidl_generator_c_TEMPLATE_DIR}/action__type_support.c.em"
58+
"${rosidl_generator_c_TEMPLATE_DIR}/empty__description.c.em"
59+
"${rosidl_generator_c_TEMPLATE_DIR}/full__description.c.em"
5760
"${rosidl_generator_c_TEMPLATE_DIR}/idl.h.em"
61+
"${rosidl_generator_c_TEMPLATE_DIR}/idl__description.c.em"
5862
"${rosidl_generator_c_TEMPLATE_DIR}/idl__functions.c.em"
5963
"${rosidl_generator_c_TEMPLATE_DIR}/idl__functions.h.em"
6064
"${rosidl_generator_c_TEMPLATE_DIR}/idl__struct.h.em"
@@ -74,6 +78,7 @@ foreach(dep ${target_dependencies})
7478
endif()
7579
endforeach()
7680

81+
get_target_property(_target_sources ${rosidl_generate_interfaces_TARGET} SOURCES)
7782
set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_c__arguments.json")
7883
rosidl_write_generator_arguments(
7984
"${generator_arguments_file}"
@@ -83,16 +88,23 @@ rosidl_write_generator_arguments(
8388
OUTPUT_DIR "${_output_path}"
8489
TEMPLATE_DIR "${rosidl_generator_c_TEMPLATE_DIR}"
8590
TARGET_DEPENDENCIES ${target_dependencies}
86-
TYPE_HASH_TUPLES "${${rosidl_generate_interfaces_TARGET}__HASH_TUPLES}"
91+
TYPE_DESCRIPTION_TUPLES "${${rosidl_generate_interfaces_TARGET}__DESCRIPTION_TUPLES}"
92+
ROS_INTERFACE_FILES "${_target_sources}"
8793
)
8894

8995
find_package(Python3 REQUIRED COMPONENTS Interpreter)
9096

97+
set(disable_description_codegen_arg)
98+
if(ROSIDL_GENERATOR_C_DISABLE_TYPE_DESCRIPTION_CODEGEN)
99+
set(disable_description_codegen_arg "--disable-description-codegen")
100+
endif()
101+
91102
add_custom_command(
92103
OUTPUT ${_generated_headers} ${_generated_sources}
93104
COMMAND Python3::Interpreter
94105
ARGS ${rosidl_generator_c_BIN}
95106
--generator-arguments-file "${generator_arguments_file}"
107+
${disable_description_codegen_arg}
96108
DEPENDS ${target_dependencies}
97109
COMMENT "Generating C code for ROS interfaces"
98110
VERBATIM
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@# Included from rosidl_generator_c/resource/idl__description.c.em
2+
@{
3+
from rosidl_generator_type_description import GET_DESCRIPTION_FUNC
4+
from rosidl_generator_type_description import GET_INDIVIDUAL_SOURCE_FUNC
5+
from rosidl_generator_type_description import GET_SOURCES_FUNC
6+
7+
def typename_to_c(typename):
8+
return typename.replace('/', '__')
9+
}@
10+
11+
/// Define exported TypeDescriptions and TypeSources
12+
@[for msg, interface_type in [toplevel_type_description] + implicit_type_descriptions]@
13+
@{
14+
td_typename = msg['type_description']['type_name']
15+
td_c_typename = typename_to_c(td_typename)
16+
}@
17+
18+
const rosidl_runtime_c__type_description__TypeDescription *
19+
@(td_c_typename)__@(GET_DESCRIPTION_FUNC)(
20+
const rosidl_@(interface_type)_type_support_t * type_support)
21+
{
22+
(void)type_support;
23+
static const rosidl_runtime_c__type_description__TypeDescription description = {
24+
{
25+
{NULL, 0, 0},
26+
{NULL, 0, 0},
27+
},
28+
{NULL, 0, 0},
29+
};
30+
return &description;
31+
}
32+
33+
const rosidl_runtime_c__type_description__TypeSource *
34+
@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(
35+
const rosidl_@(interface_type)_type_support_t * type_support)
36+
{
37+
(void)type_support;
38+
static const rosidl_runtime_c__type_description__TypeSource source = {
39+
{NULL, 0, 0},
40+
{NULL, 0, 0},
41+
{NULL, 0, 0}
42+
};
43+
return &source;
44+
}
45+
46+
const rosidl_runtime_c__type_description__TypeSource__Sequence *
47+
@(td_c_typename)__@(GET_SOURCES_FUNC)(
48+
const rosidl_@(interface_type)_type_support_t * type_support)
49+
{
50+
(void)type_support;
51+
static const rosidl_runtime_c__type_description__TypeSource__Sequence sources = {NULL, 0, 0};
52+
return &sources;
53+
}
54+
@[end for]@
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
@# Included from rosidl_generator_c/resource/idl__description.c.em
2+
@{
3+
from rosidl_generator_c import escape_string
4+
from rosidl_generator_c import idl_structure_type_to_c_include_prefix
5+
from rosidl_generator_c import type_hash_to_c_definition
6+
from rosidl_parser.definition import NamespacedType
7+
from rosidl_generator_type_description import FIELD_TYPE_ID_TO_NAME
8+
from rosidl_generator_type_description import GET_DESCRIPTION_FUNC
9+
from rosidl_generator_type_description import GET_HASH_FUNC
10+
from rosidl_generator_type_description import GET_INDIVIDUAL_SOURCE_FUNC
11+
from rosidl_generator_type_description import GET_SOURCES_FUNC
12+
13+
def typename_to_c(typename):
14+
return typename.replace('/', '__')
15+
16+
def static_seq_n(varname, n):
17+
"""Statically define a runtime Sequence or String type."""
18+
if n > 0:
19+
return f'{{{varname}, {n}, {n}}}'
20+
return '{NULL, 0, 0}'
21+
22+
def static_seq(varname, values):
23+
"""Statically define a runtime Sequence or String type."""
24+
if values:
25+
return f'{{{varname}, {len(values)}, {len(values)}}}'
26+
return '{NULL, 0, 0}'
27+
28+
def utf8_encode(value_string):
29+
from rosidl_generator_c import escape_string
30+
# Slice removes the b'' from the representation.
31+
return escape_string(repr(value_string.encode('utf-8'))[2:-1])
32+
33+
implicit_type_names = set(td['type_description']['type_name'] for td, _ in implicit_type_descriptions)
34+
includes = set()
35+
toplevel_msg, _ = toplevel_type_description
36+
37+
for referenced_td in toplevel_msg['referenced_type_descriptions']:
38+
if referenced_td['type_name'] in implicit_type_names:
39+
continue
40+
names = referenced_td['type_name'].split('/')
41+
_type = NamespacedType(names[:-1], names[-1])
42+
include_prefix = idl_structure_type_to_c_include_prefix(_type, 'detail')
43+
includes.add(include_prefix + '__functions.h')
44+
45+
full_type_descriptions = [toplevel_type_description] + implicit_type_descriptions
46+
full_type_names = [t['type_description']['type_name'] for t, _ in full_type_descriptions]
47+
all_type_descriptions = [toplevel_msg['type_description']] + toplevel_msg['referenced_type_descriptions']
48+
49+
toplevel_encoding = type_source_file.suffix[1:]
50+
with open(type_source_file, 'r', encoding='utf-8') as f:
51+
raw_source_content = f.read()
52+
}@
53+
@
54+
#include <assert.h>
55+
#include <string.h>
56+
57+
// Include directives for referenced types
58+
@[for header_file in includes]@
59+
#include "@(header_file)"
60+
@[end for]@
61+
62+
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
63+
@# Cache expected hashes for externally referenced types, for error checking
64+
// Hashes for external referenced types
65+
#ifndef NDEBUG
66+
@[for referenced_type_description in toplevel_msg['referenced_type_descriptions']]@
67+
@{
68+
type_name = referenced_type_description['type_name']
69+
c_typename = type_name.replace('/', '__')
70+
}@
71+
@[ if type_name not in full_type_names]@
72+
static const rosidl_type_hash_t @(c_typename)__EXPECTED_HASH = @(type_hash_to_c_definition(hash_lookup[type_name]));
73+
@[ end if]@
74+
@[end for]@
75+
#endif
76+
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
77+
78+
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
79+
@# Names for all types
80+
@[for itype_description in all_type_descriptions]@
81+
static char @(typename_to_c(itype_description['type_name']))__TYPE_NAME[] = "@(itype_description['type_name'])";
82+
@[end for]@
83+
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
84+
85+
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
86+
@# Define all values going into each local type
87+
@
88+
@[for msg, interface_type in full_type_descriptions]@
89+
@{
90+
itype_description = msg['type_description']
91+
td_typename = itype_description['type_name']
92+
td_c_typename = typename_to_c(td_typename)
93+
ref_tds = msg['referenced_type_descriptions']
94+
}@
95+
@
96+
// Define type names, field names, and default values
97+
@[ for field in itype_description['fields']]@
98+
static char @(td_c_typename)__FIELD_NAME__@(field['name'])[] = "@(field['name'])";
99+
@[ if field['default_value']]@
100+
static char @(td_c_typename)__DEFAULT_VALUE__@(field['name'])[] = "@(utf8_encode(field['default_value']))";
101+
@[ end if]@
102+
@[ end for]@
103+
104+
@
105+
@[ if itype_description['fields']]@
106+
static rosidl_runtime_c__type_description__Field @(td_c_typename)__FIELDS[] = {
107+
@[ for field in itype_description['fields']]@
108+
{
109+
@(static_seq(f"{td_c_typename}__FIELD_NAME__{field['name']}", field['name'])),
110+
{
111+
rosidl_runtime_c__type_description__FieldType__@(FIELD_TYPE_ID_TO_NAME[field['type']['type_id']]),
112+
@(field['type']['capacity']),
113+
@(field['type']['string_capacity']),
114+
@(static_seq(f"{typename_to_c(field['type']['nested_type_name'])}__TYPE_NAME", field['type']['nested_type_name'])),
115+
},
116+
@(static_seq(f"{td_c_typename}__DEFAULT_VALUE__{field['name']}", field['default_value'])),
117+
},
118+
@[ end for]@
119+
};
120+
@[ end if]@
121+
@
122+
@[ if ref_tds]@
123+
124+
static rosidl_runtime_c__type_description__IndividualTypeDescription @(td_c_typename)__REFERENCED_TYPE_DESCRIPTIONS[] = {
125+
@[ for ref_td in ref_tds]@
126+
{
127+
@(static_seq(f"{typename_to_c(ref_td['type_name'])}__TYPE_NAME", ref_td['type_name'])),
128+
{NULL, 0, 0},
129+
},
130+
@[ end for]@
131+
};
132+
@[ end if]@
133+
134+
const rosidl_runtime_c__type_description__TypeDescription *
135+
@(td_c_typename)__@(GET_DESCRIPTION_FUNC)(
136+
const rosidl_@(interface_type)_type_support_t * type_support)
137+
{
138+
(void)type_support;
139+
static bool constructed = false;
140+
static const rosidl_runtime_c__type_description__TypeDescription description = {
141+
{
142+
@(static_seq(f'{td_c_typename}__TYPE_NAME', td_typename)),
143+
@(static_seq(f'{td_c_typename}__FIELDS', msg['type_description']['fields'])),
144+
},
145+
@(static_seq(f'{td_c_typename}__REFERENCED_TYPE_DESCRIPTIONS', ref_tds)),
146+
};
147+
if (!constructed) {
148+
@[ for idx, ref_td in enumerate(ref_tds)]@
149+
@{
150+
c_typename = typename_to_c(ref_td['type_name'])
151+
}@
152+
@[ if ref_td['type_name'] not in full_type_names]@
153+
assert(0 == memcmp(&@(c_typename)__EXPECTED_HASH, @(c_typename)__@(GET_HASH_FUNC)(NULL), sizeof(rosidl_type_hash_t)));
154+
@[ end if]@
155+
description.referenced_type_descriptions.data[@(idx)].fields = @(c_typename)__@(GET_DESCRIPTION_FUNC)(NULL)->type_description.fields;
156+
@[ end for]@
157+
constructed = true;
158+
}
159+
return &description;
160+
}
161+
@[end for]@
162+
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
163+
164+
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
165+
@# Define individual raw sources
166+
@[if raw_source_content]@
167+
static char toplevel_type_raw_source[] =@
168+
@[ for line in raw_source_content.splitlines()[:-1]]
169+
"@(utf8_encode(line))\n"@
170+
@[ end for]
171+
"@(utf8_encode(raw_source_content.splitlines()[-1]))";
172+
@[end if]@
173+
174+
static char @(toplevel_encoding)_encoding[] = "@(toplevel_encoding)";
175+
@[if implicit_type_descriptions]@
176+
static char implicit_encoding[] = "implicit";
177+
@[end if]@
178+
179+
// Define all individual source functions
180+
@[for type_description_msg, interface_type in full_type_descriptions]@
181+
@{
182+
itype_description = type_description_msg['type_description']
183+
td_typename = itype_description['type_name']
184+
td_c_typename = typename_to_c(td_typename)
185+
if td_typename in implicit_type_names:
186+
encoding = 'implicit'
187+
contents_var = None
188+
contents = None
189+
else:
190+
encoding = toplevel_encoding
191+
contents_var = 'toplevel_type_raw_source'
192+
contents = raw_source_content
193+
}@
194+
195+
const rosidl_runtime_c__type_description__TypeSource *
196+
@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(
197+
const rosidl_@(interface_type)_type_support_t * type_support)
198+
{
199+
(void)type_support;
200+
static const rosidl_runtime_c__type_description__TypeSource source = {
201+
@(static_seq(f'{td_c_typename}__TYPE_NAME', td_typename)),
202+
@(static_seq(f'{encoding}_encoding', encoding)),
203+
@(static_seq(contents_var, contents)),
204+
};
205+
return &source;
206+
}
207+
@[end for]@
208+
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
209+
@
210+
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
211+
@# Define full raw source sequences
212+
@[for type_description_msg, interface_type in full_type_descriptions]@
213+
@{
214+
ref_tds = type_description_msg['referenced_type_descriptions']
215+
num_sources = len(ref_tds) + 1
216+
td_c_typename = typename_to_c(type_description_msg['type_description']['type_name'])
217+
}@
218+
219+
const rosidl_runtime_c__type_description__TypeSource__Sequence *
220+
@(td_c_typename)__@(GET_SOURCES_FUNC)(
221+
const rosidl_@(interface_type)_type_support_t * type_support)
222+
{
223+
(void)type_support;
224+
static rosidl_runtime_c__type_description__TypeSource sources[@(num_sources)];
225+
static const rosidl_runtime_c__type_description__TypeSource__Sequence source_sequence = @(static_seq_n('sources', num_sources));
226+
static bool constructed = false;
227+
if (!constructed) {
228+
sources[0] = *@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(NULL),
229+
@[ for idx, ref_td in enumerate(ref_tds)]@
230+
sources[@(idx + 1)] = *@(typename_to_c(ref_td['type_name']))__@(GET_INDIVIDUAL_SOURCE_FUNC)(NULL);
231+
@[ end for]@
232+
constructed = true;
233+
}
234+
return &source_sequence;
235+
}
236+
@[end for]@
237+
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

0 commit comments

Comments
 (0)