Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type Description Nested Support #735

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
cc34042
Generate code for and expose interface type descriptions
emersonknapp Feb 23, 2023
e5f96bb
Clean up the subinterfaces stuff, self contain in a single function
emersonknapp Mar 23, 2023
cd85ad1
WIP dang the subinterface list was propping up my C attempt
emersonknapp Mar 23, 2023
5713cca
New approach with function ptr
emersonknapp Mar 24, 2023
6359213
Placeholder nullptrs for get_type_description
emersonknapp Mar 24, 2023
915016b
Use the C definitions of type descriptions instead of generating cpp
emersonknapp Mar 24, 2023
778f09d
Full cleanup pass for using C description only
emersonknapp Mar 24, 2023
b7ecdd3
Add back the field to service and action typesupport
emersonknapp Mar 24, 2023
e777f8d
Add descriptions to srv support
emersonknapp Mar 24, 2023
c6e9f29
Fix tests, linters
emersonknapp Mar 25, 2023
1d411a7
Add empty raw sources
emersonknapp Mar 28, 2023
b32ff87
Add a few more description codegen smoke tests
emersonknapp Mar 29, 2023
31844f3
Add generator c package.xml dependencies
emersonknapp Mar 29, 2023
8aef6c7
Testing out windows consts
emersonknapp Mar 29, 2023
fe07260
Export symbols
emersonknapp Mar 29, 2023
25aea88
Type description functions, not globals
emersonknapp Mar 29, 2023
937fa4b
Address review comments
emersonknapp Mar 30, 2023
a9f7d0c
Encode w strings as utf 8
emersonknapp Mar 30, 2023
555d1a4
Add ability to disable C description codegen
emersonknapp Mar 30, 2023
e6fd185
WIP include referenced type descriptions
emersonknapp Mar 31, 2023
faa114a
Including referenced descriptions
emersonknapp Apr 2, 2023
d0d7ffa
Type hash functions, new getter signatures
emersonknapp Apr 2, 2023
96f373b
Generating hashes in usable form and putting in description function
emersonknapp Apr 2, 2023
cd15879
Pass hash lookup to full_description
emersonknapp Apr 2, 2023
9cb608f
Type description generator refactor complete
emersonknapp Apr 3, 2023
2de7c69
Check against expected type hashes
emersonknapp Apr 3, 2023
74cb484
Add test for copied sources against installed versions to detect out …
emersonknapp Apr 3, 2023
3405deb
Generate full type description raw sources
emersonknapp Apr 3, 2023
a6b610b
Add names to variables in function signatures
emersonknapp Apr 4, 2023
feb0503
Update runtime_c sources
emersonknapp Apr 4, 2023
69fa2b5
Generated code aesthetic cleanup
emersonknapp Apr 4, 2023
133755e
Utf8 encode wstrings
emersonknapp Apr 4, 2023
6fb6d6e
hide expected hash definitions behind debug flag to fix warning
emersonknapp Apr 4, 2023
97bacbb
Utf8 encoding on file read also
emersonknapp Apr 4, 2023
3352ecd
Fix empty build
emersonknapp Apr 4, 2023
5cbbedc
Update copyright notice year
emersonknapp Apr 4, 2023
f581176
Copy latest ndebug generated source bits
emersonknapp Apr 4, 2023
9e8f2a1
Fix raw sources index off-by-one error and add regression test
emersonknapp Apr 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function(rosidl_write_generator_arguments output_file)
set(OPTIONAL_MULTI_VALUE_KEYWORDS
"ROS_INTERFACE_DEPENDENCIES" # since the dependencies can be empty
"TARGET_DEPENDENCIES"
"TYPE_HASH_TUPLES"
"TYPE_DESCRIPTION_TUPLES"
"INCLUDE_PATHS"
"ADDITIONAL_FILES")

Expand Down
6 changes: 5 additions & 1 deletion rosidl_generator_c/bin/rosidl_generator_c
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ def main(argv=sys.argv[1:]):
'--generator-arguments-file',
required=True,
help='The location of the file containing the generator arguments')
parser.add_argument(
'--disable-description-codegen', action='store_true',
help='If set, disable the generation of static type description '
'code to reduce binary size.')
args = parser.parse_args(argv)

generate_c(args.generator_arguments_file)
generate_c(args.generator_arguments_file, args.disable_description_codegen)


