Skip to content

Commit

Permalink
Deterministic iteration order for reproducible codegen (#846)
Browse files Browse the repository at this point in the history
Python set's do not have a deterministic iteration order (unlike dicts),
therefore replace usage of set with OrderedDict's for the `includes`
variable in `full__description.c.em` to make codegen reproducible.

The `include_directives` set is used by the nested templates to
prevent generation of duplicate include directives, but is not iterated
over by those nested templates.

In `idl__type_support.c.em` rather that iterating over the
`include_directives` set (which introduces non-determinism) to generate
the includes for top level header files, we iterate over those top level
includes as a list and seperately construct the `include_directives` set
from the list. This ensures deterministic codegen.

Signed-off-by: Harry Sarson <harry.sarson@codethink.co.uk>
  • Loading branch information
harrysarson authored Dec 10, 2024
1 parent dd05300 commit 95739d5
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 5 deletions.
5 changes: 3 additions & 2 deletions rosidl_generator_c/resource/full__description.c.em
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@# Included from rosidl_generator_c/resource/idl__description.c.em
@{
from collections import OrderedDict
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
Expand Down Expand Up @@ -31,7 +32,7 @@ def utf8_encode(value_string):
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()
includes = OrderedDict()
toplevel_msg, _ = toplevel_type_description

for referenced_td in toplevel_msg['referenced_type_descriptions']:
Expand All @@ -40,7 +41,7 @@ for referenced_td in toplevel_msg['referenced_type_descriptions']:
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')
includes[include_prefix + '__functions.h'] = None

full_type_descriptions = [toplevel_type_description] + implicit_type_descriptions
full_type_names = [t['type_description']['type_name'] for t, _ in full_type_descriptions]
Expand Down
9 changes: 6 additions & 3 deletions rosidl_generator_c/resource/idl__type_support.c.em
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ include_parts = [package_name] + list(interface_path.parents[0].parts) + [
'detail', convert_camel_case_to_lower_case_underscore(interface_path.stem)]
include_base = '/'.join(include_parts)

include_directives = {
top_level_includes = [
'rosidl_typesupport_interface/macros.h',
include_base + '__type_support.h',
include_base + '__struct.h',
include_base + '__functions.h'}
include_base + '__functions.h']

include_directives = set(top_level_includes)

}@

#include <string.h>

@[for header_file in include_directives]@
@[for header_file in top_level_includes]@
#include "@(header_file)"
@[end for]@

Expand Down

0 comments on commit 95739d5

Please sign in to comment.