Make WASM codegen output to be alphabetically ordered.

PiperOrigin-RevId: 836680575
Change-Id: I7ddb7267c0db4911b2e84528f948dee6fccdf81d
This commit is contained in:
Google DeepMind
2025-11-25 08:28:32 -08:00
committed by Copybara-Service
parent 6208730744
commit 6e59f61c70
6 changed files with 4069 additions and 4060 deletions
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -37,7 +37,9 @@ class BindingBuilder:
def set_enums(self): def set_enums(self):
"""Generates and sets the enum bindings.""" """Generates and sets the enum bindings."""
self.markers_and_content += enums.generate(introspect_enums.ENUMS) self.markers_and_content += enums.generate(
list(introspect_enums.ENUMS.values())
)
return self return self
def set_structs(self): def set_structs(self):
@@ -48,10 +50,10 @@ class BindingBuilder:
def set_functions(self): def set_functions(self):
"""Generates and sets the function wrappers and bindings.""" """Generates and sets the function wrappers and bindings."""
functions_to_bind: dict[str, ast_nodes.FunctionDecl] = {} functions_to_bind = []
for name, func in introspect_functions.FUNCTIONS.items(): for name, func in introspect_functions.FUNCTIONS.items():
if not functions.is_excluded_function_name(name): if not functions.is_excluded_function_name(name):
functions_to_bind[name] = func functions_to_bind.append(func)
self.markers_and_content += functions.generate(functions_to_bind) self.markers_and_content += functions.generate(functions_to_bind)
return self return self
+3 -3
View File
@@ -14,7 +14,7 @@
"""Generates Embind bindings for MuJoCo enums.""" """Generates Embind bindings for MuJoCo enums."""
from typing import Mapping from typing import List
from introspect import ast_nodes from introspect import ast_nodes
@@ -22,13 +22,13 @@ from wasm.codegen.generators import code_builder
def generate( def generate(
enums: Mapping[str, ast_nodes.EnumDecl], enums: List[ast_nodes.EnumDecl],
) -> list[tuple[str, list[str]]]: ) -> list[tuple[str, list[str]]]:
"""Generates all Embind code for the provided enums.""" """Generates all Embind code for the provided enums."""
builder = code_builder.CodeBuilder() builder = code_builder.CodeBuilder()
with builder.block('EMSCRIPTEN_BINDINGS(mujoco_enums)'): with builder.block('EMSCRIPTEN_BINDINGS(mujoco_enums)'):
for e in enums.values(): for e in sorted(enums, key=lambda e: e.name):
if e.values: # Skip empty enums. if e.values: # Skip empty enums.
with builder.block(f'enum_<{e.name}>("{e.name}")', braces=False): with builder.block(f'enum_<{e.name}>("{e.name}")', braces=False):
names = list(e.values.keys()) names = list(e.values.keys())
+4 -4
View File
@@ -14,7 +14,7 @@
"""Helper functions for processing and generating bindings for MuJoCo functions.""" """Helper functions for processing and generating bindings for MuJoCo functions."""
from typing import Mapping, Tuple, cast from typing import List, Tuple, cast
from introspect import ast_nodes from introspect import ast_nodes
@@ -289,18 +289,18 @@ def is_excluded_function_name(func_name: str) -> bool:
def generate( def generate(
functions: Mapping[str, ast_nodes.FunctionDecl], functions: List[ast_nodes.FunctionDecl],
) -> list[tuple[str, list[str]]]: ) -> list[tuple[str, list[str]]]:
"""Generates Embind bindings for MuJoCo functions.""" """Generates Embind bindings for MuJoCo functions."""
wrapper_functions = [] wrapper_functions = []
for func in functions.values(): for func in sorted(functions, key=lambda f: f.name):
if should_be_wrapped(func): if should_be_wrapped(func):
if func.name not in constants.MANUAL_WRAPPER_FUNCTIONS: if func.name not in constants.MANUAL_WRAPPER_FUNCTIONS:
wrapper_functions.append(generate_function_wrapper(func)) wrapper_functions.append(generate_function_wrapper(func))
wrapper_content = "\n\n".join(wrapper_functions) wrapper_content = "\n\n".join(wrapper_functions)
function_bindings = [] function_bindings = []
for func in functions.values(): for func in sorted(functions, key=lambda f: f.name):
suffix = "_wrapper" if should_be_wrapped(func) else "" suffix = "_wrapper" if should_be_wrapped(func) else ""
function_bindings.append(f'function("{func.name}", &{func.name}{suffix});') function_bindings.append(f'function("{func.name}", &{func.name}{suffix});')
bindings_content = "\n".join(function_bindings) bindings_content = "\n".join(function_bindings)
+17 -10
View File
@@ -536,6 +536,7 @@ def _build_struct_bindings(
if shallow_copy and not is_mjs: if shallow_copy and not is_mjs:
builder.line(f'.function("copy", &{w}::copy, take_ownership())') builder.line(f'.function("copy", &{w}::copy, take_ownership())')
wrapped_fields.sort(key=lambda field: field.binding)
for field in wrapped_fields[:-1]: for field in wrapped_fields[:-1]:
if field.binding: if field.binding:
builder.line(field.binding) builder.line(field.binding)
@@ -726,9 +727,7 @@ def generate(struct_to_bind: List[str]) -> list[tuple[str, list[str]]]:
# wrapper/bindings statements set up for each struct # wrapper/bindings statements set up for each struct
structs_to_bind_data = generate_wasm_bindings(struct_to_bind) structs_to_bind_data = generate_wasm_bindings(struct_to_bind)
autogenned_struct_definitions = []
markers_and_content = [] markers_and_content = []
typedefs = [] typedefs = []
for type_name in sorted(constants.ANONYMOUS_STRUCTS): for type_name in sorted(constants.ANONYMOUS_STRUCTS):
s = constants.ANONYMOUS_STRUCTS[type_name] s = constants.ANONYMOUS_STRUCTS[type_name]
@@ -741,19 +740,23 @@ def generate(struct_to_bind: List[str]) -> list[tuple[str, list[str]]]:
)) ))
# Sort by struct name by dependency to ensure deterministic output order # Sort by struct name by dependency to ensure deterministic output order
sorted_struct_names = sort_structs_by_dependency(structs_to_bind_data) dependency_sorted_struct_names = sort_structs_by_dependency(
structs_to_bind_data
)
for struct_name in sorted_struct_names: autogenned_struct_definitions = []
for struct_name in dependency_sorted_struct_names:
struct_data = structs_to_bind_data[struct_name] struct_data = structs_to_bind_data[struct_name]
if struct_data.wrapped_header: if struct_data.wrapped_header:
autogenned_struct_definitions.append(struct_data.wrapped_header + "\n") autogenned_struct_definitions.append(struct_data.wrapped_header + "\n")
else: else:
definitions = []
for f in sorted(struct_data.wrapped_fields, key=lambda f: f.definition):
if f.definition:
definitions.append(f.definition)
markers_and_content.append(( markers_and_content.append((
f"// INSERT-GENERATED-{struct_data.wrap_name}-DEFINITIONS", f"// INSERT-GENERATED-{struct_data.wrap_name}-DEFINITIONS",
[ definitions,
l.definition if l.definition else ""
for l in struct_data.wrapped_fields
],
)) ))
markers_and_content.append(( markers_and_content.append((
"// {{ AUTOGENNED_STRUCTS_HEADER }}", "// {{ AUTOGENNED_STRUCTS_HEADER }}",
@@ -761,11 +764,15 @@ def generate(struct_to_bind: List[str]) -> list[tuple[str, list[str]]]:
)) ))
autogenned_struct_source = [] autogenned_struct_source = []
autogenned_struct_bindings = [] for struct_name in dependency_sorted_struct_names:
for struct_name in sorted_struct_names:
struct_data = structs_to_bind_data[struct_name] struct_data = structs_to_bind_data[struct_name]
if struct_data.wrapped_source: if struct_data.wrapped_source:
autogenned_struct_source.append(struct_data.wrapped_source + "\n") autogenned_struct_source.append(struct_data.wrapped_source + "\n")
autogenned_struct_bindings = []
alphabetically_sorted_struct_names = sorted(structs_to_bind_data.keys())
for struct_name in alphabetically_sorted_struct_names:
struct_data = structs_to_bind_data[struct_name]
autogenned_struct_bindings.append(struct_data.bindings) autogenned_struct_bindings.append(struct_data.bindings)
markers_and_content.append(( markers_and_content.append((
+9 -9
View File
@@ -802,34 +802,34 @@ class EnumsGeneratorTest(absltest.TestCase):
expected_code = """ expected_code = """
EMSCRIPTEN_BINDINGS(mujoco_enums) { EMSCRIPTEN_BINDINGS(mujoco_enums) {
enum_<AnotherEnum>("AnotherEnum")
.value("ALPHA", ALPHA)
.value("BETA", BETA);
enum_<TestEnum>("TestEnum") enum_<TestEnum>("TestEnum")
.value("FIRST_VAL", FIRST_VAL) .value("FIRST_VAL", FIRST_VAL)
.value("SECOND_VAL", SECOND_VAL) .value("SECOND_VAL", SECOND_VAL)
.value("THIRD_VAL", THIRD_VAL); .value("THIRD_VAL", THIRD_VAL);
enum_<AnotherEnum>("AnotherEnum")
.value("ALPHA", ALPHA)
.value("BETA", BETA);
}""".strip() }""".strip()
markers_and_content = enums.generate({ markers_and_content = enums.generate([
"TestEnum": ast_nodes.EnumDecl( ast_nodes.EnumDecl(
name="TestEnum", name="TestEnum",
declname="enum TestEnum_", declname="enum TestEnum_",
values={"FIRST_VAL": 0, "SECOND_VAL": 1, "THIRD_VAL": 2}, values={"FIRST_VAL": 0, "SECOND_VAL": 1, "THIRD_VAL": 2},
), ),
"AnotherEnum": ast_nodes.EnumDecl( ast_nodes.EnumDecl(
name="AnotherEnum", name="AnotherEnum",
declname="enum AnotherEnum_", declname="enum AnotherEnum_",
values={"ALPHA": 100, "BETA": 200}, values={"ALPHA": 100, "BETA": 200},
), ),
"EmptyEnum": ast_nodes.EnumDecl( ast_nodes.EnumDecl(
name="EmptyEnum", name="EmptyEnum",
declname="enum EmptyEnum_", declname="enum EmptyEnum_",
values={}, values={},
), ),
}) ])
actual_code = "\n\n".join(markers_and_content[0][1]) actual_code = "\n\n".join(markers_and_content[0][1])
self.assertEqual(actual_code, expected_code) self.assertEqual(actual_code, expected_code)