if __name__ == '__main__':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES})
"${_output_path}/${_parent_folder}/detail/${_header_name}__type_support.h"
)
list(APPEND _generated_sources
"${_output_path}/${_parent_folder}/detail/${_header_name}__description.c"
"${_output_path}/${_parent_folder}/detail/${_header_name}__functions.c"
"${_output_path}/${_parent_folder}/detail/${_header_name}__type_support.c"
)
Expand All @@ -54,7 +55,10 @@ set(target_dependencies
${rosidl_generator_c_GENERATOR_FILES}
"${rosidl_generator_c_TEMPLATE_DIR}/action__type_support.h.em"
"${rosidl_generator_c_TEMPLATE_DIR}/action__type_support.c.em"
"${rosidl_generator_c_TEMPLATE_DIR}/empty__description.c.em"
"${rosidl_generator_c_TEMPLATE_DIR}/full__description.c.em"
"${rosidl_generator_c_TEMPLATE_DIR}/idl.h.em"
"${rosidl_generator_c_TEMPLATE_DIR}/idl__description.c.em"
"${rosidl_generator_c_TEMPLATE_DIR}/idl__functions.c.em"
"${rosidl_generator_c_TEMPLATE_DIR}/idl__functions.h.em"
"${rosidl_generator_c_TEMPLATE_DIR}/idl__struct.h.em"
Expand All @@ -74,6 +78,7 @@ foreach(dep ${target_dependencies})
endif()
endforeach()

get_target_property(_target_sources ${rosidl_generate_interfaces_TARGET} SOURCES)
set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_c__arguments.json")
rosidl_write_generator_arguments(
"${generator_arguments_file}"
Expand All @@ -83,16 +88,23 @@ rosidl_write_generator_arguments(
OUTPUT_DIR "${_output_path}"
TEMPLATE_DIR "${rosidl_generator_c_TEMPLATE_DIR}"
TARGET_DEPENDENCIES ${target_dependencies}
TYPE_HASH_TUPLES "${${rosidl_generate_interfaces_TARGET}__HASH_TUPLES}"
TYPE_DESCRIPTION_TUPLES "${${rosidl_generate_interfaces_TARGET}__DESCRIPTION_TUPLES}"
ROS_INTERFACE_FILES "${_target_sources}"
)

find_package(Python3 REQUIRED COMPONENTS Interpreter)

set(disable_description_codegen_arg)
if(ROSIDL_GENERATOR_C_DISABLE_TYPE_DESCRIPTION_CODEGEN)
set(disable_description_codegen_arg "--disable-description-codegen")
endif()

add_custom_command(
OUTPUT ${_generated_headers} ${_generated_sources}
COMMAND Python3::Interpreter
ARGS ${rosidl_generator_c_BIN}
--generator-arguments-file "${generator_arguments_file}"
${disable_description_codegen_arg}
DEPENDS ${target_dependencies}
COMMENT "Generating C code for ROS interfaces"
VERBATIM
Expand Down
54 changes: 54 additions & 0 deletions rosidl_generator_c/resource/empty__description.c.em
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@# Included from rosidl_generator_c/resource/idl__description.c.em
@{
from rosidl_generator_type_description import GET_DESCRIPTION_FUNC
from rosidl_generator_type_description import GET_INDIVIDUAL_SOURCE_FUNC
from rosidl_generator_type_description import GET_SOURCES_FUNC

def typename_to_c(typename):
return typename.replace('/', '__')
}@

/// Define exported TypeDescriptions and TypeSources
@[for msg, interface_type in [toplevel_type_description] + implicit_type_descriptions]@
@{
td_typename = msg['type_description']['type_name']
td_c_typename = typename_to_c(td_typename)
}@

const rosidl_runtime_c__type_description__TypeDescription *
@(td_c_typename)__@(GET_DESCRIPTION_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static const rosidl_runtime_c__type_description__TypeDescription description = {
{
{NULL, 0, 0},
{NULL, 0, 0},
},
{NULL, 0, 0},
};
return &description;
}

const rosidl_runtime_c__type_description__TypeSource *
@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static const rosidl_runtime_c__type_description__TypeSource source = {
{NULL, 0, 0},
{NULL, 0, 0},
{NULL, 0, 0}
};
return &source;
}

const rosidl_runtime_c__type_description__TypeSource__Sequence *
@(td_c_typename)__@(GET_SOURCES_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static const rosidl_runtime_c__type_description__TypeSource__Sequence sources = {NULL, 0, 0};
return &sources;
}
@[end for]@
237 changes: 237 additions & 0 deletions rosidl_generator_c/resource/full__description.c.em
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
@# Included from rosidl_generator_c/resource/idl__description.c.em
@{
from rosidl_generator_c import escape_string
from rosidl_generator_c import idl_structure_type_to_c_include_prefix
from rosidl_generator_c import type_hash_to_c_definition
from rosidl_parser.definition import NamespacedType
from rosidl_generator_type_description import FIELD_TYPE_ID_TO_NAME
from rosidl_generator_type_description import GET_DESCRIPTION_FUNC
from rosidl_generator_type_description import GET_HASH_FUNC
from rosidl_generator_type_description import GET_INDIVIDUAL_SOURCE_FUNC
from rosidl_generator_type_description import GET_SOURCES_FUNC

def typename_to_c(typename):
return typename.replace('/', '__')

def static_seq_n(varname, n):
"""Statically define a runtime Sequence or String type."""
if n > 0:
return f'{{{varname}, {n}, {n}}}'
return '{NULL, 0, 0}'

def static_seq(varname, values):
"""Statically define a runtime Sequence or String type."""
if values:
return f'{{{varname}, {len(values)}, {len(values)}}}'
return '{NULL, 0, 0}'

def utf8_encode(value_string):
from rosidl_generator_c import escape_string
# Slice removes the b'' from the representation.
return escape_string(repr(value_string.encode('utf-8'))[2:-1])

implicit_type_names = set(td['type_description']['type_name'] for td, _ in implicit_type_descriptions)
includes = set()
toplevel_msg, _ = toplevel_type_description

for referenced_td in toplevel_msg['referenced_type_descriptions']:
if referenced_td['type_name'] in implicit_type_names:
continue
names = referenced_td['type_name'].split('/')
_type = NamespacedType(names[:-1], names[-1])
include_prefix = idl_structure_type_to_c_include_prefix(_type, 'detail')
includes.add(include_prefix + '__functions.h')

full_type_descriptions = [toplevel_type_description] + implicit_type_descriptions
full_type_names = [t['type_description']['type_name'] for t, _ in full_type_descriptions]
all_type_descriptions = [toplevel_msg['type_description']] + toplevel_msg['referenced_type_descriptions']

toplevel_encoding = type_source_file.suffix[1:]
with open(type_source_file, 'r', encoding='utf-8') as f:
raw_source_content = f.read()
}@
@
#include <assert.h>
#include <string.h>

// Include directives for referenced types
@[for header_file in includes]@
#include "@(header_file)"
@[end for]@

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Cache expected hashes for externally referenced types, for error checking
// Hashes for external referenced types
#ifndef NDEBUG
@[for referenced_type_description in toplevel_msg['referenced_type_descriptions']]@
@{
type_name = referenced_type_description['type_name']
c_typename = type_name.replace('/', '__')
}@
@[ if type_name not in full_type_names]@
static const rosidl_type_hash_t @(c_typename)__EXPECTED_HASH = @(type_hash_to_c_definition(hash_lookup[type_name]));
@[ end if]@
@[end for]@
#endif
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Names for all types
@[for itype_description in all_type_descriptions]@
static char @(typename_to_c(itype_description['type_name']))__TYPE_NAME[] = "@(itype_description['type_name'])";
@[end for]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Define all values going into each local type
@
@[for msg, interface_type in full_type_descriptions]@
@{
itype_description = msg['type_description']
td_typename = itype_description['type_name']
td_c_typename = typename_to_c(td_typename)
ref_tds = msg['referenced_type_descriptions']
}@
@
// Define type names, field names, and default values
@[ for field in itype_description['fields']]@
static char @(td_c_typename)__FIELD_NAME__@(field['name'])[] = "@(field['name'])";
@[ if field['default_value']]@
static char @(td_c_typename)__DEFAULT_VALUE__@(field['name'])[] = "@(utf8_encode(field['default_value']))";
@[ end if]@
@[ end for]@

@
@[ if itype_description['fields']]@
static rosidl_runtime_c__type_description__Field @(td_c_typename)__FIELDS[] = {
@[ for field in itype_description['fields']]@
{
@(static_seq(f"{td_c_typename}__FIELD_NAME__{field['name']}", field['name'])),
{
rosidl_runtime_c__type_description__FieldType__@(FIELD_TYPE_ID_TO_NAME[field['type']['type_id']]),
@(field['type']['capacity']),
@(field['type']['string_capacity']),
@(static_seq(f"{typename_to_c(field['type']['nested_type_name'])}__TYPE_NAME", field['type']['nested_type_name'])),
},
@(static_seq(f"{td_c_typename}__DEFAULT_VALUE__{field['name']}", field['default_value'])),
},
@[ end for]@
};
@[ end if]@
@
@[ if ref_tds]@

static rosidl_runtime_c__type_description__IndividualTypeDescription @(td_c_typename)__REFERENCED_TYPE_DESCRIPTIONS[] = {
@[ for ref_td in ref_tds]@
{
@(static_seq(f"{typename_to_c(ref_td['type_name'])}__TYPE_NAME", ref_td['type_name'])),
{NULL, 0, 0},
},
@[ end for]@
};
@[ end if]@

const rosidl_runtime_c__type_description__TypeDescription *
@(td_c_typename)__@(GET_DESCRIPTION_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static bool constructed = false;
static const rosidl_runtime_c__type_description__TypeDescription description = {
{
@(static_seq(f'{td_c_typename}__TYPE_NAME', td_typename)),
@(static_seq(f'{td_c_typename}__FIELDS', msg['type_description']['fields'])),
},
@(static_seq(f'{td_c_typename}__REFERENCED_TYPE_DESCRIPTIONS', ref_tds)),
};
if (!constructed) {
@[ for idx, ref_td in enumerate(ref_tds)]@
@{
c_typename = typename_to_c(ref_td['type_name'])
}@
@[ if ref_td['type_name'] not in full_type_names]@
assert(0 == memcmp(&@(c_typename)__EXPECTED_HASH, @(c_typename)__@(GET_HASH_FUNC)(NULL), sizeof(rosidl_type_hash_t)));
@[ end if]@
description.referenced_type_descriptions.data[@(idx)].fields = @(c_typename)__@(GET_DESCRIPTION_FUNC)(NULL)->type_description.fields;
@[ end for]@
constructed = true;
}
return &description;
}
@[end for]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Define individual raw sources
@[if raw_source_content]@
static char toplevel_type_raw_source[] =@
@[ for line in raw_source_content.splitlines()[:-1]]
"@(utf8_encode(line))\n"@
@[ end for]
"@(utf8_encode(raw_source_content.splitlines()[-1]))";
@[end if]@

static char @(toplevel_encoding)_encoding[] = "@(toplevel_encoding)";
@[if implicit_type_descriptions]@
static char implicit_encoding[] = "implicit";
@[end if]@

// Define all individual source functions
@[for type_description_msg, interface_type in full_type_descriptions]@
@{
itype_description = type_description_msg['type_description']
td_typename = itype_description['type_name']
td_c_typename = typename_to_c(td_typename)
if td_typename in implicit_type_names:
encoding = 'implicit'
contents_var = None
contents = None
else:
encoding = toplevel_encoding
contents_var = 'toplevel_type_raw_source'
contents = raw_source_content
}@

const rosidl_runtime_c__type_description__TypeSource *
@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static const rosidl_runtime_c__type_description__TypeSource source = {
@(static_seq(f'{td_c_typename}__TYPE_NAME', td_typename)),
@(static_seq(f'{encoding}_encoding', encoding)),
@(static_seq(contents_var, contents)),
};
return &source;
}
@[end for]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@
@#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@# Define full raw source sequences
@[for type_description_msg, interface_type in full_type_descriptions]@
@{
ref_tds = type_description_msg['referenced_type_descriptions']
num_sources = len(ref_tds) + 1
td_c_typename = typename_to_c(type_description_msg['type_description']['type_name'])
}@

const rosidl_runtime_c__type_description__TypeSource__Sequence *
@(td_c_typename)__@(GET_SOURCES_FUNC)(
const rosidl_@(interface_type)_type_support_t * type_support)
{
(void)type_support;
static rosidl_runtime_c__type_description__TypeSource sources[@(num_sources)];
static const rosidl_runtime_c__type_description__TypeSource__Sequence source_sequence = @(static_seq_n('sources', num_sources));
static bool constructed = false;
if (!constructed) {
sources[0] = *@(td_c_typename)__@(GET_INDIVIDUAL_SOURCE_FUNC)(NULL),
@[ for idx, ref_td in enumerate(ref_tds)]@
sources[@(idx + 1)] = *@(typename_to_c(ref_td['type_name']))__@(GET_INDIVIDUAL_SOURCE_FUNC)(NULL);
@[ end for]@
constructed = true;
}
return &source_sequence;
}
@[end for]@
@#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Loading