From 29b44b7c3433319baeb716de1356aae2e28e757e Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Fri, 2 Feb 2018 22:30:12 +0100 Subject: [PATCH 01/32] Initial implementation --- rosidl_generator_rs/CMakeLists.txt | 26 ++ rosidl_generator_rs/bin/rosidl_generator_rs | 40 +++ .../cmake/custom_command.cmake | 45 +++ rosidl_generator_rs/cmake/register_rs.cmake | 30 ++ ...idl_generator_rs_generate_interfaces.cmake | 283 ++++++++++++++++ ...rosidl_generator_rs_get_typesupports.cmake | 30 ++ rosidl_generator_rs/msg/Bool.msg | 1 + rosidl_generator_rs/msg/Byte.msg | 1 + rosidl_generator_rs/msg/Char.msg | 1 + rosidl_generator_rs/msg/Constants.msg | 5 + rosidl_generator_rs/msg/Empty.msg | 1 + rosidl_generator_rs/msg/Float32.msg | 1 + rosidl_generator_rs/msg/Float64.msg | 1 + rosidl_generator_rs/msg/Int16.msg | 1 + rosidl_generator_rs/msg/Int32.msg | 1 + rosidl_generator_rs/msg/Int64.msg | 1 + rosidl_generator_rs/msg/Int8.msg | 1 + rosidl_generator_rs/msg/Nested.msg | 6 + rosidl_generator_rs/msg/Primitives.msg | 18 ++ rosidl_generator_rs/msg/Strings.msg | 4 + rosidl_generator_rs/msg/Uint16.msg | 1 + rosidl_generator_rs/msg/Uint32.msg | 1 + rosidl_generator_rs/msg/Uint64.msg | 1 + rosidl_generator_rs/msg/Uint8.msg | 1 + rosidl_generator_rs/msg/Various.msg | 23 ++ rosidl_generator_rs/package.xml | 47 +++ rosidl_generator_rs/resource/Cargo.toml.in | 7 + rosidl_generator_rs/resource/lib.rs.em | 10 + rosidl_generator_rs/resource/msg.c.em | 76 +++++ rosidl_generator_rs/resource/msg.rs.em | 155 +++++++++ rosidl_generator_rs/resource/srv.c.em | 0 rosidl_generator_rs/resource/srv.rs.em | 0 .../rosidl_generator_rs-extras.cmake.in | 7 + .../rosidl_generator_rs/__init__.py | 304 ++++++++++++++++++ 34 files changed, 1130 insertions(+) create mode 100644 rosidl_generator_rs/CMakeLists.txt create mode 100755 rosidl_generator_rs/bin/rosidl_generator_rs create mode 100644 rosidl_generator_rs/cmake/custom_command.cmake create mode 100644 rosidl_generator_rs/cmake/register_rs.cmake create mode 100644 rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake create mode 100644 rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake create mode 100644 rosidl_generator_rs/msg/Bool.msg create mode 100644 rosidl_generator_rs/msg/Byte.msg create mode 100644 rosidl_generator_rs/msg/Char.msg create mode 100644 rosidl_generator_rs/msg/Constants.msg create mode 100644 rosidl_generator_rs/msg/Empty.msg create mode 100644 rosidl_generator_rs/msg/Float32.msg create mode 100644 rosidl_generator_rs/msg/Float64.msg create mode 100644 rosidl_generator_rs/msg/Int16.msg create mode 100644 rosidl_generator_rs/msg/Int32.msg create mode 100644 rosidl_generator_rs/msg/Int64.msg create mode 100644 rosidl_generator_rs/msg/Int8.msg create mode 100644 rosidl_generator_rs/msg/Nested.msg create mode 100644 rosidl_generator_rs/msg/Primitives.msg create mode 100644 rosidl_generator_rs/msg/Strings.msg create mode 100644 rosidl_generator_rs/msg/Uint16.msg create mode 100644 rosidl_generator_rs/msg/Uint32.msg create mode 100644 rosidl_generator_rs/msg/Uint64.msg create mode 100644 rosidl_generator_rs/msg/Uint8.msg create mode 100644 rosidl_generator_rs/msg/Various.msg create mode 100644 rosidl_generator_rs/package.xml create mode 100644 rosidl_generator_rs/resource/Cargo.toml.in create mode 100644 rosidl_generator_rs/resource/lib.rs.em create mode 100644 rosidl_generator_rs/resource/msg.c.em create mode 100644 rosidl_generator_rs/resource/msg.rs.em create mode 100644 rosidl_generator_rs/resource/srv.c.em create mode 100644 rosidl_generator_rs/resource/srv.rs.em create mode 100644 rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in create mode 100644 rosidl_generator_rs/rosidl_generator_rs/__init__.py diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt new file mode 100644 index 0000000..5bef6c3 --- /dev/null +++ b/rosidl_generator_rs/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) + +project(rosidl_generator_rs) + +find_package(ament_cmake REQUIRED) + +ament_export_dependencies(ament_cmake) +ament_export_dependencies(rosidl_cmake) + +ament_python_install_package(${PROJECT_NAME}) + +install( + PROGRAMS bin/rosidl_generator_rs + DESTINATION lib/rosidl_generator_rs +) + +install( + DIRECTORY cmake resource + DESTINATION share/${PROJECT_NAME} +) + +ament_package( + CONFIG_EXTRAS "cmake/rosidl_generator_rs_get_typesupports.cmake" + "cmake/register_rs.cmake" + "rosidl_generator_rs-extras.cmake.in" +) diff --git a/rosidl_generator_rs/bin/rosidl_generator_rs b/rosidl_generator_rs/bin/rosidl_generator_rs new file mode 100755 index 0000000..2d36a68 --- /dev/null +++ b/rosidl_generator_rs/bin/rosidl_generator_rs @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys + +try: + from rosidl_generator_rs import generate_rs +except ImportError: + # modifying sys.path and importing the Rust package with the same + # name as this script does not work on Windows + rosidl_generator_rs_root = os.path.dirname(os.path.dirname(__file__)) + rosidl_generator_rs_module = os.path.join( + rosidl_generator_rs_root, 'rosidl_generator_rs', '__init__.py') + if not os.path.exists(rosidl_generator_rs_module): + raise + from importlib.machinery import SourceFileLoader + + loader = SourceFileLoader('rosidl_generator_rs', rosidl_generator_rs_module) + rosidl_generator_rs = loader.load_module() + generate_rs = rosidl_generator_rs.generate_rs + + +def main(argv=sys.argv[1:]): + parser = argparse.ArgumentParser(description='Generate the Rust ROS interfaces.') + parser.add_argument( + '--generator-arguments-file', + required=True, + help='The location of the file containing the generator arguments') + parser.add_argument( + '--typesupport-impls', + required=True, + help='All the available typesupport implementations') + args = parser.parse_args(argv) + + return generate_rs(args.generator_arguments_file, args.typesupport_impls) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/rosidl_generator_rs/cmake/custom_command.cmake b/rosidl_generator_rs/cmake/custom_command.cmake new file mode 100644 index 0000000..e405c93 --- /dev/null +++ b/rosidl_generator_rs/cmake/custom_command.cmake @@ -0,0 +1,45 @@ +# Copyright 2017 Esteve Fernandez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +add_custom_command( + OUTPUT + ${_generated_extension_files} + ${_generated_common_rs_files} + ${_generated_msg_rs_files} + ${_generated_msg_c_files} + ${_generated_srv_rs_files} + ${_generated_srv_c_files} + COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_rs_BIN} + --generator-arguments-file "${generator_arguments_file}" + --typesupport-impls "${_typesupport_impls}" + DEPENDS ${target_dependencies} + COMMENT "Generating Rust code for ROS interfaces" + VERBATIM +) + +if(TARGET ${rosidl_generate_interfaces_TARGET}${_target_suffix}) + message(WARNING "Custom target ${rosidl_generate_interfaces_TARGET}${_target_suffix} already exists") +else() + add_custom_target( + ${rosidl_generate_interfaces_TARGET}${_target_suffix} + DEPENDS + ${_generated_extension_files} + ${_generated_common_rs_files} + ${_generated_msg_rs_files} + ${_generated_msg_c_files} + ${_generated_srv_rs_files} + ${_generated_srv_c_files} + ) +endif() diff --git a/rosidl_generator_rs/cmake/register_rs.cmake b/rosidl_generator_rs/cmake/register_rs.cmake new file mode 100644 index 0000000..487cbb7 --- /dev/null +++ b/rosidl_generator_rs/cmake/register_rs.cmake @@ -0,0 +1,30 @@ +# Copyright 2016-2017 Esteve Fernandez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +macro(rosidl_generator_rs_extras BIN GENERATOR_FILES TEMPLATE_DIR) + find_package(ament_cmake_core QUIET REQUIRED) + ament_register_extension( + "rosidl_generate_interfaces" + "rosidl_generator_rs" + "rosidl_generator_rs_generate_interfaces.cmake") + + normalize_path(BIN "${BIN}") + set(rosidl_generator_rs_BIN "${BIN}") + + normalize_path(GENERATOR_FILES "${GENERATOR_FILES}") + set(rosidl_generator_rs_GENERATOR_FILES "${GENERATOR_FILES}") + + normalize_path(TEMPLATE_DIR "${TEMPLATE_DIR}") + set(rosidl_generator_rs_TEMPLATE_DIR "${TEMPLATE_DIR}") +endmacro() diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake new file mode 100644 index 0000000..82541e4 --- /dev/null +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -0,0 +1,283 @@ +# Copyright 2016-2017 Esteve Fernandez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +find_package(ament_cmake_export_crates REQUIRED) +find_package(rosidl_generator_c REQUIRED) +find_package(rmw_implementation_cmake REQUIRED) +find_package(rmw REQUIRED) +find_package(rclrs_common REQUIRED) + +if(NOT WIN32) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error") + endif() +endif() + +# Get a list of typesupport implementations from valid rmw implementations. +rosidl_generator_rs_get_typesupports(_typesupport_impls) + +if(_typesupport_impls STREQUAL "") + message(WARNING "No valid typesupport for Rust generator. Rust messages will not be generated.") + return() +endif() + +set(_output_path + "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs/${PROJECT_NAME}") +set(_generated_extension_files "") +set(_generated_common_rs_files "") +set(_generated_msg_rs_files "") +set(_generated_msg_c_files "") +set(_generated_srv_rs_files "") +set(_generated_srv_c_files "") + +set(_has_msg FALSE) +set(_has_srv FALSE) + +foreach(_typesupport_impl ${_typesupport_impls}) + set(_generated_extension_${_typesupport_impl}_files "") +endforeach() + +foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) + get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_module_name "${_idl_file}" NAME_WE) + + if(_parent_folder STREQUAL "msg") + set(_has_msg TRUE) + elseif(_parent_folder STREQUAL "srv") + set(_has_srv TRUE) + else() + message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") + endif() +endforeach() + +list(APPEND _generated_common_rs_files + "${_output_path}/rust/src/lib.rs" +) + +if(${_has_msg}) + list(APPEND _generated_msg_rs_files + "${_output_path}/rust/src/msg.rs" + ) + + foreach(_typesupport_impl ${_typesupport_impls}) + list_append_unique(_generated_extension_${_typesupport_impl}_files "${_output_path}/msg_rs.ep.${_typesupport_impl}.c") + list_append_unique(_generated_extension_files "${_generated_extension_${_typesupport_impl}_files}") + endforeach() +endif() + +if(${_has_srv}) + list(APPEND _generated_srv_rs_files + "${_output_path}/rust/src/srv.rs" + ) + + foreach(_typesupport_impl ${_typesupport_impls}) + list_append_unique(_generated_extension_${_typesupport_impl}_files "${_output_path}/srv_rs.ep.${_typesupport_impl}.c") + list_append_unique(_generated_extension_files "${_generated_extension_${_typesupport_impl}_files}") + endforeach() +endif() + +set(_dependency_files "") +set(_dependencies "") +foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) + foreach(_idl_file ${${_pkg_name}_INTERFACE_FILES}) + set(_abs_idl_file "${${_pkg_name}_DIR}/../${_idl_file}") + normalize_path(_abs_idl_file "${_abs_idl_file}") + list(APPEND _dependency_files "${_abs_idl_file}") + list(APPEND _dependencies "${_pkg_name}:${_abs_idl_file}") + endforeach() +endforeach() + +set(target_dependencies + "${rosidl_generator_rs_BIN}" + ${rosidl_generator_rs_GENERATOR_FILES} + "${rosidl_generator_rs_TEMPLATE_DIR}/msg.c.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/srv.c.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" + ${rosidl_generate_interfaces_IDL_FILES} + ${_dependency_files}) +foreach(dep ${target_dependencies}) + if(NOT EXISTS "${dep}") + message(FATAL_ERROR "Target dependency '${dep}' does not exist") + endif() +endforeach() + +set(generator_arguments_file "${CMAKE_BINARY_DIR}/rosidl_generator_rs__arguments.json") +rosidl_write_generator_arguments( + "${generator_arguments_file}" + PACKAGE_NAME "${PROJECT_NAME}" + ROS_INTERFACE_FILES "${rosidl_generate_interfaces_IDL_FILES}" + ROS_INTERFACE_DEPENDENCIES "${_dependencies}" + OUTPUT_DIR "${_output_path}" + TEMPLATE_DIR "${rosidl_generator_rs_TEMPLATE_DIR}" + TARGET_DEPENDENCIES ${target_dependencies} +) + +file(MAKE_DIRECTORY "${_output_path}") + +set(_target_suffix "__rs") + +set(_crate_deps "") +set(CRATES_DEPENDENCIES "") +find_package(rclrs_common REQUIRED) +foreach(_crate_dep ${rclrs_common_CRATES}) + list(APPEND _crate_deps "${_crate_dep}") + set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\nrclrs_common = { path = '${rclrs_common_CRATES}' }") +endforeach() + +foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) + find_package(${_pkg_name} REQUIRED) + foreach(_crate_dep ${${_pkg_name}_CRATES}) + list(APPEND _crate_deps "${_crate_dep}") + set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = { path = '${_crate_dep}' }") + endforeach() +endforeach() + + +# needed to avoid multiple calls to the Rust generator trick copied from +# https://github.com/ros2/rosidl/blob/master/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake +set(_subdir "${CMAKE_CURRENT_BINARY_DIR}/${rosidl_generate_interfaces_TARGET}${_target_suffix}") +file(MAKE_DIRECTORY "${_subdir}") +file(READ "${rosidl_generator_rs_DIR}/custom_command.cmake" _custom_command) +file(WRITE "${_subdir}/CMakeLists.txt" "${_custom_command}") +add_subdirectory("${_subdir}" ${rosidl_generate_interfaces_TARGET}${_target_suffix}) + +add_dependencies(${rosidl_generate_interfaces_TARGET} ${rosidl_generate_interfaces_TARGET}${_target_suffix}) + +set_property( + SOURCE + ${_generated_extension_files} + ${_generated_common_rs_files} + ${_generated_msg_rs_files} + ${_generated_msg_c_files} + ${_generated_srv_rs_files} + ${_generated_srv_c_files} + PROPERTY GENERATED 1) + +set(_type_support_by_generated_c_files ${_type_support_by_generated_msg_c_files} ${_type_support_by_generated_srv_c_files}) +set(_generated_c_files ${_generated_msg_c_files} ${_generated_srv_c_files}) +set(_generated_rs_files ${_generated_msg_rs_files} ${_generated_srv_rs_files}) + +set(_rsext_suffix "__rsext") +foreach(_typesupport_impl ${_typesupport_impls}) + find_package(${_typesupport_impl} REQUIRED) + + set(_target_name "${PROJECT_NAME}__${_typesupport_impl}${_rsext_suffix}") + + add_library(${_target_name} SHARED + ${_generated_extension_${_typesupport_impl}_files} + ${_generated_msg_c_files} + ${_generated_srv_c_files} + ) + + add_dependencies( + ${_target_name} + ${rosidl_generate_interfaces_TARGET}${_target_suffix} + ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c + ) + + set(_extension_compile_flags "") + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(_extension_compile_flags -Wall -Wextra) + endif() + target_link_libraries( + ${_target_name} + ${PROJECT_NAME}__${_typesupport_impl} + ) + rosidl_target_interfaces(${_target_name} + ${PROJECT_NAME} rosidl_typesupport_c) + + target_include_directories(${_target_name} + PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_c + ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs + ) + + ament_target_dependencies(${_target_name} + "rosidl_generator_c" + "rosidl_typesupport_c" + "rosidl_typesupport_interface" + ) + foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) + ament_target_dependencies(${_target_name} + ${_pkg_name} + ) + endforeach() + + add_dependencies(${_target_name} + ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} + ) + ament_target_dependencies(${_target_name} + "rosidl_generator_c" + "rosidl_generator_rs" + "${PROJECT_NAME}__rosidl_generator_c" + ) + + if(NOT rosidl_generate_interfaces_SKIP_INSTALL) + install( + DIRECTORY "${_output_path}/rust" + DESTINATION "share/${PROJECT_NAME}" + ) + ament_export_crates("share/${PROJECT_NAME}/rust") + # install("${PROJECT_NAME}/lib.rs" "share/${PROJECT_NAME}/rust/src") + # install("${PROJECT_NAME}/msg.rs" "share/${PROJECT_NAME}/rust/src") + # install("${PROJECT_NAME}/srv.rs" "share/${PROJECT_NAME}/rust/src") + install(TARGETS ${_target_name} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + ) + + configure_file("${rosidl_generator_rs_TEMPLATE_DIR}/Cargo.toml.in" + "share/${PROJECT_NAME}/rust/Cargo.toml" + @ONLY) + + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/rust/Cargo.toml" + DESTINATION share/${PROJECT_NAME}/rust/ + ) + endif() +endforeach() + +if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) + if( + NOT _generated_msg_rs_files STREQUAL "" OR + NOT _generated_msg_c_files STREQUAL "" OR + NOT _generated_srv_rs_files STREQUAL "" OR + NOT _generated_srv_c_files STREQUAL "" + ) + find_package(ament_cmake_cppcheck REQUIRED) + ament_cppcheck( + TESTNAME "cppcheck_rosidl_generated_rs" + "${_output_path}") + + find_package(ament_cmake_cpplint REQUIRED) + get_filename_component(_cpplint_root "${_output_path}" DIRECTORY) + ament_cpplint( + TESTNAME "cpplint_rosidl_generated_rs" + # the generated code might contain longer lines for templated types + MAX_LINE_LENGTH 999 + ROOT "${_cpplint_root}" + "${_output_path}") + + find_package(ament_cmake_uncrustify REQUIRED) + ament_uncrustify( + TESTNAME "uncrustify_rosidl_generated_rs" + # the generated code might contain longer lines for templated types + MAX_LINE_LENGTH 999 + "${_output_path}") + endif() +endif() diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake new file mode 100644 index 0000000..37d3c35 --- /dev/null +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake @@ -0,0 +1,30 @@ +# Copyright 2016-2017 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +macro(accumulate_typesupports) + set(_typesupport_impl_tmp "") + get_rmw_typesupport(_typesupport_impl_tmp ${rmw_implementation} LANGUAGE "C") + list(APPEND _typesupport_impls_tmp ${_typesupport_impl_tmp}) +endmacro() + +macro(rosidl_generator_rs_get_typesupports TYPESUPPORT_IMPLS) + set(${TYPESUPPORT_IMPLS} "") + set(_typesupport_impls_tmp "") + set(_typesupport_impls_tmp_unique "") + call_for_each_rmw_implementation(accumulate_typesupports) + + foreach(_typesupport_impl ${_typesupport_impls_tmp}) + list_append_unique(${TYPESUPPORT_IMPLS} ${_typesupport_impl}) + endforeach() +endmacro() diff --git a/rosidl_generator_rs/msg/Bool.msg b/rosidl_generator_rs/msg/Bool.msg new file mode 100644 index 0000000..b1578ab --- /dev/null +++ b/rosidl_generator_rs/msg/Bool.msg @@ -0,0 +1 @@ +bool empty_bool diff --git a/rosidl_generator_rs/msg/Byte.msg b/rosidl_generator_rs/msg/Byte.msg new file mode 100644 index 0000000..2e6fa04 --- /dev/null +++ b/rosidl_generator_rs/msg/Byte.msg @@ -0,0 +1 @@ +byte empty_byte diff --git a/rosidl_generator_rs/msg/Char.msg b/rosidl_generator_rs/msg/Char.msg new file mode 100644 index 0000000..0f2e0c9 --- /dev/null +++ b/rosidl_generator_rs/msg/Char.msg @@ -0,0 +1 @@ +char empty_char diff --git a/rosidl_generator_rs/msg/Constants.msg b/rosidl_generator_rs/msg/Constants.msg new file mode 100644 index 0000000..d9e404e --- /dev/null +++ b/rosidl_generator_rs/msg/Constants.msg @@ -0,0 +1,5 @@ +int32 X=123 +int32 Y=-123 +string FOO=foo +char TOTO=127 +byte TATA=48 diff --git a/rosidl_generator_rs/msg/Empty.msg b/rosidl_generator_rs/msg/Empty.msg new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/rosidl_generator_rs/msg/Empty.msg @@ -0,0 +1 @@ + diff --git a/rosidl_generator_rs/msg/Float32.msg b/rosidl_generator_rs/msg/Float32.msg new file mode 100644 index 0000000..a8bb0cb --- /dev/null +++ b/rosidl_generator_rs/msg/Float32.msg @@ -0,0 +1 @@ +float32 empty_float32 diff --git a/rosidl_generator_rs/msg/Float64.msg b/rosidl_generator_rs/msg/Float64.msg new file mode 100644 index 0000000..d6efd99 --- /dev/null +++ b/rosidl_generator_rs/msg/Float64.msg @@ -0,0 +1 @@ +float64 empty_float64 diff --git a/rosidl_generator_rs/msg/Int16.msg b/rosidl_generator_rs/msg/Int16.msg new file mode 100644 index 0000000..14426ca --- /dev/null +++ b/rosidl_generator_rs/msg/Int16.msg @@ -0,0 +1 @@ +int16 empty_int16 diff --git a/rosidl_generator_rs/msg/Int32.msg b/rosidl_generator_rs/msg/Int32.msg new file mode 100644 index 0000000..51c88f0 --- /dev/null +++ b/rosidl_generator_rs/msg/Int32.msg @@ -0,0 +1 @@ +int32 empty_int32 diff --git a/rosidl_generator_rs/msg/Int64.msg b/rosidl_generator_rs/msg/Int64.msg new file mode 100644 index 0000000..75a1c23 --- /dev/null +++ b/rosidl_generator_rs/msg/Int64.msg @@ -0,0 +1 @@ +int64 empty_int64 diff --git a/rosidl_generator_rs/msg/Int8.msg b/rosidl_generator_rs/msg/Int8.msg new file mode 100644 index 0000000..76167eb --- /dev/null +++ b/rosidl_generator_rs/msg/Int8.msg @@ -0,0 +1 @@ +int8 empty_int8 diff --git a/rosidl_generator_rs/msg/Nested.msg b/rosidl_generator_rs/msg/Nested.msg new file mode 100644 index 0000000..a7386b5 --- /dev/null +++ b/rosidl_generator_rs/msg/Nested.msg @@ -0,0 +1,6 @@ +uint8 ANSWER=42 + +Primitives primitives +Primitives[2] two_primitives +Primitives[<=3] up_to_three_primitives +Primitives[] unbounded_primitives diff --git a/rosidl_generator_rs/msg/Primitives.msg b/rosidl_generator_rs/msg/Primitives.msg new file mode 100644 index 0000000..ec13f6a --- /dev/null +++ b/rosidl_generator_rs/msg/Primitives.msg @@ -0,0 +1,18 @@ +bool bool_value true +byte byte_value +char char_value +float32 float32_value 1.125 +float64 float64_value +int8 int8_value -5 +uint8 uint8_value 23 +int16 int16_value +uint16 uint16_value +int32 int32_value +uint32 uint32_value +int64 int64_value +uint64 uint64_value +string string_value +string string_value_with_default 'default' +#string<=5[3] fixed_length_string_value +#string<=5[<=10] upper_bound_string_value +string unbound_string_value diff --git a/rosidl_generator_rs/msg/Strings.msg b/rosidl_generator_rs/msg/Strings.msg new file mode 100644 index 0000000..f81e029 --- /dev/null +++ b/rosidl_generator_rs/msg/Strings.msg @@ -0,0 +1,4 @@ +string empty_string +string def_string "Hello world!" +string<=22 ub_string +string<=22 ub_def_string "Upper bounded string." diff --git a/rosidl_generator_rs/msg/Uint16.msg b/rosidl_generator_rs/msg/Uint16.msg new file mode 100644 index 0000000..9dd741c --- /dev/null +++ b/rosidl_generator_rs/msg/Uint16.msg @@ -0,0 +1 @@ +uint16 empty_uint16 diff --git a/rosidl_generator_rs/msg/Uint32.msg b/rosidl_generator_rs/msg/Uint32.msg new file mode 100644 index 0000000..cb65d08 --- /dev/null +++ b/rosidl_generator_rs/msg/Uint32.msg @@ -0,0 +1 @@ +uint32 empty_uint32 diff --git a/rosidl_generator_rs/msg/Uint64.msg b/rosidl_generator_rs/msg/Uint64.msg new file mode 100644 index 0000000..9e7c0f8 --- /dev/null +++ b/rosidl_generator_rs/msg/Uint64.msg @@ -0,0 +1 @@ +uint64 empty_uint64 diff --git a/rosidl_generator_rs/msg/Uint8.msg b/rosidl_generator_rs/msg/Uint8.msg new file mode 100644 index 0000000..6b0876e --- /dev/null +++ b/rosidl_generator_rs/msg/Uint8.msg @@ -0,0 +1 @@ +uint8 empty_uint8 diff --git a/rosidl_generator_rs/msg/Various.msg b/rosidl_generator_rs/msg/Various.msg new file mode 100644 index 0000000..efe284a --- /dev/null +++ b/rosidl_generator_rs/msg/Various.msg @@ -0,0 +1,23 @@ +bool bool_value false +byte byte_value 1 +char char_value 1 +float32 float32_value 1.23 +float64 float64_value +int8 int8_value -5 +uint16[2] two_uint16_value [5, 23] +int32[<=3] up_to_three_int32_values +int32[<=3] up_to_three_int32_values_with_default_values [5, 23] +uint64[] unbounded_uint64_values + +#string[2] two_string_value ['foo', 'bar'] +string[<=3] up_to_three_string_values +string[] unbounded_string_values + +Empty empty +Empty[2] two_empty +Empty[] unbounded_empty + +Nested nested +Nested[2] two_nested +Nested[<=3] up_to_three_nested +Nested[] unbounded_nested diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml new file mode 100644 index 0000000..c465681 --- /dev/null +++ b/rosidl_generator_rs/package.xml @@ -0,0 +1,47 @@ + + + + rosidl_generator_rs + 0.0.3 + Generate the ROS interfaces in Rust. + Esteve Fernandez + Esteve Fernandez + Apache License 2.0 + + ament_cmake + + ament_cmake_export_crates + rclrs_common + + ament_cmake + ament_cmake_export_crates + rosidl_cmake + rclrs_common + rosidl_typesupport_c + rosidl_typesupport_interface + + rmw_implementation + rmw_implementation_cmake + rosidl_generator_c + rosidl_parser + + ament_cmake_gtest + ament_lint_auto + ament_lint_common + + rmw_implementation + rmw_implementation_cmake + rosidl_generator_c + + rosidl_parser + rosidl_cmake + + rosidl_typesupport_c + rosidl_typesupport_connext_c + rosidl_typesupport_introspection_c + rosidl_typesupport_opensplice_c + + + ament_cmake + + diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in new file mode 100644 index 0000000..55f1039 --- /dev/null +++ b/rosidl_generator_rs/resource/Cargo.toml.in @@ -0,0 +1,7 @@ +[package] +name = "@PROJECT_NAME@" +version = "0.1.0" + +[dependencies] +libc = "0.2" +@CRATES_DEPENDENCIES@ \ No newline at end of file diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em new file mode 100644 index 0000000..d129ece --- /dev/null +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -0,0 +1,10 @@ +extern crate rclrs_common; +extern crate libc; + +@[if len(msg_specs) > 0]@ +pub mod msg; +@[end if]@ + +@[if len(srv_specs) > 0]@ +pub mod srv; +@[end if]@ \ No newline at end of file diff --git a/rosidl_generator_rs/resource/msg.c.em b/rosidl_generator_rs/resource/msg.c.em new file mode 100644 index 0000000..583cfa8 --- /dev/null +++ b/rosidl_generator_rs/resource/msg.c.em @@ -0,0 +1,76 @@ +#include "rosidl_generator_c/message_type_support_struct.h" + +#include "rosidl_generator_c/string.h" +#include "rosidl_generator_c/string_functions.h" + +#include "rosidl_generator_c/primitives_array.h" +#include "rosidl_generator_c/primitives_array_functions.h" + +@[for subfolder, msg_spec in msg_specs]@ +@{ +type_name = msg_spec.base_type.type +c_fields = [] +for field in msg_spec.fields: + if field.type.is_array: + pass + else: + if field.type.is_primitive_type(): + c_fields.append("%s %s" % (get_c_type(field.type), field.name)) + else: + pass + +def get_normalized_type(type_, subfolder='msg'): + return get_rs_type(type_, subfolder=subfolder).replace('::', '__') + +msg_normalized_type = get_normalized_type(msg_spec.base_type, subfolder=subfolder) +}@ + +#include "@(msg_spec.base_type.pkg_name)/@(subfolder)/@(convert_camel_case_to_lower_case_underscore(type_name)).h" + +uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() { + return (uintptr_t)ROSIDL_GET_MSG_TYPE_SUPPORT(@(msg_spec.base_type.pkg_name), @(subfolder), @(msg_spec.msg_name)); +} + +uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( + @(', '.join(c_fields))) { + @(msg_normalized_type) * ros_message = @(msg_normalized_type)__create(); +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@[ if field.type.type == 'string']@ + rosidl_generator_c__String__assign(&(ros_message->@(field.name)), @(field.name)); +@[ else]@ + ros_message->@(field.name) = @(field.name); +@[ end if]@ +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ + return (uintptr_t)ros_message; +} + +void @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(void * raw_ros_message) { + @(msg_normalized_type) * ros_message = raw_ros_message; + @(msg_normalized_type)__destroy(ros_message); +} + +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@(get_c_type(field.type)) @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(uintptr_t message_handle) { +@[ if field.type.type == 'string']@ + @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; + return ros_message->@(field.name).data; +@[ else]@ + @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; + return ros_message->@(field.name); +@[ end if]@ +} +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ + +@[end for] diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em new file mode 100644 index 0000000..47daa06 --- /dev/null +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -0,0 +1,155 @@ +use std; +use libc::c_char; +use libc::uintptr_t; +use rclrs_common; +use std::ffi::CString; +use std::ffi::CStr; + +@[for subfolder, msg_spec in msg_specs]@ +@{ +type_name = msg_spec.base_type.type +}@ + +#[derive(Default)] +pub struct @(type_name) { +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ + // TODO(esteve): arrays are not supported yet +@[ else]@ +@[ if field.type.is_primitive_type()]@ + pub @(field.name): @(get_rs_type(field.type)), +@[ else]@ + // TODO(esteve): nested types are not supported yet +@[ end if]@ +@[ end if]@ +@[end for]@ +} + +#[link(name = "@(package_name)__rosidl_typesupport_c__rsext")] +extern "C" { + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() -> uintptr_t; + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@[ if field.type.type == 'string']@ + @(field.name): *const c_char, +@[ else]@ + @(field.name): @(get_rs_type(field.type)), +@[ end if]@ +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ + ) -> uintptr_t; + + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle: uintptr_t) -> (); + +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@[ if field.type.type == 'string']@ + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle: uintptr_t) -> *const c_char; +@[ else]@ + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle: uintptr_t) -> @(get_rs_type(field.type)); +@[ end if]@ +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ +} + +// impl @(type_name) { +// pub fn new() -> @(type_name) { +// @(type_name) {} +// } +// } + +impl @(type_name) { + fn get_native_message(&self) -> uintptr_t { + return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@[ if field.type.type == 'string']@ + CString::new(self.@(field.name).clone()).unwrap().as_ptr(), +@[ else]@ + self.@(field.name), +@[ end if]@ +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ + ) }; + } + + fn destroy_native_message(&self, message_handle: uintptr_t) -> () { + unsafe { + @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle); + } + } + + fn read_handle(&mut self, message_handle: uintptr_t) -> () { + unsafe { + { +@[for field in msg_spec.fields]@ +@[ if field.type.is_array]@ +@[ else]@ +@[ if field.type.is_primitive_type()]@ +@[ if field.type.type == 'string']@ + let ptr = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle); + self.@(field.name) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); +@[ else]@ + self.@(field.name) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle); +@[ end if]@ +@[ else]@ +@[ end if]@ +@[ end if]@ +@[end for]@ + } + } + } +} + +impl rclrs_common::traits::Message for @(type_name) { + fn get_native_message(&self) -> uintptr_t { + return self.get_native_message(); + } + + fn destroy_native_message(&self, message_handle: uintptr_t) -> () { + self.destroy_native_message(message_handle); + } + + fn read_handle(&mut self, message_handle: uintptr_t) -> () { + self.read_handle(message_handle); + } +} + +impl rclrs_common::traits::MessageDefinition<@(type_name)> for @(type_name) { + fn get_type_support() -> uintptr_t { + return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() }; + } + + fn static_get_native_message(message: &@(type_name)) -> uintptr_t { + return message.get_native_message(); + } + + fn static_destroy_native_message(message_handle: uintptr_t) -> () { + unsafe { + @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle); + } + } +} + +// impl Default for @(type_name) { +// fn default() -> @(type_name) { +// @(type_name) { +// +// } +// } +// } + +@[end for] diff --git a/rosidl_generator_rs/resource/srv.c.em b/rosidl_generator_rs/resource/srv.c.em new file mode 100644 index 0000000..e69de29 diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em new file mode 100644 index 0000000..e69de29 diff --git a/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in b/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in new file mode 100644 index 0000000..fe75b9d --- /dev/null +++ b/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in @@ -0,0 +1,7 @@ +# generated from rosidl_generator_rs/rosidl_generator_rs-extras.cmake +include("${CMAKE_CURRENT_LIST_DIR}/register_rs.cmake") +rosidl_generator_rs_extras( + "${rosidl_generator_rs_DIR}/../../../lib/rosidl_generator_rs/rosidl_generator_rs" + "${rosidl_generator_rs_DIR}/../../../@PYTHON_INSTALL_DIR@/rosidl_generator_rs/__init__.py" + "${rosidl_generator_rs_DIR}/../resource" +) diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py new file mode 100644 index 0000000..40eda9a --- /dev/null +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -0,0 +1,304 @@ +# Copyright 2016-2017 Esteve Fernandez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import defaultdict +import os + +from rosidl_cmake import convert_camel_case_to_lower_case_underscore +from rosidl_cmake import expand_template +from rosidl_cmake import get_newest_modification_time +from rosidl_cmake import read_generator_arguments +from rosidl_parser import parse_message_file +from rosidl_parser import parse_service_file + + +# Taken from http://stackoverflow.com/a/6425628 +def convert_lower_case_underscore_to_camel_case(word): + return ''.join(x.capitalize() or '_' for x in word.split('_')) + + +def generate_rs(generator_arguments_file, typesupport_impls): + args = read_generator_arguments(generator_arguments_file) + typesupport_impls = typesupport_impls.split(';') + + template_dir = args['template_dir'] + type_support_impl_by_filename = { + '%s_rs.ep.{0}.c'.format(impl): impl + for impl in typesupport_impls + } + mapping_msgs = { + os.path.join(template_dir, 'msg.rs.em'): ['rust/src/%s.rs'], + os.path.join(template_dir, 'msg.c.em'): + type_support_impl_by_filename.keys(), + } + + mapping_srvs = { + os.path.join(template_dir, 'srv.rs.em'): ['rust/src/%s.rs'], + os.path.join(template_dir, 'srv.c.em'): + type_support_impl_by_filename.keys(), + } + + for template_file in mapping_msgs.keys(): + assert os.path.exists(template_file), \ + 'Messages template file %s not found' % template_file + for template_file in mapping_srvs.keys(): + assert os.path.exists(template_file), \ + 'Services template file %s not found' % template_file + + data = { + 'get_c_type': get_c_type, + 'get_rs_type': get_rs_type, + 'constant_value_to_rs': constant_value_to_rs, + 'value_to_rs': value_to_rs, + 'convert_camel_case_to_lower_case_underscore': + convert_camel_case_to_lower_case_underscore, + 'convert_lower_case_underscore_to_camel_case': + convert_lower_case_underscore_to_camel_case, + 'get_builtin_rs_type': get_builtin_rs_type, + 'msg_specs': [], + 'srv_specs': [], + 'package_name': args['package_name'], + 'typesupport_impls': typesupport_impls, + } + latest_target_timestamp = get_newest_modification_time( + args['target_dependencies']) + + for ros_interface_file in args['ros_interface_files']: + extension = os.path.splitext(ros_interface_file)[1] + subfolder = os.path.basename(os.path.dirname(ros_interface_file)) + if extension == '.msg': + data['msg_specs'].append((subfolder, parse_message_file( + args['package_name'], ros_interface_file))) + elif extension == '.srv': + data['srv_specs'].append((subfolder, parse_service_file( + args['package_name'], ros_interface_file))) + else: + continue + + if data['msg_specs']: + for template_file, generated_filenames in mapping_msgs.items(): + for generated_filename in generated_filenames: + generated_file = os.path.join(args['output_dir'], + generated_filename % 'msg') + expand_template( + os.path.join(template_dir, template_file), + data.copy(), + generated_file, + minimum_timestamp=latest_target_timestamp) + + if data['srv_specs']: + for template_file, generated_filenames in mapping_srvs.items(): + for generated_filename in generated_filenames: + generated_file = os.path.join(args['output_dir'], + generated_filename % 'srv') + expand_template( + os.path.join(template_dir, template_file), + data.copy(), + generated_file, + minimum_timestamp=latest_target_timestamp) + + expand_template( + os.path.join(template_dir, 'lib.rs.em'), + data.copy(), + os.path.join(args['output_dir'], 'rust/src/lib.rs'), + minimum_timestamp=latest_target_timestamp) + + return 0 + + +def escape_string(s): + s = s.replace('\\', '\\\\') + s = s.replace("'", "\\'") + return s + + +def value_to_rs(type_, value): + assert type_.is_primitive_type() + assert value is not None + + if not type_.is_array: + return primitive_value_to_rs(type_, value) + + rs_values = [] + for single_value in value: + rs_value = primitive_value_to_rs(type_, single_value) + rs_values.append(rs_value) + return '{%s}' % ', '.join(rs_values) + + +def primitive_value_to_rs(type_, value): + assert type_.is_primitive_type() + assert value is not None + + if type_.type == 'bool': + return 'true' if value else 'false' + + if type_.type in [ + 'byte', + 'char', + 'int8', + 'uint8', + 'int16', + 'uint16', + 'int32', + 'uint32', + 'int64', + 'uint64', + 'float64', + ]: + return str(value) + + if type_.type == 'float32': + return '%sf' % value + + if type_.type == 'string': + return '"%s"' % escape_string(value) + + assert False, "unknown primitive type '%s'" % type_ + + +def constant_value_to_rs(type_, value): + assert value is not None + + if type_ == 'bool': + return 'true' if value else 'false' + + if type_ in [ + 'byte', + 'char', + 'int8', + 'uint8', + 'int16', + 'uint16', + 'int32', + 'uint32', + 'int64', + 'uint64', + 'float64', + ]: + return str(value) + + if type_ == 'float32': + return '%sf' % value + + if type_ == 'string': + return '"%s"' % escape_string(value) + + assert False, "unknown constant type '%s'" % type_ + + +def get_builtin_rs_type(type_): + if type_ == 'bool': + return 'bool' + + if type_ == 'byte': + return 'u8' + + if type_ == 'char': + return 'char' + + if type_ == 'float32': + return 'f32' + + if type_ == 'float64': + return 'f64' + + if type_ == 'int8': + return 'i8' + + if type_ == 'uint8': + return 'u8' + + if type_ == 'int16': + return 'i16' + + if type_ == 'uint16': + return 'u16' + + if type_ == 'int32': + return 'i32' + + if type_ == 'uint32': + return 'u32' + + if type_ == 'int64': + return 'i64' + + if type_ == 'uint64': + return 'u64' + + if type_ == 'string': + return 'std::string::String' + + assert False, "unknown type '%s'" % type_ + + +def get_rs_type(type_, subfolder='msg'): + if not type_.is_primitive_type(): + return '%s::%s::%s' % (type_.pkg_name, subfolder, type_.type) + + return get_builtin_rs_type(type_.type) + + +def get_builtin_c_type(type_): + if type_ == 'bool': + return 'bool' + + if type_ == 'byte': + return 'uint8_t' + + if type_ == 'char': + return 'char' + + if type_ == 'float32': + return 'float' + + if type_ == 'float64': + return 'double' + + if type_ == 'int8': + return 'int8_t' + + if type_ == 'uint8': + return 'uint8_t' + + if type_ == 'int16': + return 'int16_t' + + if type_ == 'uint16': + return 'uint16_t' + + if type_ == 'int32': + return 'int32_t' + + if type_ == 'uint32': + return 'uint32_t' + + if type_ == 'int64': + return 'int64_t' + + if type_ == 'uint64': + return 'uint64_t' + + if type_ == 'string': + return 'const char *' + + assert False, "unknown type '%s'" % type_ + + +def get_c_type(type_, subfolder='msg'): + if not type_.is_primitive_type(): + return 'uintptr_t' + + return get_builtin_c_type(type_.type) From cae8d5406ee6a8f6bd56b25c20daf76999bd733d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Lelong?= Date: Sun, 5 May 2019 13:17:14 +0200 Subject: [PATCH 02/32] Crystal and more (#3) * nested messages working * fix array support * add rcl_sys * add author & fix compilation order * readme * format * fix clippy warnings * delete patch * remove leftover build.rs * fix authors * add qos support * add spin & change handle handling * clippy * edit readme * Update README.md * fix message generation issue * remove messages * fix fixed size nested array issue * delete unused files * reset authors * remove rcl_sys * remove remaining authors & revert readme * fix quickstart * fix fixed size array warning * add rosidl_defaults to repos * fix warnings with array generation * register the 'rosidl_generator_rs' * revert message generation to its initial state * add rcl build dependency to rclrs * move spin and spin_once from Node to rclrs * move publisher sleep at the end of the loop * re-add msg to rosidl_generator_rs * add TODO for publisher and subscription lifetime --- rosidl_generator_rs/CMakeLists.txt | 30 ++++++++++++++----- ...idl_generator_rs_generate_interfaces.cmake | 16 ++++++---- rosidl_generator_rs/package.xml | 21 +++++-------- rosidl_generator_rs/resource/Cargo.toml.in | 1 + rosidl_generator_rs/resource/msg.c.em | 6 ---- .../rosidl_generator_rs-extras.cmake.in | 1 + 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt index 5bef6c3..86ea93f 100644 --- a/rosidl_generator_rs/CMakeLists.txt +++ b/rosidl_generator_rs/CMakeLists.txt @@ -3,24 +3,38 @@ cmake_minimum_required(VERSION 3.5) project(rosidl_generator_rs) find_package(ament_cmake REQUIRED) +find_package(ament_cmake_python REQUIRED) +find_package(rosidl_cmake REQUIRED) +find_package(rosidl_generator_c REQUIRED) +find_package(rosidl_typesupport_interface REQUIRED) +find_package(rosidl_typesupport_introspection_c REQUIRED) -ament_export_dependencies(ament_cmake) ament_export_dependencies(rosidl_cmake) +ament_export_dependencies(rosidl_generator_c) + +ament_index_register_resource("rosidl_generator_packages") +ament_index_register_resource("rosidl_runtime_packages") ament_python_install_package(${PROJECT_NAME}) +ament_package( + CONFIG_EXTRAS "rosidl_generator_rs-extras.cmake.in" + "cmake/rosidl_generator_rs_get_typesupports.cmake" + "cmake/register_rs.cmake" +) + +install(DIRECTORY cmake + DESTINATION share/${PROJECT_NAME}) +ament_register_extension( + "rosidl_generate_interfaces" + "rosidl_generator_rs" + "${PROJECT_SOURCE_DIR}/cmake/rosidl_generator_rs_generate_interfaces.cmake") + install( PROGRAMS bin/rosidl_generator_rs DESTINATION lib/rosidl_generator_rs ) - install( DIRECTORY cmake resource DESTINATION share/${PROJECT_NAME} ) - -ament_package( - CONFIG_EXTRAS "cmake/rosidl_generator_rs_get_typesupports.cmake" - "cmake/register_rs.cmake" - "rosidl_generator_rs-extras.cmake.in" -) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 82541e4..40795ae 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -12,10 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -find_package(ament_cmake_export_crates REQUIRED) -find_package(rosidl_generator_c REQUIRED) find_package(rmw_implementation_cmake REQUIRED) find_package(rmw REQUIRED) +find_package(ament_cmake_export_crates REQUIRED) find_package(rclrs_common REQUIRED) if(NOT WIN32) @@ -57,8 +56,13 @@ foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) if(_parent_folder STREQUAL "msg") set(_has_msg TRUE) + set(_idl_file_without_actions ${_idl_file_without_actions} ${_idl_file}) elseif(_parent_folder STREQUAL "srv") set(_has_srv TRUE) + set(_idl_file_without_actions ${_idl_file_without_actions} ${_idl_file}) + elseif(_parent_folder STREQUAL "action") + set(_has_action TRUE) + message(WARNING "Rust actions generation is not implemented") else() message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") endif() @@ -108,7 +112,7 @@ set(target_dependencies "${rosidl_generator_rs_TEMPLATE_DIR}/srv.c.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" - ${rosidl_generate_interfaces_IDL_FILES} + ${_idl_file_without_actions} ${_dependency_files}) foreach(dep ${target_dependencies}) if(NOT EXISTS "${dep}") @@ -116,11 +120,11 @@ foreach(dep ${target_dependencies}) endif() endforeach() -set(generator_arguments_file "${CMAKE_BINARY_DIR}/rosidl_generator_rs__arguments.json") +set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs__arguments.json") rosidl_write_generator_arguments( "${generator_arguments_file}" PACKAGE_NAME "${PROJECT_NAME}" - ROS_INTERFACE_FILES "${rosidl_generate_interfaces_IDL_FILES}" + ROS_INTERFACE_FILES "${_idl_file_without_actions}" ROS_INTERFACE_DEPENDENCIES "${_dependencies}" OUTPUT_DIR "${_output_path}" TEMPLATE_DIR "${rosidl_generator_rs_TEMPLATE_DIR}" @@ -199,7 +203,7 @@ foreach(_typesupport_impl ${_typesupport_impls}) ${PROJECT_NAME}__${_typesupport_impl} ) rosidl_target_interfaces(${_target_name} - ${PROJECT_NAME} rosidl_typesupport_c) + ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) target_include_directories(${_target_name} PUBLIC diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index c465681..d2813b5 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -1,6 +1,6 @@ - + rosidl_generator_rs 0.0.3 Generate the ROS interfaces in Rust. @@ -20,26 +20,19 @@ rosidl_typesupport_c rosidl_typesupport_interface - rmw_implementation - rmw_implementation_cmake - rosidl_generator_c + + rosidl_generator_c + rosidl_parser ament_cmake_gtest ament_lint_auto ament_lint_common - - rmw_implementation - rmw_implementation_cmake + rosidl_cmake rosidl_generator_c - rosidl_parser - rosidl_cmake - - rosidl_typesupport_c - rosidl_typesupport_connext_c - rosidl_typesupport_introspection_c - rosidl_typesupport_opensplice_c + rosidl_generator_packages + rosidl_runtime_packages ament_cmake diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in index 55f1039..3a634a4 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.in +++ b/rosidl_generator_rs/resource/Cargo.toml.in @@ -1,6 +1,7 @@ [package] name = "@PROJECT_NAME@" version = "0.1.0" +edition = "2018" [dependencies] libc = "0.2" diff --git a/rosidl_generator_rs/resource/msg.c.em b/rosidl_generator_rs/resource/msg.c.em index 583cfa8..7b1ce2c 100644 --- a/rosidl_generator_rs/resource/msg.c.em +++ b/rosidl_generator_rs/resource/msg.c.em @@ -1,11 +1,5 @@ #include "rosidl_generator_c/message_type_support_struct.h" -#include "rosidl_generator_c/string.h" -#include "rosidl_generator_c/string_functions.h" - -#include "rosidl_generator_c/primitives_array.h" -#include "rosidl_generator_c/primitives_array_functions.h" - @[for subfolder, msg_spec in msg_specs]@ @{ type_name = msg_spec.base_type.type diff --git a/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in b/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in index fe75b9d..14ba02d 100644 --- a/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in +++ b/rosidl_generator_rs/rosidl_generator_rs-extras.cmake.in @@ -1,4 +1,5 @@ # generated from rosidl_generator_rs/rosidl_generator_rs-extras.cmake +find_package(rosidl_typesupport_c REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/register_rs.cmake") rosidl_generator_rs_extras( "${rosidl_generator_rs_DIR}/../../../lib/rosidl_generator_rs/rosidl_generator_rs" From 515b1dc53fc2c3d4a698a837cd1660aea6457369 Mon Sep 17 00:00:00 2001 From: nnarain Date: Mon, 24 Aug 2020 04:59:21 -0400 Subject: [PATCH 03/32] Build on Dashing+ (#24) * fix warnings * update README for Ubuntu 18.04 * Build on Dashing * Build on Eloquent * Build on Foxy * clean in IDL generator * Use foxy in pipeline Co-authored-by: deb0ch Co-authored-by: deb0ch --- rosidl_generator_rs/CMakeLists.txt | 3 +- .../cmake/custom_command.cmake | 3 +- rosidl_generator_rs/cmake/register_rs.cmake | 2 +- ...idl_generator_rs_generate_interfaces.cmake | 35 +-- rosidl_generator_rs/resource/msg.c.em | 77 ++++--- rosidl_generator_rs/resource/msg.rs.em | 111 ++++----- .../rosidl_generator_rs/__init__.py | 218 +++++++++--------- 7 files changed, 213 insertions(+), 236 deletions(-) diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt index 86ea93f..9a29958 100644 --- a/rosidl_generator_rs/CMakeLists.txt +++ b/rosidl_generator_rs/CMakeLists.txt @@ -10,6 +10,7 @@ find_package(rosidl_typesupport_interface REQUIRED) find_package(rosidl_typesupport_introspection_c REQUIRED) ament_export_dependencies(rosidl_cmake) +ament_export_dependencies(rmw) ament_export_dependencies(rosidl_generator_c) ament_index_register_resource("rosidl_generator_packages") @@ -26,7 +27,7 @@ ament_package( install(DIRECTORY cmake DESTINATION share/${PROJECT_NAME}) ament_register_extension( - "rosidl_generate_interfaces" + "rosidl_generate_idl_interfaces" "rosidl_generator_rs" "${PROJECT_SOURCE_DIR}/cmake/rosidl_generator_rs_generate_interfaces.cmake") diff --git a/rosidl_generator_rs/cmake/custom_command.cmake b/rosidl_generator_rs/cmake/custom_command.cmake index e405c93..a3f3ff9 100644 --- a/rosidl_generator_rs/cmake/custom_command.cmake +++ b/rosidl_generator_rs/cmake/custom_command.cmake @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. - add_custom_command( OUTPUT ${_generated_extension_files} @@ -33,7 +32,7 @@ if(TARGET ${rosidl_generate_interfaces_TARGET}${_target_suffix}) message(WARNING "Custom target ${rosidl_generate_interfaces_TARGET}${_target_suffix} already exists") else() add_custom_target( - ${rosidl_generate_interfaces_TARGET}${_target_suffix} + ${rosidl_generate_interfaces_TARGET}${_target_suffix} ALL DEPENDS ${_generated_extension_files} ${_generated_common_rs_files} diff --git a/rosidl_generator_rs/cmake/register_rs.cmake b/rosidl_generator_rs/cmake/register_rs.cmake index 487cbb7..1dbfcda 100644 --- a/rosidl_generator_rs/cmake/register_rs.cmake +++ b/rosidl_generator_rs/cmake/register_rs.cmake @@ -15,7 +15,7 @@ macro(rosidl_generator_rs_extras BIN GENERATOR_FILES TEMPLATE_DIR) find_package(ament_cmake_core QUIET REQUIRED) ament_register_extension( - "rosidl_generate_interfaces" + "rosidl_generate_idl_interfaces" "rosidl_generator_rs" "rosidl_generator_rs_generate_interfaces.cmake") diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 40795ae..9275c04 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -37,6 +37,10 @@ set(_output_path "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs/${PROJECT_NAME}") set(_generated_extension_files "") set(_generated_common_rs_files "") + +set(_generated_c_files "") +set(_generated_rs_files "") + set(_generated_msg_rs_files "") set(_generated_msg_c_files "") set(_generated_srv_rs_files "") @@ -49,7 +53,7 @@ foreach(_typesupport_impl ${_typesupport_impls}) set(_generated_extension_${_typesupport_impl}_files "") endforeach() -foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) +foreach(_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) get_filename_component(_parent_folder "${_parent_folder}" NAME) get_filename_component(_module_name "${_idl_file}" NAME_WE) @@ -97,7 +101,7 @@ endif() set(_dependency_files "") set(_dependencies "") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - foreach(_idl_file ${${_pkg_name}_INTERFACE_FILES}) + foreach(_idl_file ${${_pkg_name}_IDL_FILES}) set(_abs_idl_file "${${_pkg_name}_DIR}/../${_idl_file}") normalize_path(_abs_idl_file "${_abs_idl_file}") list(APPEND _dependency_files "${_abs_idl_file}") @@ -112,6 +116,7 @@ set(target_dependencies "${rosidl_generator_rs_TEMPLATE_DIR}/srv.c.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" + ${rosidl_generate_interfaces_ABS_IDL_FILES} ${_idl_file_without_actions} ${_dependency_files}) foreach(dep ${target_dependencies}) @@ -124,6 +129,7 @@ set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs__a rosidl_write_generator_arguments( "${generator_arguments_file}" PACKAGE_NAME "${PROJECT_NAME}" + IDL_TUPLES "${rosidl_generate_interfaces_IDL_TUPLES}" ROS_INTERFACE_FILES "${_idl_file_without_actions}" ROS_INTERFACE_DEPENDENCIES "${_dependencies}" OUTPUT_DIR "${_output_path}" @@ -169,39 +175,37 @@ set_property( ${_generated_msg_rs_files} ${_generated_msg_c_files} ${_generated_srv_rs_files} - ${_generated_srv_c_files} PROPERTY GENERATED 1) set(_type_support_by_generated_c_files ${_type_support_by_generated_msg_c_files} ${_type_support_by_generated_srv_c_files}) -set(_generated_c_files ${_generated_msg_c_files} ${_generated_srv_c_files}) set(_generated_rs_files ${_generated_msg_rs_files} ${_generated_srv_rs_files}) set(_rsext_suffix "__rsext") foreach(_typesupport_impl ${_typesupport_impls}) find_package(${_typesupport_impl} REQUIRED) + set(_extension_compile_flags "") + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(_extension_compile_flags -Wall -Wextra) + endif() + set(_target_name "${PROJECT_NAME}__${_typesupport_impl}${_rsext_suffix}") add_library(${_target_name} SHARED ${_generated_extension_${_typesupport_impl}_files} - ${_generated_msg_c_files} - ${_generated_srv_c_files} ) - add_dependencies( ${_target_name} ${rosidl_generate_interfaces_TARGET}${_target_suffix} ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c ) - set(_extension_compile_flags "") - if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(_extension_compile_flags -Wall -Wextra) - endif() target_link_libraries( ${_target_name} ${PROJECT_NAME}__${_typesupport_impl} + ${rosidl_generate_interfaces_TARGET}__rosidl_generator_c ) + rosidl_target_interfaces(${_target_name} ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) @@ -212,7 +216,7 @@ foreach(_typesupport_impl ${_typesupport_impls}) ) ament_target_dependencies(${_target_name} - "rosidl_generator_c" + "rosidl_runtime_c" "rosidl_typesupport_c" "rosidl_typesupport_interface" ) @@ -226,9 +230,8 @@ foreach(_typesupport_impl ${_typesupport_impls}) ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} ) ament_target_dependencies(${_target_name} - "rosidl_generator_c" + "rosidl_runtime_c" "rosidl_generator_rs" - "${PROJECT_NAME}__rosidl_generator_c" ) if(NOT rosidl_generate_interfaces_SKIP_INSTALL) @@ -259,9 +262,7 @@ endforeach() if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( NOT _generated_msg_rs_files STREQUAL "" OR - NOT _generated_msg_c_files STREQUAL "" OR - NOT _generated_srv_rs_files STREQUAL "" OR - NOT _generated_srv_c_files STREQUAL "" + NOT _generated_srv_rs_files STREQUAL "" ) find_package(ament_cmake_cppcheck REQUIRED) ament_cppcheck( diff --git a/rosidl_generator_rs/resource/msg.c.em b/rosidl_generator_rs/resource/msg.c.em index 7b1ce2c..372e24d 100644 --- a/rosidl_generator_rs/resource/msg.c.em +++ b/rosidl_generator_rs/resource/msg.c.em @@ -1,44 +1,44 @@ -#include "rosidl_generator_c/message_type_support_struct.h" +#include "rosidl_runtime_c/string_functions.h" +#include "rosidl_runtime_c/message_type_support_struct.h" + +@{ +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +}@ @[for subfolder, msg_spec in msg_specs]@ @{ -type_name = msg_spec.base_type.type +type_name = msg_spec.structure.namespaced_type.name c_fields = [] -for field in msg_spec.fields: - if field.type.is_array: +for member in msg_spec.structure.members: + if type(member.type) is Array: pass else: - if field.type.is_primitive_type(): - c_fields.append("%s %s" % (get_c_type(field.type), field.name)) + if isinstance(member.type, BasicType) or isinstance(member.type, AbstractGenericString): + c_fields.append("%s %s" % (get_c_type(member.type), member.name)) else: pass -def get_normalized_type(type_, subfolder='msg'): - return get_rs_type(type_, subfolder=subfolder).replace('::', '__') - -msg_normalized_type = get_normalized_type(msg_spec.base_type, subfolder=subfolder) +msg_normalized_type = get_rs_type(msg_spec.structure.namespaced_type).replace('::', '__') }@ -#include "@(msg_spec.base_type.pkg_name)/@(subfolder)/@(convert_camel_case_to_lower_case_underscore(type_name)).h" +#include "@(package_name)/@(subfolder)/@(convert_camel_case_to_lower_case_underscore(type_name)).h" uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() { - return (uintptr_t)ROSIDL_GET_MSG_TYPE_SUPPORT(@(msg_spec.base_type.pkg_name), @(subfolder), @(msg_spec.msg_name)); + return (uintptr_t)ROSIDL_GET_MSG_TYPE_SUPPORT(@(package_name), @(subfolder), @(msg_spec.structure.namespaced_type.name)); } uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( @(', '.join(c_fields))) { - @(msg_normalized_type) * ros_message = @(msg_normalized_type)__create(); -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - rosidl_generator_c__String__assign(&(ros_message->@(field.name)), @(field.name)); -@[ else]@ - ros_message->@(field.name) = @(field.name); -@[ end if]@ -@[ else]@ -@[ end if]@ + @(msg_normalized_type) *ros_message = @(msg_normalized_type)__create(); +@[for member in msg_spec.structure.members]@ +@[ if isinstance(member.type, Array)]@ +@[ elif isinstance(member.type, AbstractGenericString)]@ + rosidl_runtime_c__String__assign(&(ros_message->@(member.name)), @(member.name)); +@[ elif isinstance(member.type, BasicType)]@ + ros_message->@(member.name) = @(member.name); @[ end if]@ @[end for]@ return (uintptr_t)ros_message; @@ -49,22 +49,25 @@ void @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name @(msg_normalized_type)__destroy(ros_message); } -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@(get_c_type(field.type)) @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(uintptr_t message_handle) { -@[ if field.type.type == 'string']@ +@[for member in msg_spec.structure.members]@ +@(get_c_type(member.type)) @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(uintptr_t message_handle) { +@[ if isinstance(member.type, Array)]@ + (void)message_handle; + return 0; +@[ elif isinstance(member.type, AbstractGenericString)]@ @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; - return ros_message->@(field.name).data; -@[ else]@ + return ros_message->@(member.name).data; +@[ elif isinstance(member.type, BasicType)]@ @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; - return ros_message->@(field.name); -@[ end if]@ -} -@[ else]@ -@[ end if]@ + return ros_message->@(member.name); +@[ elif isinstance(member.type, AbstractNestedType)]@ + @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; + return (@(get_c_type(member.type)))&ros_message->@(member.name); +@[ else]@ + (void)message_handle; + return 0; @[ end if]@ +} @[end for]@ @[end for] diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 47daa06..7e712d4 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -1,86 +1,64 @@ -use std; use libc::c_char; use libc::uintptr_t; use rclrs_common; use std::ffi::CString; use std::ffi::CStr; +@{ +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import AbstractSequence +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import Array +}@ + @[for subfolder, msg_spec in msg_specs]@ @{ -type_name = msg_spec.base_type.type +type_name = msg_spec.structure.namespaced_type.name }@ #[derive(Default)] pub struct @(type_name) { -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ - // TODO(esteve): arrays are not supported yet -@[ else]@ -@[ if field.type.is_primitive_type()]@ - pub @(field.name): @(get_rs_type(field.type)), -@[ else]@ - // TODO(esteve): nested types are not supported yet -@[ end if]@ -@[ end if]@ +@[for member in msg_spec.structure.members]@ + pub @(member.name): @(get_rs_type(member.type).replace(package_name, 'crate')), @[end for]@ } #[link(name = "@(package_name)__rosidl_typesupport_c__rsext")] extern "C" { fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() -> uintptr_t; + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - @(field.name): *const c_char, -@[ else]@ - @(field.name): @(get_rs_type(field.type)), -@[ end if]@ -@[ else]@ -@[ end if]@ +@[for member in msg_spec.structure.members]@ +@[ if isinstance(member.type, AbstractGenericString)]@ + @(member.name): *const c_char, +@[ elif isinstance(member.type, BasicType)]@ + @(member.name): @(get_rs_type(member.type)), @[ end if]@ @[end for]@ ) -> uintptr_t; fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle: uintptr_t) -> (); -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle: uintptr_t) -> *const c_char; -@[ else]@ - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle: uintptr_t) -> @(get_rs_type(field.type)); -@[ end if]@ -@[ else]@ -@[ end if]@ +@[for member in msg_spec.structure.members]@ +@[ if isinstance(member.type, Array)]@ +@[ elif isinstance(member.type, AbstractGenericString)]@ + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(message_handle: uintptr_t) -> *const c_char; +@[ elif isinstance(member.type, BasicType)]@ + fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(message_handle: uintptr_t) -> @(get_rs_type(member.type)); @[ end if]@ @[end for]@ } -// impl @(type_name) { -// pub fn new() -> @(type_name) { -// @(type_name) {} -// } -// } - impl @(type_name) { fn get_native_message(&self) -> uintptr_t { return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - CString::new(self.@(field.name).clone()).unwrap().as_ptr(), -@[ else]@ - self.@(field.name), -@[ end if]@ -@[ else]@ -@[ end if]@ +@[for member in msg_spec.structure.members]@ +@[ if isinstance(member.type, Array)]@ +@[ elif isinstance(member.type, AbstractGenericString)]@ + CString::new(self.@(member.name).clone()).unwrap().as_ptr(), +@[ elif isinstance(member.type, BasicType)]@ + self.@(member.name), @[ end if]@ @[end for]@ ) }; @@ -92,21 +70,18 @@ impl @(type_name) { } } - fn read_handle(&mut self, message_handle: uintptr_t) -> () { + #[allow(unused_unsafe)] + fn read_handle(&mut self, _message_handle: uintptr_t) -> () { unsafe { { -@[for field in msg_spec.fields]@ -@[ if field.type.is_array]@ -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - let ptr = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle); - self.@(field.name) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); -@[ else]@ - self.@(field.name) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(field.name)_read_handle(message_handle); -@[ end if]@ -@[ else]@ -@[ end if]@ +@[for member in msg_spec.structure.members]@ +@[ if isinstance(member.type, Array)]@ +@[ elif isinstance(member.type, AbstractGenericString)]@ + let ptr = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); + self.@(member.name) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); +@[ elif isinstance(member.type, BasicType)]@ + self.@(member.name) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); +@[ elif isinstance(member.type, AbstractSequence)]@ @[ end if]@ @[end for]@ } @@ -144,12 +119,4 @@ impl rclrs_common::traits::MessageDefinition<@(type_name)> for @(type_name) { } } -// impl Default for @(type_name) { -// fn default() -> @(type_name) { -// @(type_name) { -// -// } -// } -// } - @[end for] diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 40eda9a..c67af71 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -12,15 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -from collections import defaultdict import os +import pathlib from rosidl_cmake import convert_camel_case_to_lower_case_underscore from rosidl_cmake import expand_template +from rosidl_cmake import generate_files from rosidl_cmake import get_newest_modification_time from rosidl_cmake import read_generator_arguments -from rosidl_parser import parse_message_file -from rosidl_parser import parse_service_file + +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import AbstractSequence +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BASIC_TYPES +from rosidl_parser.definition import IdlContent +from rosidl_parser.definition import IdlLocator +from rosidl_parser.definition import Message +from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import Service + +from rosidl_parser.parser import parse_idl_file # Taken from http://stackoverflow.com/a/6425628 @@ -30,6 +44,23 @@ def convert_lower_case_underscore_to_camel_case(word): def generate_rs(generator_arguments_file, typesupport_impls): args = read_generator_arguments(generator_arguments_file) + package_name = args['package_name'] + + # expand init modules for each directory + modules = {} + idl_content = IdlContent() + for idl_tuple in args.get('idl_tuples', []): + idl_parts = idl_tuple.rsplit(':', 1) + assert len(idl_parts) == 2 + + idl_rel_path = pathlib.Path(idl_parts[1]) + idl_stems = modules.setdefault(str(idl_rel_path.parent), set()) + idl_stems.add(idl_rel_path.stem) + + locator = IdlLocator(*idl_parts) + idl_file = parse_idl_file(locator) + idl_content.elements += idl_file.content.elements + typesupport_impls = typesupport_impls.split(';') template_dir = args['template_dir'] @@ -37,18 +68,18 @@ def generate_rs(generator_arguments_file, typesupport_impls): '%s_rs.ep.{0}.c'.format(impl): impl for impl in typesupport_impls } + mapping_msgs = { os.path.join(template_dir, 'msg.rs.em'): ['rust/src/%s.rs'], - os.path.join(template_dir, 'msg.c.em'): - type_support_impl_by_filename.keys(), + os.path.join(template_dir, 'msg.c.em'): type_support_impl_by_filename.keys(), } mapping_srvs = { os.path.join(template_dir, 'srv.rs.em'): ['rust/src/%s.rs'], - os.path.join(template_dir, 'srv.c.em'): - type_support_impl_by_filename.keys(), + os.path.join(template_dir, 'srv.c.em'): type_support_impl_by_filename.keys(), } + # Ensure the required templates exist for template_file in mapping_msgs.keys(): assert os.path.exists(template_file), \ 'Messages template file %s not found' % template_file @@ -74,17 +105,11 @@ def generate_rs(generator_arguments_file, typesupport_impls): latest_target_timestamp = get_newest_modification_time( args['target_dependencies']) - for ros_interface_file in args['ros_interface_files']: - extension = os.path.splitext(ros_interface_file)[1] - subfolder = os.path.basename(os.path.dirname(ros_interface_file)) - if extension == '.msg': - data['msg_specs'].append((subfolder, parse_message_file( - args['package_name'], ros_interface_file))) - elif extension == '.srv': - data['srv_specs'].append((subfolder, parse_service_file( - args['package_name'], ros_interface_file))) - else: - continue + for message in idl_content.get_elements_of_type(Message): + data['msg_specs'].append(('msg', message)) + + for service in idl_content.get_elements_of_type(Service): + data['srv_specs'].append(('srv', service)) if data['msg_specs']: for template_file, generated_filenames in mapping_msgs.items(): @@ -198,107 +223,88 @@ def constant_value_to_rs(type_, value): assert False, "unknown constant type '%s'" % type_ -def get_builtin_rs_type(type_): - if type_ == 'bool': - return 'bool' - - if type_ == 'byte': - return 'u8' - - if type_ == 'char': - return 'char' - - if type_ == 'float32': - return 'f32' - - if type_ == 'float64': - return 'f64' - - if type_ == 'int8': - return 'i8' - - if type_ == 'uint8': - return 'u8' - - if type_ == 'int16': - return 'i16' - - if type_ == 'uint16': - return 'u16' - - if type_ == 'int32': - return 'i32' - - if type_ == 'uint32': - return 'u32' - - if type_ == 'int64': - return 'i64' - - if type_ == 'uint64': - return 'u64' - - if type_ == 'string': +def get_builtin_rs_type(type_, package_name=None): + if isinstance(type_, BasicType): + if type_.typename == 'boolean': + return 'bool' + elif type_.typename in ['byte', 'octet']: + return 'u8' + elif type_.typename == 'char': + return 'u8' + elif type_.typename in ['float']: + return 'f32' + elif type_.typename in ['double', 'long double']: + return 'f64' + elif type_.typename == 'int8': + return 'i8' + elif type_.typename == 'uint8': + return 'u8' + elif type_.typename == 'int16': + return 'i16' + elif type_.typename == 'uint16': + return 'u16' + elif type_.typename == 'int32': + return 'i32' + elif type_.typename == 'uint32': + return 'u32' + elif type_.typename == 'int64': + return 'i64' + elif type_.typename == 'uint64': + return 'u64' + elif isinstance(type_, AbstractGenericString): return 'std::string::String' + elif isinstance(type_, Array): + return '[{}, {}]'.format(get_rs_type(type_.value_type), 32 if type_.size <= 32 else 32) + elif isinstance(type_, AbstractSequence): + return 'Vec<{}>'.format(get_rs_type(type_.value_type)) - assert False, "unknown type '%s'" % type_ + assert False, "unknown type '%s'" % type_.typename -def get_rs_type(type_, subfolder='msg'): - if not type_.is_primitive_type(): - return '%s::%s::%s' % (type_.pkg_name, subfolder, type_.type) +def get_rs_type(type_): + if isinstance(type_, NamespacedType): + return '::'.join(type_.namespaced_name()) - return get_builtin_rs_type(type_.type) + return get_builtin_rs_type(type_) def get_builtin_c_type(type_): - if type_ == 'bool': - return 'bool' - - if type_ == 'byte': - return 'uint8_t' - - if type_ == 'char': - return 'char' - - if type_ == 'float32': - return 'float' - - if type_ == 'float64': - return 'double' - if type_ == 'int8': - return 'int8_t' - - if type_ == 'uint8': - return 'uint8_t' - - if type_ == 'int16': - return 'int16_t' - - if type_ == 'uint16': - return 'uint16_t' - - if type_ == 'int32': - return 'int32_t' - - if type_ == 'uint32': - return 'uint32_t' - - if type_ == 'int64': - return 'int64_t' - - if type_ == 'uint64': - return 'uint64_t' - - if type_ == 'string': + if isinstance(type_, BasicType): + if type_.typename == 'boolean': + return 'bool' + if type_.typename in ['byte', 'octet']: + return 'uint8_t' + if type_.typename == 'char': + return 'char' + if type_.typename in ['float']: + return 'float' + if type_.typename in ['double', 'long double']: + return 'double' + if type_.typename == 'int8': + return 'int8_t' + if type_.typename == 'uint8': + return 'uint8_t' + if type_.typename == 'int16': + return 'int16_t' + if type_.typename == 'uint16': + return 'uint16_t' + if type_.typename == 'int32': + return 'int32_t' + if type_.typename == 'uint32': + return 'uint32_t' + if type_.typename == 'int64': + return 'int64_t' + if type_.typename == 'uint64': + return 'uint64_t' + elif isinstance(type_, AbstractGenericString): return 'const char *' - assert False, "unknown type '%s'" % type_ + assert False, "unknown type '%s'" % type_.typename def get_c_type(type_, subfolder='msg'): - if not type_.is_primitive_type(): + if not isinstance(type_, BasicType) and not isinstance(type_, AbstractGenericString): return 'uintptr_t' - return get_builtin_c_type(type_.type) + return get_builtin_c_type(type_) From 6d8826ba97f65e725db03b90f3089b559521d455 Mon Sep 17 00:00:00 2001 From: nnarain Date: Wed, 11 Nov 2020 08:16:01 -0500 Subject: [PATCH 04/32] Fix array type generation. And append an '_' to field names that an rust keywords. (#30) --- rosidl_generator_rs/resource/msg.rs.em | 14 +++++++------- .../rosidl_generator_rs/__init__.py | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 7e712d4..29e7e03 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -20,7 +20,7 @@ type_name = msg_spec.structure.namespaced_type.name #[derive(Default)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ - pub @(member.name): @(get_rs_type(member.type).replace(package_name, 'crate')), + pub @(get_rs_name(member.name)): @(get_rs_type(member.type).replace(package_name, 'crate')), @[end for]@ } @@ -31,9 +31,9 @@ extern "C" { fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( @[for member in msg_spec.structure.members]@ @[ if isinstance(member.type, AbstractGenericString)]@ - @(member.name): *const c_char, + @(get_rs_name(member.name)): *const c_char, @[ elif isinstance(member.type, BasicType)]@ - @(member.name): @(get_rs_type(member.type)), + @(get_rs_name(member.name)): @(get_rs_type(member.type)), @[ end if]@ @[end for]@ ) -> uintptr_t; @@ -56,9 +56,9 @@ impl @(type_name) { @[for member in msg_spec.structure.members]@ @[ if isinstance(member.type, Array)]@ @[ elif isinstance(member.type, AbstractGenericString)]@ - CString::new(self.@(member.name).clone()).unwrap().as_ptr(), + CString::new(self.@(get_rs_name(member.name)).clone()).unwrap().as_ptr(), @[ elif isinstance(member.type, BasicType)]@ - self.@(member.name), + self.@(get_rs_name(member.name)), @[ end if]@ @[end for]@ ) }; @@ -78,9 +78,9 @@ impl @(type_name) { @[ if isinstance(member.type, Array)]@ @[ elif isinstance(member.type, AbstractGenericString)]@ let ptr = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); - self.@(member.name) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); + self.@(get_rs_name(member.name)) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); @[ elif isinstance(member.type, BasicType)]@ - self.@(member.name) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); + self.@(get_rs_name(member.name)) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); @[ elif isinstance(member.type, AbstractSequence)]@ @[ end if]@ @[end for]@ diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index c67af71..4cd2aa8 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -90,6 +90,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): data = { 'get_c_type': get_c_type, 'get_rs_type': get_rs_type, + 'get_rs_name': get_rs_name, 'constant_value_to_rs': constant_value_to_rs, 'value_to_rs': value_to_rs, 'convert_camel_case_to_lower_case_underscore': @@ -141,6 +142,20 @@ def generate_rs(generator_arguments_file, typesupport_impls): return 0 +def get_rs_name(name): + keywords = [ + # strict keywords + 'as', 'break', 'const', 'continue', 'crate', 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'for', + 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'Self', 'static', + 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'while', + # Edition 2018+ + 'async', 'await', 'dyn', + # Reserved + 'abstract', 'become', 'box', 'do', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual', + 'yield', 'try' + ] + # If the field name is a reserved keyword in rust append an underscore + return name if not name in keywords else name + '_' def escape_string(s): s = s.replace('\\', '\\\\') @@ -254,7 +269,7 @@ def get_builtin_rs_type(type_, package_name=None): elif isinstance(type_, AbstractGenericString): return 'std::string::String' elif isinstance(type_, Array): - return '[{}, {}]'.format(get_rs_type(type_.value_type), 32 if type_.size <= 32 else 32) + return '[{}; {}]'.format(get_rs_type(type_.value_type), 32 if type_.size <= 32 else 32) elif isinstance(type_, AbstractSequence): return 'Vec<{}>'.format(get_rs_type(type_.value_type)) From 0e94f69489835bfecd15e1c7d647850246d52536 Mon Sep 17 00:00:00 2001 From: jhdcs <48914066+jhdcs@users.noreply.github.com> Date: Mon, 22 Nov 2021 08:48:36 -0500 Subject: [PATCH 05/32] Build system refactor (#64) * Experimental change to build system. Allows IDE to parse dependencies. Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Remove commented code Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Refactoring to workspace layout. Does not compile. Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Revert change to workspace, general CMake tweaks Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Initial re-make of build system Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Fixing warnings within rosidl_generator Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Make sure cargo builds within the correct directory Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Add in checks for ROS 2 version to change the compilation syntax Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Properly query environment variable Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Only bind rcl, rmw, and rcutils Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Re-write to move most of `rclrs_common` to `rclrs` Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Updating/fixing package XML to comply with format 3 schema Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Missed a schema update Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Missed another schema... Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold * Remove manual crate paths in toml files Distro A, OPSEC #4584. You may have additional rights; please see https://rosmilitary.org/faq/?category=ros-2-license Signed-off-by: Jacob Hassold --- ...sidl_generator_rs_generate_interfaces.cmake | 18 ++++++++++++------ rosidl_generator_rs/package.xml | 10 +++++----- rosidl_generator_rs/resource/lib.rs.em | 2 +- rosidl_generator_rs/resource/msg.rs.em | 11 +++++++---- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 9275c04..bcb338d 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -15,7 +15,7 @@ find_package(rmw_implementation_cmake REQUIRED) find_package(rmw REQUIRED) find_package(ament_cmake_export_crates REQUIRED) -find_package(rclrs_common REQUIRED) +find_package(rclrs_msg_utilities REQUIRED) if(NOT WIN32) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -143,10 +143,10 @@ set(_target_suffix "__rs") set(_crate_deps "") set(CRATES_DEPENDENCIES "") -find_package(rclrs_common REQUIRED) -foreach(_crate_dep ${rclrs_common_CRATES}) +find_package(rclrs_msg_utilities REQUIRED) +foreach(_crate_dep ${rclrs_msg_utilities_CRATES}) list(APPEND _crate_deps "${_crate_dep}") - set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\nrclrs_common = { path = '${rclrs_common_CRATES}' }") + set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\nrclrs_msg_utilities = { path = '${rclrs_msg_utilities_CRATES}' }") endforeach() foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) @@ -182,6 +182,7 @@ set(_generated_rs_files ${_generated_msg_rs_files} ${_generated_srv_rs_files}) set(_rsext_suffix "__rsext") foreach(_typesupport_impl ${_typesupport_impls}) + # message(" _typesupport_impl: ${_typesupport_impl}") find_package(${_typesupport_impl} REQUIRED) set(_extension_compile_flags "") @@ -206,8 +207,13 @@ foreach(_typesupport_impl ${_typesupport_impls}) ${rosidl_generate_interfaces_TARGET}__rosidl_generator_c ) - rosidl_target_interfaces(${_target_name} - ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) + if (NOT $ENV{ROS_DISTRO} STREQUAL "rolling") + rosidl_target_interfaces(${_target_name} + ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) + else() + rosidl_get_typesupport_target(rust_typesupport_target ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) + target_link_libraries(${_target_name} ${rust_typesupport_target}) + endif() target_include_directories(${_target_name} PUBLIC diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index d2813b5..e647713 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -1,22 +1,22 @@ - + rosidl_generator_rs - 0.0.3 + 0.0.4 Generate the ROS interfaces in Rust. - Esteve Fernandez Esteve Fernandez Apache License 2.0 + Esteve Fernandez ament_cmake ament_cmake_export_crates - rclrs_common + rclrs_msg_utilities ament_cmake ament_cmake_export_crates rosidl_cmake - rclrs_common + rclrs_msg_utilities rosidl_typesupport_c rosidl_typesupport_interface diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em index d129ece..8acd152 100644 --- a/rosidl_generator_rs/resource/lib.rs.em +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -1,4 +1,4 @@ -extern crate rclrs_common; +extern crate rclrs_msg_utilities; extern crate libc; @[if len(msg_specs) > 0]@ diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 29e7e03..071db20 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -1,6 +1,6 @@ use libc::c_char; use libc::uintptr_t; -use rclrs_common; +use rclrs_msg_utilities; use std::ffi::CString; use std::ffi::CStr; @@ -56,7 +56,10 @@ impl @(type_name) { @[for member in msg_spec.structure.members]@ @[ if isinstance(member.type, Array)]@ @[ elif isinstance(member.type, AbstractGenericString)]@ - CString::new(self.@(get_rs_name(member.name)).clone()).unwrap().as_ptr(), + {let s = CString::new(self.@(get_rs_name(member.name)).clone()).unwrap(); + let p = s.as_ptr(); + std::mem::forget(s); + p}, @[ elif isinstance(member.type, BasicType)]@ self.@(get_rs_name(member.name)), @[ end if]@ @@ -89,7 +92,7 @@ impl @(type_name) { } } -impl rclrs_common::traits::Message for @(type_name) { +impl rclrs_msg_utilities::traits::Message for @(type_name) { fn get_native_message(&self) -> uintptr_t { return self.get_native_message(); } @@ -103,7 +106,7 @@ impl rclrs_common::traits::Message for @(type_name) { } } -impl rclrs_common::traits::MessageDefinition<@(type_name)> for @(type_name) { +impl rclrs_msg_utilities::traits::MessageDefinition<@(type_name)> for @(type_name) { fn get_type_support() -> uintptr_t { return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() }; } From 34b005d1dcf6bc3615d144666b63d3fe238e272a Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Fri, 18 Feb 2022 18:55:08 +0100 Subject: [PATCH 06/32] Use the ament_cargo build type (#73) * Use the ament_cargo build type The rclrs_crate_config_generator is superseded by colcon-ros-cargo. The ament_cmake_export_crates mechanism is subsumed by creating entries in the ament index directly in the rosidl_generator_rs and cargo-ament-build. * Install colcon-cargo and colcon-ros-cargo * Force running pip3 as root * Install cargo-ament-build * Removed no longer needed dependencies * Disable Rolling job * Update README * Update rust.yml * Update README.md Co-authored-by: Esteve Fernandez --- ...idl_generator_rs_generate_interfaces.cmake | 22 +++---------------- rosidl_generator_rs/package.xml | 2 -- .../rosidl_generator_rs/__init__.py | 2 +- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index bcb338d..4ee0c91 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -14,8 +14,6 @@ find_package(rmw_implementation_cmake REQUIRED) find_package(rmw REQUIRED) -find_package(ament_cmake_export_crates REQUIRED) -find_package(rclrs_msg_utilities REQUIRED) if(NOT WIN32) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -141,21 +139,12 @@ file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") -set(_crate_deps "") -set(CRATES_DEPENDENCIES "") -find_package(rclrs_msg_utilities REQUIRED) -foreach(_crate_dep ${rclrs_msg_utilities_CRATES}) - list(APPEND _crate_deps "${_crate_dep}") - set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\nrclrs_msg_utilities = { path = '${rclrs_msg_utilities_CRATES}' }") -endforeach() - +set(CRATES_DEPENDENCIES "rclrs_msg_utilities = \"*\"") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) find_package(${_pkg_name} REQUIRED) - foreach(_crate_dep ${${_pkg_name}_CRATES}) - list(APPEND _crate_deps "${_crate_dep}") - set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = { path = '${_crate_dep}' }") - endforeach() + set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = \"*\"") endforeach() +ament_index_register_resource("rust_packages") # needed to avoid multiple calls to the Rust generator trick copied from @@ -182,7 +171,6 @@ set(_generated_rs_files ${_generated_msg_rs_files} ${_generated_srv_rs_files}) set(_rsext_suffix "__rsext") foreach(_typesupport_impl ${_typesupport_impls}) - # message(" _typesupport_impl: ${_typesupport_impl}") find_package(${_typesupport_impl} REQUIRED) set(_extension_compile_flags "") @@ -245,10 +233,6 @@ foreach(_typesupport_impl ${_typesupport_impls}) DIRECTORY "${_output_path}/rust" DESTINATION "share/${PROJECT_NAME}" ) - ament_export_crates("share/${PROJECT_NAME}/rust") - # install("${PROJECT_NAME}/lib.rs" "share/${PROJECT_NAME}/rust/src") - # install("${PROJECT_NAME}/msg.rs" "share/${PROJECT_NAME}/rust/src") - # install("${PROJECT_NAME}/srv.rs" "share/${PROJECT_NAME}/rust/src") install(TARGETS ${_target_name} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index e647713..ed75977 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -10,11 +10,9 @@ ament_cmake - ament_cmake_export_crates rclrs_msg_utilities ament_cmake - ament_cmake_export_crates rosidl_cmake rclrs_msg_utilities rosidl_typesupport_c diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 4cd2aa8..37a8ab1 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -154,7 +154,7 @@ def get_rs_name(name): 'abstract', 'become', 'box', 'do', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual', 'yield', 'try' ] - # If the field name is a reserved keyword in rust append an underscore + # If the field name is a reserved keyword in Rust append an underscore return name if not name in keywords else name + '_' def escape_string(s): From c5738a65457d033719459a1e5fedf8e7e01a9bee Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Fri, 18 Mar 2022 12:11:58 +0100 Subject: [PATCH 07/32] Message generation refactoring (#80) Previously, only messages consisting of basic types and strings were supported. Now, all message types will work, including those that have fields of nested types, bounded types, or arrays. Changes: - The "rsext" library is deleted - Unused messages in "rosidl_generator_rs" are deleted - There is a new package, "rosidl_runtime_rs", see below - The RMW-compatible messages from C, which do not require an extra conversion step, are exposed in addition to the "idiomatic" messages - Publisher and subscription are changed to work with both idiomatic and rmw types, through the unifying `Message` trait On `rosidl_runtime_rs`: This package is the successor of `rclrs_msg_utilities` package, but doesn't have much in common with it anymore. It provides common types and functionality for messages. The `String` and `Sequence` types and their variants in that package essentially wrap C types from the `rosidl_runtime_c` package and C messages generated by the "rosidl_generator_c" package. A number of functions and traits are implemented on these types, so that they feel as ergonomic as possible, for instance, a `seq!` macro for creating a sequence. There is also some documentation and doctests. The memory for the (non-pretty) message types is managed by the C allocator. Not yet implemented: - long double - constants - Services/clients - @verbatim comments - ndarray for sequences/arrays of numeric types - implementing `Eq`, `Ord` and `Hash` when a message contains no floats --- ...idl_generator_rs_generate_interfaces.cmake | 111 +----- rosidl_generator_rs/msg/Bool.msg | 1 - rosidl_generator_rs/msg/Byte.msg | 1 - rosidl_generator_rs/msg/Char.msg | 1 - rosidl_generator_rs/msg/Constants.msg | 5 - rosidl_generator_rs/msg/Empty.msg | 1 - rosidl_generator_rs/msg/Float32.msg | 1 - rosidl_generator_rs/msg/Float64.msg | 1 - rosidl_generator_rs/msg/Int16.msg | 1 - rosidl_generator_rs/msg/Int32.msg | 1 - rosidl_generator_rs/msg/Int64.msg | 1 - rosidl_generator_rs/msg/Int8.msg | 1 - rosidl_generator_rs/msg/Nested.msg | 6 - rosidl_generator_rs/msg/Primitives.msg | 18 - rosidl_generator_rs/msg/Strings.msg | 4 - rosidl_generator_rs/msg/Uint16.msg | 1 - rosidl_generator_rs/msg/Uint32.msg | 1 - rosidl_generator_rs/msg/Uint64.msg | 1 - rosidl_generator_rs/msg/Uint8.msg | 1 - rosidl_generator_rs/msg/Various.msg | 23 -- rosidl_generator_rs/package.xml | 4 +- rosidl_generator_rs/resource/Cargo.toml.in | 2 +- rosidl_generator_rs/resource/lib.rs.em | 5 +- rosidl_generator_rs/resource/msg.c.em | 73 ---- rosidl_generator_rs/resource/msg.rs.em | 328 +++++++++++++----- rosidl_generator_rs/resource/srv.c.em | 0 .../rosidl_generator_rs/__init__.py | 198 +++++------ 27 files changed, 357 insertions(+), 434 deletions(-) delete mode 100644 rosidl_generator_rs/msg/Bool.msg delete mode 100644 rosidl_generator_rs/msg/Byte.msg delete mode 100644 rosidl_generator_rs/msg/Char.msg delete mode 100644 rosidl_generator_rs/msg/Constants.msg delete mode 100644 rosidl_generator_rs/msg/Empty.msg delete mode 100644 rosidl_generator_rs/msg/Float32.msg delete mode 100644 rosidl_generator_rs/msg/Float64.msg delete mode 100644 rosidl_generator_rs/msg/Int16.msg delete mode 100644 rosidl_generator_rs/msg/Int32.msg delete mode 100644 rosidl_generator_rs/msg/Int64.msg delete mode 100644 rosidl_generator_rs/msg/Int8.msg delete mode 100644 rosidl_generator_rs/msg/Nested.msg delete mode 100644 rosidl_generator_rs/msg/Primitives.msg delete mode 100644 rosidl_generator_rs/msg/Strings.msg delete mode 100644 rosidl_generator_rs/msg/Uint16.msg delete mode 100644 rosidl_generator_rs/msg/Uint32.msg delete mode 100644 rosidl_generator_rs/msg/Uint64.msg delete mode 100644 rosidl_generator_rs/msg/Uint8.msg delete mode 100644 rosidl_generator_rs/msg/Various.msg delete mode 100644 rosidl_generator_rs/resource/msg.c.em delete mode 100644 rosidl_generator_rs/resource/srv.c.em diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 4ee0c91..6940177 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -36,13 +36,8 @@ set(_output_path set(_generated_extension_files "") set(_generated_common_rs_files "") -set(_generated_c_files "") -set(_generated_rs_files "") - set(_generated_msg_rs_files "") -set(_generated_msg_c_files "") set(_generated_srv_rs_files "") -set(_generated_srv_c_files "") set(_has_msg FALSE) set(_has_srv FALSE) @@ -110,8 +105,6 @@ endforeach() set(target_dependencies "${rosidl_generator_rs_BIN}" ${rosidl_generator_rs_GENERATOR_FILES} - "${rosidl_generator_rs_TEMPLATE_DIR}/msg.c.em" - "${rosidl_generator_rs_TEMPLATE_DIR}/srv.c.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" ${rosidl_generate_interfaces_ABS_IDL_FILES} @@ -139,7 +132,7 @@ file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") -set(CRATES_DEPENDENCIES "rclrs_msg_utilities = \"*\"") +set(CRATES_DEPENDENCIES "rosidl_runtime_rs = \"*\"") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) find_package(${_pkg_name} REQUIRED) set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = \"*\"") @@ -162,117 +155,35 @@ set_property( ${_generated_extension_files} ${_generated_common_rs_files} ${_generated_msg_rs_files} - ${_generated_msg_c_files} ${_generated_srv_rs_files} PROPERTY GENERATED 1) -set(_type_support_by_generated_c_files ${_type_support_by_generated_msg_c_files} ${_type_support_by_generated_srv_c_files}) -set(_generated_rs_files ${_generated_msg_rs_files} ${_generated_srv_rs_files}) - set(_rsext_suffix "__rsext") foreach(_typesupport_impl ${_typesupport_impls}) find_package(${_typesupport_impl} REQUIRED) - set(_extension_compile_flags "") - if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(_extension_compile_flags -Wall -Wextra) - endif() - - set(_target_name "${PROJECT_NAME}__${_typesupport_impl}${_rsext_suffix}") - - add_library(${_target_name} SHARED - ${_generated_extension_${_typesupport_impl}_files} - ) - add_dependencies( - ${_target_name} - ${rosidl_generate_interfaces_TARGET}${_target_suffix} - ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c - ) - - target_link_libraries( - ${_target_name} - ${PROJECT_NAME}__${_typesupport_impl} - ${rosidl_generate_interfaces_TARGET}__rosidl_generator_c - ) - - if (NOT $ENV{ROS_DISTRO} STREQUAL "rolling") - rosidl_target_interfaces(${_target_name} - ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) - else() - rosidl_get_typesupport_target(rust_typesupport_target ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) - target_link_libraries(${_target_name} ${rust_typesupport_target}) - endif() - - target_include_directories(${_target_name} - PUBLIC - ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_c - ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs - ) - - ament_target_dependencies(${_target_name} - "rosidl_runtime_c" - "rosidl_typesupport_c" - "rosidl_typesupport_interface" - ) - foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - ament_target_dependencies(${_target_name} - ${_pkg_name} - ) - endforeach() - - add_dependencies(${_target_name} - ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} - ) - ament_target_dependencies(${_target_name} - "rosidl_runtime_c" - "rosidl_generator_rs" - ) - if(NOT rosidl_generate_interfaces_SKIP_INSTALL) install( DIRECTORY "${_output_path}/rust" DESTINATION "share/${PROJECT_NAME}" ) - install(TARGETS ${_target_name} - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib - ) - - configure_file("${rosidl_generator_rs_TEMPLATE_DIR}/Cargo.toml.in" - "share/${PROJECT_NAME}/rust/Cargo.toml" - @ONLY) - - install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/rust/Cargo.toml" - DESTINATION share/${PROJECT_NAME}/rust/ - ) endif() endforeach() +configure_file("${rosidl_generator_rs_TEMPLATE_DIR}/Cargo.toml.in" + "share/${PROJECT_NAME}/rust/Cargo.toml" + @ONLY) + +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/rust/Cargo.toml" + DESTINATION share/${PROJECT_NAME}/rust/ +) + if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( NOT _generated_msg_rs_files STREQUAL "" OR NOT _generated_srv_rs_files STREQUAL "" ) - find_package(ament_cmake_cppcheck REQUIRED) - ament_cppcheck( - TESTNAME "cppcheck_rosidl_generated_rs" - "${_output_path}") - - find_package(ament_cmake_cpplint REQUIRED) - get_filename_component(_cpplint_root "${_output_path}" DIRECTORY) - ament_cpplint( - TESTNAME "cpplint_rosidl_generated_rs" - # the generated code might contain longer lines for templated types - MAX_LINE_LENGTH 999 - ROOT "${_cpplint_root}" - "${_output_path}") - - find_package(ament_cmake_uncrustify REQUIRED) - ament_uncrustify( - TESTNAME "uncrustify_rosidl_generated_rs" - # the generated code might contain longer lines for templated types - MAX_LINE_LENGTH 999 - "${_output_path}") + # TODO(esteve): add linters for Rust files endif() endif() diff --git a/rosidl_generator_rs/msg/Bool.msg b/rosidl_generator_rs/msg/Bool.msg deleted file mode 100644 index b1578ab..0000000 --- a/rosidl_generator_rs/msg/Bool.msg +++ /dev/null @@ -1 +0,0 @@ -bool empty_bool diff --git a/rosidl_generator_rs/msg/Byte.msg b/rosidl_generator_rs/msg/Byte.msg deleted file mode 100644 index 2e6fa04..0000000 --- a/rosidl_generator_rs/msg/Byte.msg +++ /dev/null @@ -1 +0,0 @@ -byte empty_byte diff --git a/rosidl_generator_rs/msg/Char.msg b/rosidl_generator_rs/msg/Char.msg deleted file mode 100644 index 0f2e0c9..0000000 --- a/rosidl_generator_rs/msg/Char.msg +++ /dev/null @@ -1 +0,0 @@ -char empty_char diff --git a/rosidl_generator_rs/msg/Constants.msg b/rosidl_generator_rs/msg/Constants.msg deleted file mode 100644 index d9e404e..0000000 --- a/rosidl_generator_rs/msg/Constants.msg +++ /dev/null @@ -1,5 +0,0 @@ -int32 X=123 -int32 Y=-123 -string FOO=foo -char TOTO=127 -byte TATA=48 diff --git a/rosidl_generator_rs/msg/Empty.msg b/rosidl_generator_rs/msg/Empty.msg deleted file mode 100644 index 8b13789..0000000 --- a/rosidl_generator_rs/msg/Empty.msg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rosidl_generator_rs/msg/Float32.msg b/rosidl_generator_rs/msg/Float32.msg deleted file mode 100644 index a8bb0cb..0000000 --- a/rosidl_generator_rs/msg/Float32.msg +++ /dev/null @@ -1 +0,0 @@ -float32 empty_float32 diff --git a/rosidl_generator_rs/msg/Float64.msg b/rosidl_generator_rs/msg/Float64.msg deleted file mode 100644 index d6efd99..0000000 --- a/rosidl_generator_rs/msg/Float64.msg +++ /dev/null @@ -1 +0,0 @@ -float64 empty_float64 diff --git a/rosidl_generator_rs/msg/Int16.msg b/rosidl_generator_rs/msg/Int16.msg deleted file mode 100644 index 14426ca..0000000 --- a/rosidl_generator_rs/msg/Int16.msg +++ /dev/null @@ -1 +0,0 @@ -int16 empty_int16 diff --git a/rosidl_generator_rs/msg/Int32.msg b/rosidl_generator_rs/msg/Int32.msg deleted file mode 100644 index 51c88f0..0000000 --- a/rosidl_generator_rs/msg/Int32.msg +++ /dev/null @@ -1 +0,0 @@ -int32 empty_int32 diff --git a/rosidl_generator_rs/msg/Int64.msg b/rosidl_generator_rs/msg/Int64.msg deleted file mode 100644 index 75a1c23..0000000 --- a/rosidl_generator_rs/msg/Int64.msg +++ /dev/null @@ -1 +0,0 @@ -int64 empty_int64 diff --git a/rosidl_generator_rs/msg/Int8.msg b/rosidl_generator_rs/msg/Int8.msg deleted file mode 100644 index 76167eb..0000000 --- a/rosidl_generator_rs/msg/Int8.msg +++ /dev/null @@ -1 +0,0 @@ -int8 empty_int8 diff --git a/rosidl_generator_rs/msg/Nested.msg b/rosidl_generator_rs/msg/Nested.msg deleted file mode 100644 index a7386b5..0000000 --- a/rosidl_generator_rs/msg/Nested.msg +++ /dev/null @@ -1,6 +0,0 @@ -uint8 ANSWER=42 - -Primitives primitives -Primitives[2] two_primitives -Primitives[<=3] up_to_three_primitives -Primitives[] unbounded_primitives diff --git a/rosidl_generator_rs/msg/Primitives.msg b/rosidl_generator_rs/msg/Primitives.msg deleted file mode 100644 index ec13f6a..0000000 --- a/rosidl_generator_rs/msg/Primitives.msg +++ /dev/null @@ -1,18 +0,0 @@ -bool bool_value true -byte byte_value -char char_value -float32 float32_value 1.125 -float64 float64_value -int8 int8_value -5 -uint8 uint8_value 23 -int16 int16_value -uint16 uint16_value -int32 int32_value -uint32 uint32_value -int64 int64_value -uint64 uint64_value -string string_value -string string_value_with_default 'default' -#string<=5[3] fixed_length_string_value -#string<=5[<=10] upper_bound_string_value -string unbound_string_value diff --git a/rosidl_generator_rs/msg/Strings.msg b/rosidl_generator_rs/msg/Strings.msg deleted file mode 100644 index f81e029..0000000 --- a/rosidl_generator_rs/msg/Strings.msg +++ /dev/null @@ -1,4 +0,0 @@ -string empty_string -string def_string "Hello world!" -string<=22 ub_string -string<=22 ub_def_string "Upper bounded string." diff --git a/rosidl_generator_rs/msg/Uint16.msg b/rosidl_generator_rs/msg/Uint16.msg deleted file mode 100644 index 9dd741c..0000000 --- a/rosidl_generator_rs/msg/Uint16.msg +++ /dev/null @@ -1 +0,0 @@ -uint16 empty_uint16 diff --git a/rosidl_generator_rs/msg/Uint32.msg b/rosidl_generator_rs/msg/Uint32.msg deleted file mode 100644 index cb65d08..0000000 --- a/rosidl_generator_rs/msg/Uint32.msg +++ /dev/null @@ -1 +0,0 @@ -uint32 empty_uint32 diff --git a/rosidl_generator_rs/msg/Uint64.msg b/rosidl_generator_rs/msg/Uint64.msg deleted file mode 100644 index 9e7c0f8..0000000 --- a/rosidl_generator_rs/msg/Uint64.msg +++ /dev/null @@ -1 +0,0 @@ -uint64 empty_uint64 diff --git a/rosidl_generator_rs/msg/Uint8.msg b/rosidl_generator_rs/msg/Uint8.msg deleted file mode 100644 index 6b0876e..0000000 --- a/rosidl_generator_rs/msg/Uint8.msg +++ /dev/null @@ -1 +0,0 @@ -uint8 empty_uint8 diff --git a/rosidl_generator_rs/msg/Various.msg b/rosidl_generator_rs/msg/Various.msg deleted file mode 100644 index efe284a..0000000 --- a/rosidl_generator_rs/msg/Various.msg +++ /dev/null @@ -1,23 +0,0 @@ -bool bool_value false -byte byte_value 1 -char char_value 1 -float32 float32_value 1.23 -float64 float64_value -int8 int8_value -5 -uint16[2] two_uint16_value [5, 23] -int32[<=3] up_to_three_int32_values -int32[<=3] up_to_three_int32_values_with_default_values [5, 23] -uint64[] unbounded_uint64_values - -#string[2] two_string_value ['foo', 'bar'] -string[<=3] up_to_three_string_values -string[] unbounded_string_values - -Empty empty -Empty[2] two_empty -Empty[] unbounded_empty - -Nested nested -Nested[2] two_nested -Nested[<=3] up_to_three_nested -Nested[] unbounded_nested diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index ed75977..7fab4fd 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -10,11 +10,11 @@ ament_cmake - rclrs_msg_utilities + rosidl_runtime_rs ament_cmake rosidl_cmake - rclrs_msg_utilities + rosidl_runtime_rs rosidl_typesupport_c rosidl_typesupport_interface diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in index 3a634a4..4673315 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.in +++ b/rosidl_generator_rs/resource/Cargo.toml.in @@ -1,7 +1,7 @@ [package] name = "@PROJECT_NAME@" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] libc = "0.2" diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em index 8acd152..02df735 100644 --- a/rosidl_generator_rs/resource/lib.rs.em +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -1,10 +1,7 @@ -extern crate rclrs_msg_utilities; -extern crate libc; - @[if len(msg_specs) > 0]@ pub mod msg; @[end if]@ @[if len(srv_specs) > 0]@ pub mod srv; -@[end if]@ \ No newline at end of file +@[end if]@ diff --git a/rosidl_generator_rs/resource/msg.c.em b/rosidl_generator_rs/resource/msg.c.em deleted file mode 100644 index 372e24d..0000000 --- a/rosidl_generator_rs/resource/msg.c.em +++ /dev/null @@ -1,73 +0,0 @@ -#include "rosidl_runtime_c/string_functions.h" -#include "rosidl_runtime_c/message_type_support_struct.h" - -@{ -from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import AbstractNestedType -from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType -}@ - -@[for subfolder, msg_spec in msg_specs]@ -@{ -type_name = msg_spec.structure.namespaced_type.name -c_fields = [] -for member in msg_spec.structure.members: - if type(member.type) is Array: - pass - else: - if isinstance(member.type, BasicType) or isinstance(member.type, AbstractGenericString): - c_fields.append("%s %s" % (get_c_type(member.type), member.name)) - else: - pass - -msg_normalized_type = get_rs_type(msg_spec.structure.namespaced_type).replace('::', '__') -}@ - -#include "@(package_name)/@(subfolder)/@(convert_camel_case_to_lower_case_underscore(type_name)).h" - -uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() { - return (uintptr_t)ROSIDL_GET_MSG_TYPE_SUPPORT(@(package_name), @(subfolder), @(msg_spec.structure.namespaced_type.name)); -} - -uintptr_t @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( - @(', '.join(c_fields))) { - @(msg_normalized_type) *ros_message = @(msg_normalized_type)__create(); -@[for member in msg_spec.structure.members]@ -@[ if isinstance(member.type, Array)]@ -@[ elif isinstance(member.type, AbstractGenericString)]@ - rosidl_runtime_c__String__assign(&(ros_message->@(member.name)), @(member.name)); -@[ elif isinstance(member.type, BasicType)]@ - ros_message->@(member.name) = @(member.name); -@[ end if]@ -@[end for]@ - return (uintptr_t)ros_message; -} - -void @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(void * raw_ros_message) { - @(msg_normalized_type) * ros_message = raw_ros_message; - @(msg_normalized_type)__destroy(ros_message); -} - -@[for member in msg_spec.structure.members]@ -@(get_c_type(member.type)) @(package_name)_msg_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(uintptr_t message_handle) { -@[ if isinstance(member.type, Array)]@ - (void)message_handle; - return 0; -@[ elif isinstance(member.type, AbstractGenericString)]@ - @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; - return ros_message->@(member.name).data; -@[ elif isinstance(member.type, BasicType)]@ - @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; - return ros_message->@(member.name); -@[ elif isinstance(member.type, AbstractNestedType)]@ - @(msg_normalized_type) * ros_message = (@(msg_normalized_type) *)message_handle; - return (@(get_c_type(member.type)))&ros_message->@(member.name); -@[ else]@ - (void)message_handle; - return 0; -@[ end if]@ -} -@[end for]@ - -@[end for] diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 071db20..94efcc3 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -1,123 +1,277 @@ -use libc::c_char; -use libc::uintptr_t; -use rclrs_msg_utilities; -use std::ffi::CString; -use std::ffi::CStr; - @{ -from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import AbstractNestedType -from rosidl_parser.definition import AbstractSequence -from rosidl_parser.definition import BasicType from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import BoundedString +from rosidl_parser.definition import NamedType +from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import UnboundedString +from rosidl_parser.definition import UnboundedWString }@ - -@[for subfolder, msg_spec in msg_specs]@ +pub mod rmw { + @[for subfolder, msg_spec in msg_specs]@ @{ type_name = msg_spec.structure.namespaced_type.name }@ -#[derive(Default)] +#[link(name = "@(package_name)__rosidl_typesupport_c")] +extern "C" { + fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; +} + +#[link(name = "@(package_name)__rosidl_generator_c")] +extern "C" { + fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: libc::size_t) -> bool; + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>) -> (); +} + +@# Drop is not needed, since the default drop glue does the same as fini here: +@# it just calls the drop/fini functions of all fields +// Corresponds to @(package_name)__@(subfolder)__@(type_name) +#[repr(C)] +#[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ - pub @(get_rs_name(member.name)): @(get_rs_type(member.type).replace(package_name, 'crate')), + pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), @[end for]@ } -#[link(name = "@(package_name)__rosidl_typesupport_c__rsext")] -extern "C" { - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() -> uintptr_t; +impl Default for @(type_name) { + fn default() -> Self { + unsafe { +@# // This is safe since a zeroed bit pattern always forms a valid message. + let mut msg = std::mem::zeroed(); +@# // This is safe since the precondititons for inti() are fulfilled by giving it a zeroed message. + if !@(package_name)__@(subfolder)__@(type_name)__init(&mut msg as *mut _) { + panic!("Call to @(package_name)__@(subfolder)__@(type_name)__init() failed"); + } + msg + } + } +} + + + +impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { + fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: libc::size_t) -> bool { + unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq as *mut _, size) } + } + fn sequence_fini(seq: &mut rosidl_runtime_rs::Sequence) { + unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq as *mut _) } + } + fn sequence_copy(in_seq: &rosidl_runtime_rs::Sequence, out_seq: &mut rosidl_runtime_rs::Sequence) -> bool { + out_seq.resize_to_at_least(in_seq.len()); + out_seq.clone_from_slice(in_seq.as_slice()); + true + } +} + +impl rosidl_runtime_rs::Message for @(type_name) { + type RmwMsg = Self; + fn into_rmw_message<'a>(msg_cow: std::borrow::Cow<'a, Self>) -> std::borrow::Cow<'a, Self::RmwMsg> { msg_cow } + fn from_rmw_message(msg: Self::RmwMsg) -> Self { msg } +} + +impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { + fn get_type_support() -> libc::uintptr_t { + return unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() }; + } +} + +@[end for] +} // mod rmw + +@# ################################################# +@# ############ Idiomatic message types ############ +@# ################################################# +@# These types use standard Rust containers where possible. +@[for subfolder, msg_spec in msg_specs]@ +@{ +type_name = msg_spec.structure.namespaced_type.name +}@ - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct @(type_name) { @[for member in msg_spec.structure.members]@ -@[ if isinstance(member.type, AbstractGenericString)]@ - @(get_rs_name(member.name)): *const c_char, -@[ elif isinstance(member.type, BasicType)]@ - @(get_rs_name(member.name)): @(get_rs_type(member.type)), -@[ end if]@ + pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), @[end for]@ - ) -> uintptr_t; +} + +impl Default for @(type_name) { + fn default() -> Self { +@# This has the benefit of automatically setting the right default values + ::from_rmw_message(crate::msg::rmw::@(type_name)::default()) + } +} - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle: uintptr_t) -> (); +impl rosidl_runtime_rs::Message for @(type_name) { + type RmwMsg = crate::msg::rmw::@(type_name); + fn into_rmw_message<'a>(msg_cow: std::borrow::Cow<'a, Self>) -> std::borrow::Cow<'a, Self::RmwMsg> { + match msg_cow { + std::borrow::Cow::Owned(msg) => std::borrow::Cow::Owned(Self::RmwMsg { @[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == @[ if isinstance(member.type, Array)]@ -@[ elif isinstance(member.type, AbstractGenericString)]@ - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(message_handle: uintptr_t) -> *const c_char; -@[ elif isinstance(member.type, BasicType)]@ - fn @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(message_handle: uintptr_t) -> @(get_rs_type(member.type)); +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| elem.as_str().into()), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), +@[ end if]@ +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() + .map(|elem| elem.as_str().into()) + .collect(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()) + .collect(), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).into(), +@[ end if]@ +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Owned(msg.@(get_rs_name(member.name)))).into_owned(), +@# +@# +@# == Bounded and basic types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), @[ end if]@ @[end for]@ -} - -impl @(type_name) { - fn get_native_message(&self) -> uintptr_t { - return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_native_message( + }), + std::borrow::Cow::Borrowed(msg) => std::borrow::Cow::Owned(Self::RmwMsg { @[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == @[ if isinstance(member.type, Array)]@ -@[ elif isinstance(member.type, AbstractGenericString)]@ - {let s = CString::new(self.@(get_rs_name(member.name)).clone()).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); - p}, +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| elem.as_str().into()) + .collect::>() + .try_into() + .unwrap(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) + .collect::>() + .try_into() + .unwrap(), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), +@[ end if]@ +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| elem.as_str().into()) + .collect(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) + .collect(), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_slice().into(), +@[ end if]@ +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Borrowed(&msg.@(get_rs_name(member.name)))).into_owned(), +@# +@# +@# == BasicType == @[ elif isinstance(member.type, BasicType)]@ - self.@(get_rs_name(member.name)), + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@# +@# +@# == Bounded types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), @[ end if]@ @[end for]@ - ) }; - } - - fn destroy_native_message(&self, message_handle: uintptr_t) -> () { - unsafe { - @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle); + }) } } - #[allow(unused_unsafe)] - fn read_handle(&mut self, _message_handle: uintptr_t) -> () { - unsafe { - { + fn from_rmw_message(msg: Self::RmwMsg) -> Self { + Self { @[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == @[ if isinstance(member.type, Array)]@ -@[ elif isinstance(member.type, AbstractGenericString)]@ - let ptr = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); - self.@(get_rs_name(member.name)) = CStr::from_ptr(ptr).to_string_lossy().into_owned(); -@[ elif isinstance(member.type, BasicType)]@ - self.@(get_rs_name(member.name)) = @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_@(member.name)_read_handle(_message_handle); -@[ elif isinstance(member.type, AbstractSequence)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| elem.to_string()), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message(elem)), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ end if]@ +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + .map(|elem| elem.to_string()) +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message(elem)) +@[ end if]@ + .collect(), +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).to_string(), +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::from_rmw_message(msg.@(get_rs_name(member.name))), +@# +@# +@# == Bounded and basic types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), @[ end if]@ @[end for]@ - } - } - } -} - -impl rclrs_msg_utilities::traits::Message for @(type_name) { - fn get_native_message(&self) -> uintptr_t { - return self.get_native_message(); - } - - fn destroy_native_message(&self, message_handle: uintptr_t) -> () { - self.destroy_native_message(message_handle); - } - - fn read_handle(&mut self, message_handle: uintptr_t) -> () { - self.read_handle(message_handle); - } -} - -impl rclrs_msg_utilities::traits::MessageDefinition<@(type_name)> for @(type_name) { - fn get_type_support() -> uintptr_t { - return unsafe { @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_get_type_support() }; - } - - fn static_get_native_message(message: &@(type_name)) -> uintptr_t { - return message.get_native_message(); - } - - fn static_destroy_native_message(message_handle: uintptr_t) -> () { - unsafe { - @(package_name)_@(subfolder)_@(convert_camel_case_to_lower_case_underscore(type_name))_destroy_native_message(message_handle); } } } diff --git a/rosidl_generator_rs/resource/srv.c.em b/rosidl_generator_rs/resource/srv.c.em deleted file mode 100644 index e69de29..0000000 diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 37a8ab1..dcc9b59 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -14,6 +14,9 @@ import os import pathlib +import subprocess + +from pathlib import Path from rosidl_cmake import convert_camel_case_to_lower_case_underscore from rosidl_cmake import expand_template @@ -24,15 +27,22 @@ from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import AbstractNestedType from rosidl_parser.definition import AbstractSequence -from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractWString from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType from rosidl_parser.definition import BASIC_TYPES +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import BoundedString +from rosidl_parser.definition import BoundedWString from rosidl_parser.definition import IdlContent from rosidl_parser.definition import IdlLocator from rosidl_parser.definition import Message from rosidl_parser.definition import NamespacedType from rosidl_parser.definition import Service +from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import UnboundedString +from rosidl_parser.definition import UnboundedWString from rosidl_parser.parser import parse_idl_file @@ -49,6 +59,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): # expand init modules for each directory modules = {} idl_content = IdlContent() + (Path(args['output_dir']) / 'rust/src/bindings').mkdir(parents=True, exist_ok=True) for idl_tuple in args.get('idl_tuples', []): idl_parts = idl_tuple.rsplit(':', 1) assert len(idl_parts) == 2 @@ -64,19 +75,13 @@ def generate_rs(generator_arguments_file, typesupport_impls): typesupport_impls = typesupport_impls.split(';') template_dir = args['template_dir'] - type_support_impl_by_filename = { - '%s_rs.ep.{0}.c'.format(impl): impl - for impl in typesupport_impls - } mapping_msgs = { os.path.join(template_dir, 'msg.rs.em'): ['rust/src/%s.rs'], - os.path.join(template_dir, 'msg.c.em'): type_support_impl_by_filename.keys(), } mapping_srvs = { os.path.join(template_dir, 'srv.rs.em'): ['rust/src/%s.rs'], - os.path.join(template_dir, 'srv.c.em'): type_support_impl_by_filename.keys(), } # Ensure the required templates exist @@ -88,21 +93,21 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'Services template file %s not found' % template_file data = { - 'get_c_type': get_c_type, - 'get_rs_type': get_rs_type, + 'get_rmw_rs_type': make_get_rmw_rs_type(args['package_name']), 'get_rs_name': get_rs_name, + 'get_idiomatic_rs_type': make_get_idiomatic_rs_type(args['package_name']), 'constant_value_to_rs': constant_value_to_rs, 'value_to_rs': value_to_rs, 'convert_camel_case_to_lower_case_underscore': convert_camel_case_to_lower_case_underscore, 'convert_lower_case_underscore_to_camel_case': convert_lower_case_underscore_to_camel_case, - 'get_builtin_rs_type': get_builtin_rs_type, 'msg_specs': [], 'srv_specs': [], 'package_name': args['package_name'], 'typesupport_impls': typesupport_impls, } + latest_target_timestamp = get_newest_modification_time( args['target_dependencies']) @@ -237,89 +242,88 @@ def constant_value_to_rs(type_, value): assert False, "unknown constant type '%s'" % type_ - -def get_builtin_rs_type(type_, package_name=None): - if isinstance(type_, BasicType): - if type_.typename == 'boolean': - return 'bool' - elif type_.typename in ['byte', 'octet']: - return 'u8' - elif type_.typename == 'char': - return 'u8' - elif type_.typename in ['float']: - return 'f32' - elif type_.typename in ['double', 'long double']: - return 'f64' - elif type_.typename == 'int8': - return 'i8' - elif type_.typename == 'uint8': - return 'u8' - elif type_.typename == 'int16': - return 'i16' - elif type_.typename == 'uint16': - return 'u16' - elif type_.typename == 'int32': - return 'i32' - elif type_.typename == 'uint32': - return 'u32' - elif type_.typename == 'int64': - return 'i64' - elif type_.typename == 'uint64': - return 'u64' - elif isinstance(type_, AbstractGenericString): - return 'std::string::String' - elif isinstance(type_, Array): - return '[{}; {}]'.format(get_rs_type(type_.value_type), 32 if type_.size <= 32 else 32) - elif isinstance(type_, AbstractSequence): - return 'Vec<{}>'.format(get_rs_type(type_.value_type)) - - assert False, "unknown type '%s'" % type_.typename - - -def get_rs_type(type_): - if isinstance(type_, NamespacedType): - return '::'.join(type_.namespaced_name()) - - return get_builtin_rs_type(type_) - - -def get_builtin_c_type(type_): - - if isinstance(type_, BasicType): - if type_.typename == 'boolean': - return 'bool' - if type_.typename in ['byte', 'octet']: - return 'uint8_t' - if type_.typename == 'char': - return 'char' - if type_.typename in ['float']: - return 'float' - if type_.typename in ['double', 'long double']: - return 'double' - if type_.typename == 'int8': - return 'int8_t' - if type_.typename == 'uint8': - return 'uint8_t' - if type_.typename == 'int16': - return 'int16_t' - if type_.typename == 'uint16': - return 'uint16_t' - if type_.typename == 'int32': - return 'int32_t' - if type_.typename == 'uint32': - return 'uint32_t' - if type_.typename == 'int64': - return 'int64_t' - if type_.typename == 'uint64': - return 'uint64_t' - elif isinstance(type_, AbstractGenericString): - return 'const char *' - - assert False, "unknown type '%s'" % type_.typename - - -def get_c_type(type_, subfolder='msg'): - if not isinstance(type_, BasicType) and not isinstance(type_, AbstractGenericString): - return 'uintptr_t' - - return get_builtin_c_type(type_) +# Type hierarchy: +# +# AbstractType +# - AbstractNestableType +# - AbstractGenericString +# - AbstractString +# - BoundedString +# - UnboundedString +# - AbstractWString +# - BoundedWString +# - UnboundedWString +# - BasicType +# - NamedType +# - NamespacedType +# - AbstractNestedType +# - Array +# - AbstractSequence +# - BoundedSequence +# - UnboundedSequence + +def make_get_idiomatic_rs_type(package_name): + get_rmw_rs_type = make_get_rmw_rs_type(package_name) + def get_idiomatic_rs_type(type_): + if isinstance(type_, UnboundedString) or isinstance(type_, UnboundedWString): + return 'std::string::String' + elif isinstance(type_, UnboundedSequence): + return 'Vec::<{}>'.format(get_idiomatic_rs_type(type_.value_type)) + elif isinstance(type_, NamespacedType): + return '::'.join(type_.namespaced_name()).replace(package_name, 'crate') + elif isinstance(type_, Array): + return '[{}; {}]'.format(get_idiomatic_rs_type(type_.value_type), type_.size) + else: + return get_rmw_rs_type(type_) + return get_idiomatic_rs_type + +def make_get_rmw_rs_type(package_name): + def get_rmw_rs_type(type_): + if isinstance(type_, NamespacedType): + parts = list(type_.namespaced_name()) + parts.insert(-1, 'rmw') + return '::'.join(parts).replace(package_name, 'crate') + elif isinstance(type_, BasicType): + if type_.typename == 'boolean': + return 'bool' + elif type_.typename in ['byte', 'octet']: + return 'u8' + elif type_.typename == 'char': + return 'u8' + elif type_.typename == 'float': + return 'f32' + elif type_.typename == 'double': + return 'f64' + elif type_.typename == 'int8': + return 'i8' + elif type_.typename == 'uint8': + return 'u8' + elif type_.typename == 'int16': + return 'i16' + elif type_.typename == 'uint16': + return 'u16' + elif type_.typename == 'int32': + return 'i32' + elif type_.typename == 'uint32': + return 'u32' + elif type_.typename == 'int64': + return 'i64' + elif type_.typename == 'uint64': + return 'u64' + elif isinstance(type_, UnboundedString): + return 'rosidl_runtime_rs::String' + elif isinstance(type_, UnboundedWString): + return 'rosidl_runtime_rs::WString' + elif isinstance(type_, BoundedString): + return 'rosidl_runtime_rs::BoundedString<{}>'.format(type_.maximum_size) + elif isinstance(type_, BoundedWString): + return 'rosidl_runtime_rs::BoundedWString<{}>'.format(type_.maximum_size) + elif isinstance(type_, Array): + return '[{}; {}]'.format(get_rmw_rs_type(type_.value_type), type_.size) + elif isinstance(type_, UnboundedSequence): + return 'rosidl_runtime_rs::Sequence<{}>'.format(get_rmw_rs_type(type_.value_type)) + elif isinstance(type_, BoundedSequence): + return 'rosidl_runtime_rs::BoundedSequence<{}, {}>'.format(get_rmw_rs_type(type_.value_type), type_.maximum_size) + + assert False, "unknown type '%s'" % type_.typename + return get_rmw_rs_type From dc37b06cea0a86f14d0e8fed7df6e87986cb8a12 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Mon, 21 Mar 2022 14:56:50 +0100 Subject: [PATCH 08/32] Enable Clippy in CI (#83) --- rosidl_generator_rs/resource/msg.rs.em | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 94efcc3..4899c7c 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -24,7 +24,7 @@ extern "C" { extern "C" { fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: libc::size_t) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>) -> (); + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>); } @# Drop is not needed, since the default drop glue does the same as fini here: @@ -70,13 +70,13 @@ impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { impl rosidl_runtime_rs::Message for @(type_name) { type RmwMsg = Self; - fn into_rmw_message<'a>(msg_cow: std::borrow::Cow<'a, Self>) -> std::borrow::Cow<'a, Self::RmwMsg> { msg_cow } + fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { msg_cow } fn from_rmw_message(msg: Self::RmwMsg) -> Self { msg } } impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { fn get_type_support() -> libc::uintptr_t { - return unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() }; + unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } } } @@ -109,7 +109,7 @@ impl Default for @(type_name) { impl rosidl_runtime_rs::Message for @(type_name) { type RmwMsg = crate::msg::rmw::@(type_name); - fn into_rmw_message<'a>(msg_cow: std::borrow::Cow<'a, Self>) -> std::borrow::Cow<'a, Self::RmwMsg> { + fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { match msg_cow { std::borrow::Cow::Owned(msg) => std::borrow::Cow::Owned(Self::RmwMsg { @[for member in msg_spec.structure.members]@ @@ -123,6 +123,8 @@ impl rosidl_runtime_rs::Message for @(type_name) { @[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()), +@[ elif isinstance(member.type.value_type, BasicType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), @[ else]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), @[ end if]@ @@ -181,6 +183,8 @@ impl rosidl_runtime_rs::Message for @(type_name) { .collect::>() .try_into() .unwrap(), +@[ elif isinstance(member.type.value_type, BasicType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), @[ else]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), @[ end if]@ @@ -239,7 +243,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { .map(|elem| elem.to_string()), @[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message(elem)), + .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message), @[ else]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), @[ end if]@ @@ -252,7 +256,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { @[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ .map(|elem| elem.to_string()) @[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message(elem)) + .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message) @[ end if]@ .collect(), @# From 4e0632ec72d790af64b373f10460c448f7d49b00 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Sun, 17 Apr 2022 14:47:36 +0200 Subject: [PATCH 09/32] Bump every package to version 0.2 (#100) --- rosidl_generator_rs/package.xml | 2 +- rosidl_generator_rs/resource/Cargo.toml.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index 7fab4fd..d4e60a2 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.0.4 + 0.2.0 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in index 4673315..0d87b47 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.in +++ b/rosidl_generator_rs/resource/Cargo.toml.in @@ -1,6 +1,6 @@ [package] name = "@PROJECT_NAME@" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] From 382873bbdcdd8cd97e4140f808c2dd71e4e5c453 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Fri, 29 Apr 2022 11:49:32 +0200 Subject: [PATCH 10/32] Add serde support to messages (#131) --- .../cmake/rosidl_generator_rs_generate_interfaces.cmake | 3 +++ rosidl_generator_rs/resource/Cargo.toml.in | 6 +++++- rosidl_generator_rs/resource/msg.rs.em | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 6940177..a39bb73 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -133,10 +133,13 @@ file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") set(CRATES_DEPENDENCIES "rosidl_runtime_rs = \"*\"") +set(SERDE_FEATURES "[\"dep:serde\", \"rosidl_runtime_rs/serde\"") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) find_package(${_pkg_name} REQUIRED) set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = \"*\"") + set(SERDE_FEATURES "${SERDE_FEATURES}, \"${_pkg_name}/serde\"") endforeach() + set(SERDE_FEATURES "${SERDE_FEATURES}]") ament_index_register_resource("rust_packages") diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in index 0d87b47..56e5638 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.in +++ b/rosidl_generator_rs/resource/Cargo.toml.in @@ -5,4 +5,8 @@ edition = "2021" [dependencies] libc = "0.2" -@CRATES_DEPENDENCIES@ \ No newline at end of file +serde = { version = "1", optional = true, features = ["derive"] } +@CRATES_DEPENDENCIES@ + +[features] +serde = @SERDE_FEATURES@ diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 4899c7c..2e137da 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -10,6 +10,8 @@ from rosidl_parser.definition import UnboundedString from rosidl_parser.definition import UnboundedWString }@ pub mod rmw { +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; @[for subfolder, msg_spec in msg_specs]@ @{ type_name = msg_spec.structure.namespaced_type.name @@ -31,6 +33,7 @@ extern "C" { @# it just calls the drop/fini functions of all fields // Corresponds to @(package_name)__@(subfolder)__@(type_name) #[repr(C)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ @@ -87,11 +90,14 @@ impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { @# ############ Idiomatic message types ############ @# ################################################# @# These types use standard Rust containers where possible. +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; @[for subfolder, msg_spec in msg_specs]@ @{ type_name = msg_spec.structure.namespaced_type.name }@ +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ From e6613679ce47f5547dd9afc0c80edb0550b564cd Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Mon, 2 May 2022 12:41:21 +0200 Subject: [PATCH 11/32] Generate Cargo.toml of message crate with an EmPy template, not CMake (#138) * Generate Cargo.toml of message crate with an EmPy template, not CMake * Add comment --- ...idl_generator_rs_generate_interfaces.cmake | 18 +-------------- rosidl_generator_rs/resource/Cargo.toml.em | 22 +++++++++++++++++++ rosidl_generator_rs/resource/Cargo.toml.in | 12 ---------- .../rosidl_generator_rs/__init__.py | 21 +++++++++++++++++- 4 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 rosidl_generator_rs/resource/Cargo.toml.em delete mode 100644 rosidl_generator_rs/resource/Cargo.toml.in diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index a39bb73..f10a796 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -67,6 +67,7 @@ endforeach() list(APPEND _generated_common_rs_files "${_output_path}/rust/src/lib.rs" + "${_output_path}/rust/Cargo.toml" ) if(${_has_msg}) @@ -132,14 +133,6 @@ file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") -set(CRATES_DEPENDENCIES "rosidl_runtime_rs = \"*\"") -set(SERDE_FEATURES "[\"dep:serde\", \"rosidl_runtime_rs/serde\"") -foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - find_package(${_pkg_name} REQUIRED) - set(CRATES_DEPENDENCIES "${CRATES_DEPENDENCIES}\n${_pkg_name} = \"*\"") - set(SERDE_FEATURES "${SERDE_FEATURES}, \"${_pkg_name}/serde\"") -endforeach() - set(SERDE_FEATURES "${SERDE_FEATURES}]") ament_index_register_resource("rust_packages") @@ -173,15 +166,6 @@ foreach(_typesupport_impl ${_typesupport_impls}) endif() endforeach() -configure_file("${rosidl_generator_rs_TEMPLATE_DIR}/Cargo.toml.in" - "share/${PROJECT_NAME}/rust/Cargo.toml" - @ONLY) - -install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/rust/Cargo.toml" - DESTINATION share/${PROJECT_NAME}/rust/ -) - if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( NOT _generated_msg_rs_files STREQUAL "" OR diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em new file mode 100644 index 0000000..234c28e --- /dev/null +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -0,0 +1,22 @@ +[package] +name = "@(package_name)" +@# The version should ideally be taken from package.xml, see +@# https://github.com/ros2-rust/ros2_rust/issues/116 +version = "0.2.0" +edition = "2021" + +[dependencies] +libc = "0.2" +rosidl_runtime_rs = "*" +serde = { version = "1", optional = true, features = ["derive"] } +@[for dep in dependency_packages]@ +@(dep) = "*" +@[end for]@ + +[features] +@{ +serde_features = ["dep:serde", "rosidl_runtime_rs/serde"] +for dep in dependency_packages: + serde_features.append("{}/serde".format(dep)) +}@ +serde = @(serde_features) diff --git a/rosidl_generator_rs/resource/Cargo.toml.in b/rosidl_generator_rs/resource/Cargo.toml.in deleted file mode 100644 index 56e5638..0000000 --- a/rosidl_generator_rs/resource/Cargo.toml.in +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "@PROJECT_NAME@" -version = "0.2.0" -edition = "2021" - -[dependencies] -libc = "0.2" -serde = { version = "1", optional = true, features = ["derive"] } -@CRATES_DEPENDENCIES@ - -[features] -serde = @SERDE_FEATURES@ diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index dcc9b59..02c544a 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -59,7 +59,16 @@ def generate_rs(generator_arguments_file, typesupport_impls): # expand init modules for each directory modules = {} idl_content = IdlContent() - (Path(args['output_dir']) / 'rust/src/bindings').mkdir(parents=True, exist_ok=True) + dependency_packages = set() + + (Path(args['output_dir']) / 'rust/src').mkdir(parents=True, exist_ok=True) + + for dep_tuple in args.get('ros_interface_dependencies', []): + dep_parts = dep_tuple.rsplit(':', 1) + assert len(dep_parts) == 2 + if dep_parts[0] != package_name: + dependency_packages.add(dep_parts[0]) + for idl_tuple in args.get('idl_tuples', []): idl_parts = idl_tuple.rsplit(':', 1) assert len(idl_parts) == 2 @@ -145,6 +154,16 @@ def generate_rs(generator_arguments_file, typesupport_impls): os.path.join(args['output_dir'], 'rust/src/lib.rs'), minimum_timestamp=latest_target_timestamp) + cargo_toml_data = { + 'dependency_packages': dependency_packages, + 'package_name': args['package_name'], + } + expand_template( + os.path.join(template_dir, 'Cargo.toml.em'), + cargo_toml_data, + os.path.join(args['output_dir'], 'rust/Cargo.toml'), + minimum_timestamp=latest_target_timestamp) + return 0 def get_rs_name(name): From 42b81ff0690d5754a9a55854479ac446fe02858b Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Tue, 10 May 2022 17:04:46 +0200 Subject: [PATCH 12/32] Add build.rs to messages to automatically find the message libraries (#140) --- .../rosidl_generator_rs_generate_interfaces.cmake | 1 + rosidl_generator_rs/resource/build.rs.em | 10 ++++++++++ rosidl_generator_rs/rosidl_generator_rs/__init__.py | 6 ++++++ 3 files changed, 17 insertions(+) create mode 100644 rosidl_generator_rs/resource/build.rs.em diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index f10a796..33ff0e8 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -67,6 +67,7 @@ endforeach() list(APPEND _generated_common_rs_files "${_output_path}/rust/src/lib.rs" + "${_output_path}/rust/build.rs" "${_output_path}/rust/Cargo.toml" ) diff --git a/rosidl_generator_rs/resource/build.rs.em b/rosidl_generator_rs/resource/build.rs.em new file mode 100644 index 0000000..d861d0e --- /dev/null +++ b/rosidl_generator_rs/resource/build.rs.em @@ -0,0 +1,10 @@ +use std::path::Path; + +fn main() { + let lib_dir = Path::new("../../../lib") + .canonicalize() + .expect("Could not find '../../../lib'"); + // This allows building Rust packages that depend on message crates without + // sourcing the install directory first. + println!("cargo:rustc-link-search={}", lib_dir.display()); +} diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 02c544a..6d99849 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -164,6 +164,12 @@ def generate_rs(generator_arguments_file, typesupport_impls): os.path.join(args['output_dir'], 'rust/Cargo.toml'), minimum_timestamp=latest_target_timestamp) + expand_template( + os.path.join(template_dir, 'build.rs.em'), + {}, + os.path.join(args['output_dir'], 'rust/build.rs'), + minimum_timestamp=latest_target_timestamp) + return 0 def get_rs_name(name): From ac8793e89128dc3767578b43cbde17e19e5c99e0 Mon Sep 17 00:00:00 2001 From: Daisuke Nishimatsu <42202095+wep21@users.noreply.github.com> Date: Tue, 7 Jun 2022 17:29:41 +0900 Subject: [PATCH 13/32] feat: obtain interface version from cmake variable (#191) * feat: obtain interface version from cmake variable Signed-off-by: Daisuke Nishimatsu * refactor: append package version into generator arguments file Signed-off-by: Daisuke Nishimatsu --- rosidl_generator_rs/bin/rosidl_generator_rs | 5 ++++- .../cmake/rosidl_generator_rs_generate_interfaces.cmake | 5 +++++ rosidl_generator_rs/resource/Cargo.toml.em | 4 +--- rosidl_generator_rs/rosidl_generator_rs/__init__.py | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/rosidl_generator_rs/bin/rosidl_generator_rs b/rosidl_generator_rs/bin/rosidl_generator_rs index 2d36a68..0bfcc22 100755 --- a/rosidl_generator_rs/bin/rosidl_generator_rs +++ b/rosidl_generator_rs/bin/rosidl_generator_rs @@ -33,7 +33,10 @@ def main(argv=sys.argv[1:]): help='All the available typesupport implementations') args = parser.parse_args(argv) - return generate_rs(args.generator_arguments_file, args.typesupport_impls) + return generate_rs( + args.generator_arguments_file, + args.typesupport_impls, + ) if __name__ == '__main__': diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 33ff0e8..0d04344 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -130,6 +130,11 @@ rosidl_write_generator_arguments( TARGET_DEPENDENCIES ${target_dependencies} ) +file(READ ${generator_arguments_file} contents) +string(REPLACE "\n}" + ",\n \"package_version\": \"${${PROJECT_NAME}_VERSION}\"\n}" contents ${contents}) +file(WRITE ${generator_arguments_file} ${contents}) + file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index 234c28e..2aa193b 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -1,8 +1,6 @@ [package] name = "@(package_name)" -@# The version should ideally be taken from package.xml, see -@# https://github.com/ros2-rust/ros2_rust/issues/116 -version = "0.2.0" +version = "@(package_version)" edition = "2021" [dependencies] diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 6d99849..d0e3c6a 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -157,6 +157,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): cargo_toml_data = { 'dependency_packages': dependency_packages, 'package_name': args['package_name'], + 'package_version': args['package_version'], } expand_template( os.path.join(template_dir, 'Cargo.toml.em'), From 186774097493380e3325b2f3a33410172831c830 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez Date: Fri, 8 Jul 2022 14:43:36 +0200 Subject: [PATCH 14/32] Added support for clients and services (#146) * Added support for clients and services --- ...idl_generator_rs_generate_interfaces.cmake | 2 + rosidl_generator_rs/resource/lib.rs.em | 2 + rosidl_generator_rs/resource/msg.rs.em | 294 +----------------- .../resource/msg_idiomatic.rs.em | 213 +++++++++++++ rosidl_generator_rs/resource/msg_rmw.rs.em | 88 ++++++ rosidl_generator_rs/resource/srv.rs.em | 80 +++++ .../rosidl_generator_rs/__init__.py | 1 + 7 files changed, 398 insertions(+), 282 deletions(-) create mode 100644 rosidl_generator_rs/resource/msg_idiomatic.rs.em create mode 100644 rosidl_generator_rs/resource/msg_rmw.rs.em diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 0d04344..fd48a55 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -107,6 +107,8 @@ endforeach() set(target_dependencies "${rosidl_generator_rs_BIN}" ${rosidl_generator_rs_GENERATOR_FILES} + "${rosidl_generator_rs_TEMPLATE_DIR}/msg_idiomatic.rs.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/msg_rmw.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" ${rosidl_generate_interfaces_ABS_IDL_FILES} diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em index 02df735..51e4a5b 100644 --- a/rosidl_generator_rs/resource/lib.rs.em +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -1,3 +1,5 @@ +#![allow(non_camel_case_types)] + @[if len(msg_specs) > 0]@ pub mod msg; @[end if]@ diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 2e137da..43c0996 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -1,289 +1,19 @@ -@{ -from rosidl_parser.definition import Array -from rosidl_parser.definition import BasicType -from rosidl_parser.definition import BoundedSequence -from rosidl_parser.definition import BoundedString -from rosidl_parser.definition import NamedType -from rosidl_parser.definition import NamespacedType -from rosidl_parser.definition import UnboundedSequence -from rosidl_parser.definition import UnboundedString -from rosidl_parser.definition import UnboundedWString -}@ pub mod rmw { -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - @[for subfolder, msg_spec in msg_specs]@ @{ -type_name = msg_spec.structure.namespaced_type.name +TEMPLATE( + 'msg_rmw.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=msg_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + get_idiomatic_rs_type=get_idiomatic_rs_type) }@ - -#[link(name = "@(package_name)__rosidl_typesupport_c")] -extern "C" { - fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; -} - -#[link(name = "@(package_name)__rosidl_generator_c")] -extern "C" { - fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: libc::size_t) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>); -} - -@# Drop is not needed, since the default drop glue does the same as fini here: -@# it just calls the drop/fini functions of all fields -// Corresponds to @(package_name)__@(subfolder)__@(type_name) -#[repr(C)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct @(type_name) { -@[for member in msg_spec.structure.members]@ - pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), -@[end for]@ -} - -impl Default for @(type_name) { - fn default() -> Self { - unsafe { -@# // This is safe since a zeroed bit pattern always forms a valid message. - let mut msg = std::mem::zeroed(); -@# // This is safe since the precondititons for inti() are fulfilled by giving it a zeroed message. - if !@(package_name)__@(subfolder)__@(type_name)__init(&mut msg as *mut _) { - panic!("Call to @(package_name)__@(subfolder)__@(type_name)__init() failed"); - } - msg - } - } -} - - - -impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { - fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: libc::size_t) -> bool { - unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq as *mut _, size) } - } - fn sequence_fini(seq: &mut rosidl_runtime_rs::Sequence) { - unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq as *mut _) } - } - fn sequence_copy(in_seq: &rosidl_runtime_rs::Sequence, out_seq: &mut rosidl_runtime_rs::Sequence) -> bool { - out_seq.resize_to_at_least(in_seq.len()); - out_seq.clone_from_slice(in_seq.as_slice()); - true - } -} - -impl rosidl_runtime_rs::Message for @(type_name) { - type RmwMsg = Self; - fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { msg_cow } - fn from_rmw_message(msg: Self::RmwMsg) -> Self { msg } -} - -impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { - fn get_type_support() -> libc::uintptr_t { - unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } -} - -@[end for] } // mod rmw -@# ################################################# -@# ############ Idiomatic message types ############ -@# ################################################# -@# These types use standard Rust containers where possible. -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -@[for subfolder, msg_spec in msg_specs]@ @{ -type_name = msg_spec.structure.namespaced_type.name +TEMPLATE( + 'msg_idiomatic.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=msg_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + get_idiomatic_rs_type=get_idiomatic_rs_type) }@ - -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct @(type_name) { -@[for member in msg_spec.structure.members]@ - pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), -@[end for]@ -} - -impl Default for @(type_name) { - fn default() -> Self { -@# This has the benefit of automatically setting the right default values - ::from_rmw_message(crate::msg::rmw::@(type_name)::default()) - } -} - -impl rosidl_runtime_rs::Message for @(type_name) { - type RmwMsg = crate::msg::rmw::@(type_name); - - fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { - match msg_cow { - std::borrow::Cow::Owned(msg) => std::borrow::Cow::Owned(Self::RmwMsg { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| elem.as_str().into()), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()), -@[ elif isinstance(member.type.value_type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() - .map(|elem| elem.as_str().into()) - .collect(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()) - .collect(), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).into(), -@[ end if]@ -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Owned(msg.@(get_rs_name(member.name)))).into_owned(), -@# -@# -@# == Bounded and basic types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@[end for]@ - }), - std::borrow::Cow::Borrowed(msg) => std::borrow::Cow::Owned(Self::RmwMsg { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| elem.as_str().into()) - .collect::>() - .try_into() - .unwrap(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) - .collect::>() - .try_into() - .unwrap(), -@[ elif isinstance(member.type.value_type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| elem.as_str().into()) - .collect(), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .iter() - .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) - .collect(), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_slice().into(), -@[ end if]@ -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Borrowed(&msg.@(get_rs_name(member.name)))).into_owned(), -@# -@# -@# == BasicType == -@[ elif isinstance(member.type, BasicType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@# -@# -@# == Bounded types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), -@[ end if]@ -@[end for]@ - }) - } - } - - fn from_rmw_message(msg: Self::RmwMsg) -> Self { - Self { -@[for member in msg_spec.structure.members]@ -@# -@# -@# == Array == -@[ if isinstance(member.type, Array)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(|elem| elem.to_string()), -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message), -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@# -@# -@# == UnboundedSequence == -@[ elif isinstance(member.type, UnboundedSequence)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) - .into_iter() -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - .map(|elem| elem.to_string()) -@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ - .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message) -@[ end if]@ - .collect(), -@# -@# -@# == UnboundedString + UnboundedWString == -@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).to_string(), -@# -@# -@# == NamedType + NamespacedType == -@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ - @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::from_rmw_message(msg.@(get_rs_name(member.name))), -@# -@# -@# == Bounded and basic types == -@[ else]@ - @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), -@[ end if]@ -@[end for]@ - } - } -} - -@[end for] diff --git a/rosidl_generator_rs/resource/msg_idiomatic.rs.em b/rosidl_generator_rs/resource/msg_idiomatic.rs.em new file mode 100644 index 0000000..832b000 --- /dev/null +++ b/rosidl_generator_rs/resource/msg_idiomatic.rs.em @@ -0,0 +1,213 @@ +@{ +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import BoundedString +from rosidl_parser.definition import NamedType +from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import UnboundedString +from rosidl_parser.definition import UnboundedWString +}@ + +@# ################################################# +@# ############ Idiomatic message types ############ +@# ################################################# +@# These types use standard Rust containers where possible. +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +@[for subfolder, msg_spec in msg_specs]@ +@{ +type_name = msg_spec.structure.namespaced_type.name +}@ + +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct @(type_name) { +@[for member in msg_spec.structure.members]@ + pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), +@[end for]@ +} + +impl Default for @(type_name) { + fn default() -> Self { +@# This has the benefit of automatically setting the right default values + ::from_rmw_message(crate::@(subfolder)::rmw::@(type_name)::default()) + } +} + +impl rosidl_runtime_rs::Message for @(type_name) { + type RmwMsg = crate::@(subfolder)::rmw::@(type_name); + + fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { + match msg_cow { + std::borrow::Cow::Owned(msg) => std::borrow::Cow::Owned(Self::RmwMsg { +@[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == +@[ if isinstance(member.type, Array)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| elem.as_str().into()), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()), +@[ elif isinstance(member.type.value_type, BasicType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), +@[ end if]@ +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() + .map(|elem| elem.as_str().into()) + .collect(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Owned(elem)).into_owned()) + .collect(), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).into(), +@[ end if]@ +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Owned(msg.@(get_rs_name(member.name)))).into_owned(), +@# +@# +@# == Bounded and basic types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ end if]@ +@[end for]@ + }), + std::borrow::Cow::Borrowed(msg) => std::borrow::Cow::Owned(Self::RmwMsg { +@[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == +@[ if isinstance(member.type, Array)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| elem.as_str().into()) + .collect::>() + .try_into() + .unwrap(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) + .collect::>() + .try_into() + .unwrap(), +@[ elif isinstance(member.type.value_type, BasicType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), +@[ end if]@ +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_str().into(), +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| elem.as_str().into()) + .collect(), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .iter() + .map(|elem| @(get_idiomatic_rs_type(member.type.value_type))::into_rmw_message(std::borrow::Cow::Borrowed(elem)).into_owned()) + .collect(), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).as_slice().into(), +@[ end if]@ +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::into_rmw_message(std::borrow::Cow::Borrowed(&msg.@(get_rs_name(member.name)))).into_owned(), +@# +@# +@# == BasicType == +@[ elif isinstance(member.type, BasicType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@# +@# +@# == Bounded types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).clone(), +@[ end if]@ +@[end for]@ + }) + } + } + + fn from_rmw_message(msg: Self::RmwMsg) -> Self { + Self { +@[for member in msg_spec.structure.members]@ +@# +@# +@# == Array == +@[ if isinstance(member.type, Array)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(|elem| elem.to_string()), +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message), +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ end if]@ +@# +@# +@# == UnboundedSequence == +@[ elif isinstance(member.type, UnboundedSequence)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) + .into_iter() +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + .map(|elem| elem.to_string()) +@[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ + .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message) +@[ end if]@ + .collect(), +@# +@# +@# == UnboundedString + UnboundedWString == +@[ elif isinstance(member.type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)).to_string(), +@# +@# +@# == NamedType + NamespacedType == +@[ elif isinstance(member.type, NamedType) or isinstance(member.type, NamespacedType)]@ + @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type))::from_rmw_message(msg.@(get_rs_name(member.name))), +@# +@# +@# == Bounded and basic types == +@[ else]@ + @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)), +@[ end if]@ +@[end for]@ + } + } +} + +@[end for] diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em new file mode 100644 index 0000000..17d65cb --- /dev/null +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -0,0 +1,88 @@ +@{ +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import BoundedString +from rosidl_parser.definition import NamedType +from rosidl_parser.definition import NamespacedType +from rosidl_parser.definition import UnboundedSequence +from rosidl_parser.definition import UnboundedString +from rosidl_parser.definition import UnboundedWString +}@ +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + @[for subfolder, msg_spec in msg_specs]@ +@{ +type_name = msg_spec.structure.namespaced_type.name +}@ + +#[link(name = "@(package_name)__rosidl_typesupport_c")] +extern "C" { + fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; +} + +#[link(name = "@(package_name)__rosidl_generator_c")] +extern "C" { + fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: libc::size_t) -> bool; + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>); +} + +@# Drop is not needed, since the default drop glue does the same as fini here: +@# it just calls the drop/fini functions of all fields +// Corresponds to @(package_name)__@(subfolder)__@(type_name) +#[repr(C)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct @(type_name) { +@[for member in msg_spec.structure.members]@ + pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), +@[end for]@ +} + +impl Default for @(type_name) { + fn default() -> Self { + unsafe { +@# // SAFETY: This is safe since a zeroed bit pattern always forms a valid message. + let mut msg = std::mem::zeroed(); +@# // SAFETY: This is safe since the precondititons for init() are fulfilled by giving it a zeroed message. + if !@(package_name)__@(subfolder)__@(type_name)__init(&mut msg as *mut _) { + panic!("Call to @(package_name)__@(subfolder)__@(type_name)__init() failed"); + } + msg + } + } +} + + + +impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { + fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: libc::size_t) -> bool { + // SAFETY: This is safe since a the point is guaranteed to be valid/initialized. + unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq as *mut _, size) } + } + fn sequence_fini(seq: &mut rosidl_runtime_rs::Sequence) { + // SAFETY: This is safe since a the point is guaranteed to be valid/initialized. + unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq as *mut _) } + } + fn sequence_copy(in_seq: &rosidl_runtime_rs::Sequence, out_seq: &mut rosidl_runtime_rs::Sequence) -> bool { + out_seq.resize_to_at_least(in_seq.len()); + out_seq.clone_from_slice(in_seq.as_slice()); + true + } +} + +impl rosidl_runtime_rs::Message for @(type_name) { + type RmwMsg = Self; + fn into_rmw_message(msg_cow: std::borrow::Cow<'_, Self>) -> std::borrow::Cow<'_, Self::RmwMsg> { msg_cow } + fn from_rmw_message(msg: Self::RmwMsg) -> Self { msg } +} + +impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { + fn get_type_support() -> libc::uintptr_t { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } +} + +@[end for] diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em index e69de29..9a1ea31 100644 --- a/rosidl_generator_rs/resource/srv.rs.em +++ b/rosidl_generator_rs/resource/srv.rs.em @@ -0,0 +1,80 @@ +@{ +req_res_specs = [] + +for subfolder, service in srv_specs: + req_res_specs.append((subfolder, service.request_message)) + req_res_specs.append((subfolder, service.response_message)) +}@ + +@{ +TEMPLATE( + 'msg_idiomatic.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=req_res_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + get_idiomatic_rs_type=get_idiomatic_rs_type) +}@ + +@[for subfolder, srv_spec in srv_specs] + +@{ +type_name = srv_spec.namespaced_type.name +}@ + +#[link(name = "@(package_name)__rosidl_typesupport_c")] +extern "C" { + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; +} + +// Corresponds to @(package_name)__@(subfolder)__@(type_name) +pub struct @(type_name); + +impl rosidl_runtime_rs::Service for @(type_name) { + type Request = crate::@(subfolder)::@(type_name)_Request; + type Response = crate::@(subfolder)::@(type_name)_Response; + + fn get_type_support() -> libc::uintptr_t { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } +} + +@[end for] + +pub mod rmw { +@{ +TEMPLATE( + 'msg_rmw.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=req_res_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + get_idiomatic_rs_type=get_idiomatic_rs_type) +}@ + +@[for subfolder, srv_spec in srv_specs] + +@{ +type_name = srv_spec.namespaced_type.name +}@ + + #[link(name = "@(package_name)__rosidl_typesupport_c")] + extern "C" { + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; + } + + // Corresponds to @(package_name)__@(subfolder)__@(type_name) + pub struct @(type_name); + + impl rosidl_runtime_rs::Service for @(type_name) { + type Request = crate::@(subfolder)::rmw::@(type_name)_Request; + type Response = crate::@(subfolder)::rmw::@(type_name)_Response; + + fn get_type_support() -> libc::uintptr_t { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } + } + +@[end for] + +} // mod rmw diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index d0e3c6a..84155f9 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -115,6 +115,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'srv_specs': [], 'package_name': args['package_name'], 'typesupport_impls': typesupport_impls, + 'interface_path': idl_rel_path, } latest_target_timestamp = get_newest_modification_time( From 850fa4830b6c0e8d3bd1a2d1321bd9f6d4245ec9 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Fri, 15 Jul 2022 16:19:36 +0200 Subject: [PATCH 15/32] Fix path handling in rosidl_generator_rs on Windows (#228) Paths on Windows can contain colons. With rsplit, the drive letter was grouped with the package name. --- rosidl_generator_rs/rosidl_generator_rs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 84155f9..955f9ae 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -64,7 +64,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): (Path(args['output_dir']) / 'rust/src').mkdir(parents=True, exist_ok=True) for dep_tuple in args.get('ros_interface_dependencies', []): - dep_parts = dep_tuple.rsplit(':', 1) + dep_parts = dep_tuple.split(':', 1) assert len(dep_parts) == 2 if dep_parts[0] != package_name: dependency_packages.add(dep_parts[0]) From 8a87cac7623b3495ecc04ec5d4c7629978994951 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Sun, 31 Jul 2022 13:58:50 +0200 Subject: [PATCH 16/32] Small bugfix for sequences of WStrings (#240) Message packages containing unbounded sequences of WStrings, like test_msgs, would not compile because of this. --- rosidl_generator_rs/resource/msg_idiomatic.rs.em | 6 +++--- rosidl_generator_rs/rosidl_generator_rs/__init__.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rosidl_generator_rs/resource/msg_idiomatic.rs.em b/rosidl_generator_rs/resource/msg_idiomatic.rs.em index 832b000..5466b68 100644 --- a/rosidl_generator_rs/resource/msg_idiomatic.rs.em +++ b/rosidl_generator_rs/resource/msg_idiomatic.rs.em @@ -67,7 +67,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { @# @# == UnboundedSequence == @[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) .into_iter() .map(|elem| elem.as_str().into()) @@ -127,7 +127,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { @# @# == UnboundedSequence == @[ elif isinstance(member.type, UnboundedSequence)]@ -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) .iter() .map(|elem| elem.as_str().into()) @@ -183,7 +183,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { @[ elif isinstance(member.type, UnboundedSequence)]@ @(get_rs_name(member.name)): msg.@(get_rs_name(member.name)) .into_iter() -@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type, UnboundedWString)]@ +@[ if isinstance(member.type.value_type, UnboundedString) or isinstance(member.type.value_type, UnboundedWString)]@ .map(|elem| elem.to_string()) @[ elif isinstance(member.type.value_type, NamedType) or isinstance(member.type.value_type, NamespacedType)]@ .map(@(get_idiomatic_rs_type(member.type.value_type))::from_rmw_message) diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 955f9ae..479d912 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -295,7 +295,7 @@ def get_idiomatic_rs_type(type_): if isinstance(type_, UnboundedString) or isinstance(type_, UnboundedWString): return 'std::string::String' elif isinstance(type_, UnboundedSequence): - return 'Vec::<{}>'.format(get_idiomatic_rs_type(type_.value_type)) + return 'Vec<{}>'.format(get_idiomatic_rs_type(type_.value_type)) elif isinstance(type_, NamespacedType): return '::'.join(type_.namespaced_name()).replace(package_name, 'crate') elif isinstance(type_, Array): From 7f5faa93dbfa16312b09ba47d008d65900097b76 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Fri, 30 Sep 2022 12:57:21 +0200 Subject: [PATCH 17/32] Add support for constants to message generation (#269) This will produce: ``` impl VariousTypes { /// binary, hexadecimal and octal constants are also possible pub const TWO_PLUS_TWO: i8 = 5; /// Only unbounded strings are possible pub const PASSWORD: &'static str = "hunter2"; /// As determined by Edward J. Goodwin pub const PI: f32 = 3.0; } ``` --- rosidl_generator_rs/resource/msg.rs.em | 6 ++-- .../resource/msg_idiomatic.rs.em | 25 ++++++++++++++++ rosidl_generator_rs/resource/msg_rmw.rs.em | 29 +++++++++++++++++-- rosidl_generator_rs/resource/srv.rs.em | 6 ++-- .../rosidl_generator_rs/__init__.py | 26 ++++------------- 5 files changed, 65 insertions(+), 27 deletions(-) diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 43c0996..8e99957 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -5,7 +5,8 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=msg_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - get_idiomatic_rs_type=get_idiomatic_rs_type) + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) }@ } // mod rmw @@ -15,5 +16,6 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=msg_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - get_idiomatic_rs_type=get_idiomatic_rs_type) + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) }@ diff --git a/rosidl_generator_rs/resource/msg_idiomatic.rs.em b/rosidl_generator_rs/resource/msg_idiomatic.rs.em index 5466b68..b1974a3 100644 --- a/rosidl_generator_rs/resource/msg_idiomatic.rs.em +++ b/rosidl_generator_rs/resource/msg_idiomatic.rs.em @@ -1,4 +1,5 @@ @{ +from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence @@ -29,6 +30,30 @@ pub struct @(type_name) { @[end for]@ } +@[if msg_spec.constants]@ +impl @(type_name) { +@[for constant in msg_spec.constants]@ +@{ +comments = getattr(constant, 'get_comment_lines', lambda: [])() +}@ +@[ for line in comments]@ +@[ if line]@ + /// @(line) +@[ else]@ + /// +@[ end if]@ +@[ end for]@ +@[ if isinstance(constant.type, BasicType)]@ + pub const @(get_rs_name(constant.name)): @(get_rmw_rs_type(constant.type)) = @(constant_value_to_rs(constant.type, constant.value)); +@[ elif isinstance(constant.type, AbstractGenericString)]@ + pub const @(get_rs_name(constant.name)): &'static str = @(constant_value_to_rs(constant.type, constant.value)); +@[ else]@ +@{assert False, 'Unhandled constant type: ' + str(constant.type)}@ +@[ end if]@ +@[end for]@ +} +@[end if] + impl Default for @(type_name) { fn default() -> Self { @# This has the benefit of automatically setting the right default values diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em index 17d65cb..ef34567 100644 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -1,4 +1,5 @@ @{ +from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence @@ -11,7 +12,7 @@ from rosidl_parser.definition import UnboundedWString }@ #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; - @[for subfolder, msg_spec in msg_specs]@ +@[for subfolder, msg_spec in msg_specs]@ @{ type_name = msg_spec.structure.namespaced_type.name }@ @@ -40,6 +41,30 @@ pub struct @(type_name) { @[end for]@ } +@[if msg_spec.constants]@ +impl @(type_name) { +@[for constant in msg_spec.constants]@ +@{ +comments = getattr(constant, 'get_comment_lines', lambda: [])() +}@ +@[ for line in comments]@ +@[ if line]@ + /// @(line) +@[ else]@ + /// +@[ end if]@ +@[ end for]@ +@[ if isinstance(constant.type, BasicType)]@ + pub const @(get_rs_name(constant.name)): @(get_rmw_rs_type(constant.type)) = @(constant_value_to_rs(constant.type, constant.value)); +@[ elif isinstance(constant.type, AbstractGenericString)]@ + pub const @(get_rs_name(constant.name)): &'static str = @(constant_value_to_rs(constant.type, constant.value)); +@[ else]@ +@{assert False, 'Unhandled constant type: ' + str(constant.type)}@ +@[ end if]@ +@[end for]@ +} +@[end if] + impl Default for @(type_name) { fn default() -> Self { unsafe { @@ -54,8 +79,6 @@ impl Default for @(type_name) { } } - - impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: libc::size_t) -> bool { // SAFETY: This is safe since a the point is guaranteed to be valid/initialized. diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em index 9a1ea31..925b8cd 100644 --- a/rosidl_generator_rs/resource/srv.rs.em +++ b/rosidl_generator_rs/resource/srv.rs.em @@ -12,7 +12,8 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=req_res_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - get_idiomatic_rs_type=get_idiomatic_rs_type) + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) }@ @[for subfolder, srv_spec in srv_specs] @@ -48,7 +49,8 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=req_res_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, - get_idiomatic_rs_type=get_idiomatic_rs_type) + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) }@ @[for subfolder, srv_spec in srv_specs] diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 479d912..679fe96 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -243,28 +243,14 @@ def primitive_value_to_rs(type_, value): def constant_value_to_rs(type_, value): assert value is not None - if type_ == 'bool': - return 'true' if value else 'false' - - if type_ in [ - 'byte', - 'char', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: + if isinstance(type_, BasicType): + if type_.typename == 'boolean': + return 'true' if value else 'false' + elif type_.typename == 'float32': + return '%sf' % value return str(value) - if type_ == 'float32': - return '%sf' % value - - if type_ == 'string': + if isinstance(type_, AbstractGenericString): return '"%s"' % escape_string(value) assert False, "unknown constant type '%s'" % type_ From b0128c425712850e1f3cb4a6ad60c07a83f4902e Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Mon, 3 Oct 2022 15:39:39 +0200 Subject: [PATCH 18/32] Bump package versions to 0.3 (#274) --- rosidl_generator_rs/package.xml | 2 +- rosidl_generator_rs/resource/Cargo.toml.em | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index d4e60a2..b2b6136 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.2.0 + 0.3.0 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index 2aa193b..4c730bf 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -5,7 +5,7 @@ edition = "2021" [dependencies] libc = "0.2" -rosidl_runtime_rs = "*" +rosidl_runtime_rs = "0.3" serde = { version = "1", optional = true, features = ["derive"] } @[for dep in dependency_packages]@ @(dep) = "*" From df75c1903defb5f28479988275413827b0360207 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Wed, 5 Oct 2022 13:48:12 +0200 Subject: [PATCH 19/32] Add TYPE_NAME constant to messages and make error fields public (#277) --- rosidl_generator_rs/resource/msg_rmw.rs.em | 1 + 1 file changed, 1 insertion(+) diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em index ef34567..24d416d 100644 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -102,6 +102,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { } impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { + const TYPE_NAME: &'static str = "@(package_name)/@(subfolder)/@(type_name)"; fn get_type_support() -> libc::uintptr_t { // SAFETY: No preconditions for this function. unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } From 58d852341a2b2fc4040dd302038b2f10f10418a0 Mon Sep 17 00:00:00 2001 From: Nikolai Morin Date: Mon, 17 Oct 2022 10:50:22 +0200 Subject: [PATCH 20/32] Version 0.3.1 (#285) --- rosidl_generator_rs/package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index b2b6136..c12e46c 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.3.0 + 0.3.1 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 From 0db3a63495461d1132707455b83e72f17fabf6ee Mon Sep 17 00:00:00 2001 From: Tatsuro Sakaguchi Date: Wed, 19 Oct 2022 03:16:18 +0900 Subject: [PATCH 21/32] Remove libc dependencies (#284) --- rosidl_generator_rs/resource/Cargo.toml.em | 1 - rosidl_generator_rs/resource/msg_rmw.rs.em | 18 +++++++++--------- rosidl_generator_rs/resource/srv.rs.em | 8 ++++---- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index 4c730bf..110d2c4 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -4,7 +4,6 @@ version = "@(package_version)" edition = "2021" [dependencies] -libc = "0.2" rosidl_runtime_rs = "0.3" serde = { version = "1", optional = true, features = ["derive"] } @[for dep in dependency_packages]@ diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em index 24d416d..b850618 100644 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -19,14 +19,15 @@ type_name = msg_spec.structure.namespaced_type.name #[link(name = "@(package_name)__rosidl_typesupport_c")] extern "C" { - fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; + fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; } #[link(name = "@(package_name)__rosidl_generator_c")] extern "C" { fn @(package_name)__@(subfolder)__@(type_name)__init(msg: *mut @(type_name)) -> bool; - fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: libc::size_t) -> bool; + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>, size: usize) -> bool; fn @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>); + fn @(package_name)__@(subfolder)__@(type_name)__Sequence__copy(in_seq: &rosidl_runtime_rs::Sequence<@(type_name)>, out_seq: *mut rosidl_runtime_rs::Sequence<@(type_name)>) -> bool; } @# Drop is not needed, since the default drop glue does the same as fini here: @@ -80,18 +81,17 @@ impl Default for @(type_name) { } impl rosidl_runtime_rs::SequenceAlloc for @(type_name) { - fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: libc::size_t) -> bool { - // SAFETY: This is safe since a the point is guaranteed to be valid/initialized. + fn sequence_init(seq: &mut rosidl_runtime_rs::Sequence, size: usize) -> bool { + // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__init(seq as *mut _, size) } } fn sequence_fini(seq: &mut rosidl_runtime_rs::Sequence) { - // SAFETY: This is safe since a the point is guaranteed to be valid/initialized. + // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__fini(seq as *mut _) } } fn sequence_copy(in_seq: &rosidl_runtime_rs::Sequence, out_seq: &mut rosidl_runtime_rs::Sequence) -> bool { - out_seq.resize_to_at_least(in_seq.len()); - out_seq.clone_from_slice(in_seq.as_slice()); - true + // SAFETY: This is safe since the pointer is guaranteed to be valid/initialized. + unsafe { @(package_name)__@(subfolder)__@(type_name)__Sequence__copy(in_seq, out_seq as *mut _) } } } @@ -103,7 +103,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { const TYPE_NAME: &'static str = "@(package_name)/@(subfolder)/@(type_name)"; - fn get_type_support() -> libc::uintptr_t { + fn get_type_support() -> *const std::os::raw::c_void { // SAFETY: No preconditions for this function. unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } } diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em index 925b8cd..31cabc7 100644 --- a/rosidl_generator_rs/resource/srv.rs.em +++ b/rosidl_generator_rs/resource/srv.rs.em @@ -24,7 +24,7 @@ type_name = srv_spec.namespaced_type.name #[link(name = "@(package_name)__rosidl_typesupport_c")] extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; } // Corresponds to @(package_name)__@(subfolder)__@(type_name) @@ -34,7 +34,7 @@ impl rosidl_runtime_rs::Service for @(type_name) { type Request = crate::@(subfolder)::@(type_name)_Request; type Response = crate::@(subfolder)::@(type_name)_Response; - fn get_type_support() -> libc::uintptr_t { + fn get_type_support() -> *const std::os::raw::c_void { // SAFETY: No preconditions for this function. unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } } @@ -61,7 +61,7 @@ type_name = srv_spec.namespaced_type.name #[link(name = "@(package_name)__rosidl_typesupport_c")] extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> libc::uintptr_t; + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; } // Corresponds to @(package_name)__@(subfolder)__@(type_name) @@ -71,7 +71,7 @@ type_name = srv_spec.namespaced_type.name type Request = crate::@(subfolder)::rmw::@(type_name)_Request; type Response = crate::@(subfolder)::rmw::@(type_name)_Response; - fn get_type_support() -> libc::uintptr_t { + fn get_type_support() -> *const std::os::raw::c_void { // SAFETY: No preconditions for this function. unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } } From fdc19d33969b319e1b5298da089836cb6541147e Mon Sep 17 00:00:00 2001 From: Sam Privett Date: Thu, 6 Apr 2023 07:13:03 -0700 Subject: [PATCH 22/32] Swapped usage of rosidl_cmake over to the new rosidl_pycommon. (#297) * Swapped usage of rosidl_cmake over to the new rosidl_pycommon. As of [rosidl 3.3.0](https://github.com/ros2/rosidl/commit/9348ce9b466335590dc334aab01f4f0dd270713b), the rosidl_cmake Python module was moved to a new rosidl_pycommon package and the Python module in rosidl_cmake was deprecated. * Support builds from older ROS 2 distros. * Fixed build for rolling * Added `test_depend` conditional inclusion as well. * Swap to a more elegant check * PR Feedback --------- Co-authored-by: Sam Privett --- rosidl_generator_rs/CMakeLists.txt | 7 ++++-- rosidl_generator_rs/package.xml | 10 +++++--- .../rosidl_generator_rs/__init__.py | 25 +++++++++---------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt index 9a29958..64e5577 100644 --- a/rosidl_generator_rs/CMakeLists.txt +++ b/rosidl_generator_rs/CMakeLists.txt @@ -4,12 +4,15 @@ project(rosidl_generator_rs) find_package(ament_cmake REQUIRED) find_package(ament_cmake_python REQUIRED) -find_package(rosidl_cmake REQUIRED) find_package(rosidl_generator_c REQUIRED) find_package(rosidl_typesupport_interface REQUIRED) find_package(rosidl_typesupport_introspection_c REQUIRED) -ament_export_dependencies(rosidl_cmake) +if("$ENV{ROS_DISTRO}" STRLESS_EQUAL "humble") + find_package(rosidl_cmake REQUIRED) + ament_export_dependencies(rosidl_cmake) +endif() + ament_export_dependencies(rmw) ament_export_dependencies(rosidl_generator_c) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index c12e46c..18ff07f 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -9,11 +9,14 @@ Esteve Fernandez ament_cmake - + ros_environment + rosidl_runtime_rs ament_cmake - rosidl_cmake + ros_environment + rosidl_cmake + rosidl_pycommon rosidl_runtime_rs rosidl_typesupport_c rosidl_typesupport_interface @@ -26,7 +29,8 @@ ament_cmake_gtest ament_lint_auto ament_lint_common - rosidl_cmake + rosidl_cmake + rosidl_pycommon rosidl_generator_c rosidl_generator_packages diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 679fe96..ae61a3e 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -18,11 +18,10 @@ from pathlib import Path -from rosidl_cmake import convert_camel_case_to_lower_case_underscore -from rosidl_cmake import expand_template -from rosidl_cmake import generate_files -from rosidl_cmake import get_newest_modification_time -from rosidl_cmake import read_generator_arguments +if os.environ['ROS_DISTRO'] <= 'humble': + import rosidl_cmake as rosidl_pycommon +else: + import rosidl_pycommon from rosidl_parser.definition import AbstractGenericString from rosidl_parser.definition import AbstractNestedType @@ -53,7 +52,7 @@ def convert_lower_case_underscore_to_camel_case(word): def generate_rs(generator_arguments_file, typesupport_impls): - args = read_generator_arguments(generator_arguments_file) + args = rosidl_pycommon.read_generator_arguments(generator_arguments_file) package_name = args['package_name'] # expand init modules for each directory @@ -108,7 +107,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'constant_value_to_rs': constant_value_to_rs, 'value_to_rs': value_to_rs, 'convert_camel_case_to_lower_case_underscore': - convert_camel_case_to_lower_case_underscore, + rosidl_pycommon.convert_camel_case_to_lower_case_underscore, 'convert_lower_case_underscore_to_camel_case': convert_lower_case_underscore_to_camel_case, 'msg_specs': [], @@ -118,7 +117,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'interface_path': idl_rel_path, } - latest_target_timestamp = get_newest_modification_time( + latest_target_timestamp = rosidl_pycommon.get_newest_modification_time( args['target_dependencies']) for message in idl_content.get_elements_of_type(Message): @@ -132,7 +131,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): for generated_filename in generated_filenames: generated_file = os.path.join(args['output_dir'], generated_filename % 'msg') - expand_template( + rosidl_pycommon.expand_template( os.path.join(template_dir, template_file), data.copy(), generated_file, @@ -143,13 +142,13 @@ def generate_rs(generator_arguments_file, typesupport_impls): for generated_filename in generated_filenames: generated_file = os.path.join(args['output_dir'], generated_filename % 'srv') - expand_template( + rosidl_pycommon.expand_template( os.path.join(template_dir, template_file), data.copy(), generated_file, minimum_timestamp=latest_target_timestamp) - expand_template( + rosidl_pycommon.expand_template( os.path.join(template_dir, 'lib.rs.em'), data.copy(), os.path.join(args['output_dir'], 'rust/src/lib.rs'), @@ -160,13 +159,13 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'package_name': args['package_name'], 'package_version': args['package_version'], } - expand_template( + rosidl_pycommon.expand_template( os.path.join(template_dir, 'Cargo.toml.em'), cargo_toml_data, os.path.join(args['output_dir'], 'rust/Cargo.toml'), minimum_timestamp=latest_target_timestamp) - expand_template( + rosidl_pycommon.expand_template( os.path.join(template_dir, 'build.rs.em'), {}, os.path.join(args['output_dir'], 'rust/build.rs'), From 96322cdd0bfd0e6fa522f301d1f654e7e9ecebcf Mon Sep 17 00:00:00 2001 From: Fawdlstty Date: Tue, 22 Aug 2023 19:12:20 +0800 Subject: [PATCH 23/32] add serde big array support (fixed #327) (#328) * add serde big array support --- rosidl_generator_rs/resource/Cargo.toml.em | 3 ++- rosidl_generator_rs/resource/msg.rs.em | 2 ++ rosidl_generator_rs/resource/msg_idiomatic.rs.em | 2 +- rosidl_generator_rs/resource/msg_rmw.rs.em | 2 +- rosidl_generator_rs/resource/srv.rs.em | 2 ++ rosidl_generator_rs/rosidl_generator_rs/__init__.py | 9 +++++++++ 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index 110d2c4..fcf3461 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -6,13 +6,14 @@ edition = "2021" [dependencies] rosidl_runtime_rs = "0.3" serde = { version = "1", optional = true, features = ["derive"] } +serde-big-array = { version = "0.5.1", optional = true } @[for dep in dependency_packages]@ @(dep) = "*" @[end for]@ [features] @{ -serde_features = ["dep:serde", "rosidl_runtime_rs/serde"] +serde_features = ["dep:serde", "dep:serde-big-array", "rosidl_runtime_rs/serde"] for dep in dependency_packages: serde_features.append("{}/serde".format(dep)) }@ diff --git a/rosidl_generator_rs/resource/msg.rs.em b/rosidl_generator_rs/resource/msg.rs.em index 8e99957..3f7d10e 100644 --- a/rosidl_generator_rs/resource/msg.rs.em +++ b/rosidl_generator_rs/resource/msg.rs.em @@ -5,6 +5,7 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=msg_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) }@ @@ -16,6 +17,7 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=msg_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) }@ diff --git a/rosidl_generator_rs/resource/msg_idiomatic.rs.em b/rosidl_generator_rs/resource/msg_idiomatic.rs.em index b1974a3..e6ec288 100644 --- a/rosidl_generator_rs/resource/msg_idiomatic.rs.em +++ b/rosidl_generator_rs/resource/msg_idiomatic.rs.em @@ -26,7 +26,7 @@ type_name = msg_spec.structure.namespaced_type.name #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ - pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), + @(pre_field_serde(member.type))pub @(get_rs_name(member.name)): @(get_idiomatic_rs_type(member.type)), @[end for]@ } diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em index b850618..c4420a6 100644 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -38,7 +38,7 @@ extern "C" { #[derive(Clone, Debug, PartialEq, PartialOrd)] pub struct @(type_name) { @[for member in msg_spec.structure.members]@ - pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), + @(pre_field_serde(member.type))pub @(get_rs_name(member.name)): @(get_rmw_rs_type(member.type)), @[end for]@ } diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em index 31cabc7..369696f 100644 --- a/rosidl_generator_rs/resource/srv.rs.em +++ b/rosidl_generator_rs/resource/srv.rs.em @@ -12,6 +12,7 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=req_res_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) }@ @@ -49,6 +50,7 @@ TEMPLATE( package_name=package_name, interface_path=interface_path, msg_specs=req_res_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) }@ diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index ae61a3e..762fd48 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -101,6 +101,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): 'Services template file %s not found' % template_file data = { + 'pre_field_serde': pre_field_serde, 'get_rmw_rs_type': make_get_rmw_rs_type(args['package_name']), 'get_rs_name': get_rs_name, 'get_idiomatic_rs_type': make_get_idiomatic_rs_type(args['package_name']), @@ -274,6 +275,14 @@ def constant_value_to_rs(type_, value): # - BoundedSequence # - UnboundedSequence + +def pre_field_serde(type_): + if isinstance(type_, Array) and type_.size > 32: + return '#[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))]\n ' + else: + return '' + + def make_get_idiomatic_rs_type(package_name): get_rmw_rs_type = make_get_rmw_rs_type(package_name) def get_idiomatic_rs_type(type_): From 76dde3915ed0333427f76af530155c211124febc Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:38:55 +0100 Subject: [PATCH 24/32] Version 0.4.0 (#343) Signed-off-by: Esteve Fernandez --- rosidl_generator_rs/package.xml | 2 +- rosidl_generator_rs/resource/Cargo.toml.em | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index 18ff07f..0cad229 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.3.1 + 0.4.0 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index fcf3461..7c8f7fe 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -4,7 +4,7 @@ version = "@(package_version)" edition = "2021" [dependencies] -rosidl_runtime_rs = "0.3" +rosidl_runtime_rs = "0.4" serde = { version = "1", optional = true, features = ["derive"] } serde-big-array = { version = "0.5.1", optional = true } @[for dep in dependency_packages]@ From 227a6ff3101d601b4c179a219f8c6f38205c4ce5 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Tue, 7 Nov 2023 17:11:56 +0100 Subject: [PATCH 25/32] Revert "Version 0.4.0 (#343)" (#344) This reverts commit a64e397990319db39caf79ef7863b21fb2c828ea. --- rosidl_generator_rs/package.xml | 2 +- rosidl_generator_rs/resource/Cargo.toml.em | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index 0cad229..18ff07f 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.4.0 + 0.3.1 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index 7c8f7fe..fcf3461 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -4,7 +4,7 @@ version = "@(package_version)" edition = "2021" [dependencies] -rosidl_runtime_rs = "0.4" +rosidl_runtime_rs = "0.3" serde = { version = "1", optional = true, features = ["derive"] } serde-big-array = { version = "0.5.1", optional = true } @[for dep in dependency_packages]@ From 90f3ab9e84c96470214c6b41972fc41d7ac55cd8 Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:19:40 +0100 Subject: [PATCH 26/32] Version 0.4.0 (#346) --- rosidl_generator_rs/package.xml | 2 +- rosidl_generator_rs/resource/Cargo.toml.em | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index 18ff07f..0cad229 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.3.1 + 0.4.0 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 diff --git a/rosidl_generator_rs/resource/Cargo.toml.em b/rosidl_generator_rs/resource/Cargo.toml.em index fcf3461..7c8f7fe 100644 --- a/rosidl_generator_rs/resource/Cargo.toml.em +++ b/rosidl_generator_rs/resource/Cargo.toml.em @@ -4,7 +4,7 @@ version = "@(package_version)" edition = "2021" [dependencies] -rosidl_runtime_rs = "0.3" +rosidl_runtime_rs = "0.4" serde = { version = "1", optional = true, features = ["derive"] } serde-big-array = { version = "0.5.1", optional = true } @[for dep in dependency_packages]@ From 7f888ff731eb78f935c0b75d15ba923fd424d09c Mon Sep 17 00:00:00 2001 From: Esteve Fernandez <33620+esteve@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:49:19 +0100 Subject: [PATCH 27/32] Version 0.4.1 (#353) --- rosidl_generator_rs/package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosidl_generator_rs/package.xml b/rosidl_generator_rs/package.xml index 0cad229..bd6124c 100644 --- a/rosidl_generator_rs/package.xml +++ b/rosidl_generator_rs/package.xml @@ -2,7 +2,7 @@ rosidl_generator_rs - 0.4.0 + 0.4.1 Generate the ROS interfaces in Rust. Esteve Fernandez Apache License 2.0 From b122fea4fc68863ce43b71fd1ac6cc1eedacfed3 Mon Sep 17 00:00:00 2001 From: Sam Privett Date: Tue, 9 Jan 2024 07:07:10 -0800 Subject: [PATCH 28/32] Add wchar support (#349) * Add wchar support and .idl example * Undo automatic IDE formatting noise * Added back unused imports to see if this fixes the build * More attempts to fix the weird build failure * Removed the linter tests for auto-generated message source files in `rclrs_example_msgs`. Re-applied some changes removed when root causing. --------- Co-authored-by: Sam Privett --- rosidl_generator_rs/rosidl_generator_rs/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 762fd48..502d1d3 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -14,7 +14,6 @@ import os import pathlib -import subprocess from pathlib import Path @@ -24,12 +23,7 @@ import rosidl_pycommon from rosidl_parser.definition import AbstractGenericString -from rosidl_parser.definition import AbstractNestedType -from rosidl_parser.definition import AbstractSequence -from rosidl_parser.definition import AbstractString -from rosidl_parser.definition import AbstractWString from rosidl_parser.definition import Array -from rosidl_parser.definition import BASIC_TYPES from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence from rosidl_parser.definition import BoundedString @@ -219,6 +213,7 @@ def primitive_value_to_rs(type_, value): if type_.type in [ 'byte', 'char', + 'wchar', 'int8', 'uint8', 'int16', @@ -311,6 +306,8 @@ def get_rmw_rs_type(type_): return 'u8' elif type_.typename == 'char': return 'u8' + elif type_.typename == 'wchar': + return 'u16' elif type_.typename == 'float': return 'f32' elif type_.typename == 'double': From 40e697168a53f67e590c1f931f8212a87ecf470f Mon Sep 17 00:00:00 2001 From: "Michael X. Grey" Date: Tue, 12 Mar 2024 11:36:02 +0000 Subject: [PATCH 29/32] Allow ros2_rust to be built within a distro workspace Signed-off-by: Michael X. Grey --- rosidl_generator_rs/CMakeLists.txt | 4 +- ...idl_generator_rs_generate_interfaces.cmake | 43 +++---------------- ...rosidl_generator_rs_get_typesupports.cmake | 30 ------------- 3 files changed, 8 insertions(+), 69 deletions(-) delete mode 100644 rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake diff --git a/rosidl_generator_rs/CMakeLists.txt b/rosidl_generator_rs/CMakeLists.txt index 64e5577..fd7d1c1 100644 --- a/rosidl_generator_rs/CMakeLists.txt +++ b/rosidl_generator_rs/CMakeLists.txt @@ -22,8 +22,8 @@ ament_index_register_resource("rosidl_runtime_packages") ament_python_install_package(${PROJECT_NAME}) ament_package( - CONFIG_EXTRAS "rosidl_generator_rs-extras.cmake.in" - "cmake/rosidl_generator_rs_get_typesupports.cmake" + CONFIG_EXTRAS + "rosidl_generator_rs-extras.cmake.in" "cmake/register_rs.cmake" ) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index fd48a55..051ccd8 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -12,9 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -find_package(rmw_implementation_cmake REQUIRED) -find_package(rmw REQUIRED) - if(NOT WIN32) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") @@ -23,17 +20,8 @@ if(NOT WIN32) endif() endif() -# Get a list of typesupport implementations from valid rmw implementations. -rosidl_generator_rs_get_typesupports(_typesupport_impls) - -if(_typesupport_impls STREQUAL "") - message(WARNING "No valid typesupport for Rust generator. Rust messages will not be generated.") - return() -endif() - set(_output_path "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_rs/${PROJECT_NAME}") -set(_generated_extension_files "") set(_generated_common_rs_files "") set(_generated_msg_rs_files "") @@ -42,10 +30,6 @@ set(_generated_srv_rs_files "") set(_has_msg FALSE) set(_has_srv FALSE) -foreach(_typesupport_impl ${_typesupport_impls}) - set(_generated_extension_${_typesupport_impl}_files "") -endforeach() - foreach(_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) get_filename_component(_parent_folder "${_parent_folder}" NAME) @@ -75,22 +59,12 @@ if(${_has_msg}) list(APPEND _generated_msg_rs_files "${_output_path}/rust/src/msg.rs" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_extension_${_typesupport_impl}_files "${_output_path}/msg_rs.ep.${_typesupport_impl}.c") - list_append_unique(_generated_extension_files "${_generated_extension_${_typesupport_impl}_files}") - endforeach() endif() if(${_has_srv}) list(APPEND _generated_srv_rs_files "${_output_path}/rust/src/srv.rs" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_extension_${_typesupport_impl}_files "${_output_path}/srv_rs.ep.${_typesupport_impl}.c") - list_append_unique(_generated_extension_files "${_generated_extension_${_typesupport_impl}_files}") - endforeach() endif() set(_dependency_files "") @@ -156,23 +130,18 @@ add_dependencies(${rosidl_generate_interfaces_TARGET} ${rosidl_generate_interfac set_property( SOURCE - ${_generated_extension_files} ${_generated_common_rs_files} ${_generated_msg_rs_files} ${_generated_srv_rs_files} PROPERTY GENERATED 1) set(_rsext_suffix "__rsext") -foreach(_typesupport_impl ${_typesupport_impls}) - find_package(${_typesupport_impl} REQUIRED) - - if(NOT rosidl_generate_interfaces_SKIP_INSTALL) - install( - DIRECTORY "${_output_path}/rust" - DESTINATION "share/${PROJECT_NAME}" - ) - endif() -endforeach() +if(NOT rosidl_generate_interfaces_SKIP_INSTALL) + install( + DIRECTORY "${_output_path}/rust" + DESTINATION "share/${PROJECT_NAME}" + ) +endif() if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake deleted file mode 100644 index 37d3c35..0000000 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_get_typesupports.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2016-2017 Open Source Robotics Foundation, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -macro(accumulate_typesupports) - set(_typesupport_impl_tmp "") - get_rmw_typesupport(_typesupport_impl_tmp ${rmw_implementation} LANGUAGE "C") - list(APPEND _typesupport_impls_tmp ${_typesupport_impl_tmp}) -endmacro() - -macro(rosidl_generator_rs_get_typesupports TYPESUPPORT_IMPLS) - set(${TYPESUPPORT_IMPLS} "") - set(_typesupport_impls_tmp "") - set(_typesupport_impls_tmp_unique "") - call_for_each_rmw_implementation(accumulate_typesupports) - - foreach(_typesupport_impl ${_typesupport_impls_tmp}) - list_append_unique(${TYPESUPPORT_IMPLS} ${_typesupport_impl}) - endforeach() -endmacro() From c6dba8ff21f25ca0fced2159ee6d7741866c4a1a Mon Sep 17 00:00:00 2001 From: Grey Date: Thu, 21 Mar 2024 04:59:32 +0800 Subject: [PATCH 30/32] Declare rust_packages only when installing Rust IDL bindings (#380) Signed-off-by: Michael X. Grey --- .../cmake/rosidl_generator_rs_generate_interfaces.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 051ccd8..9cfdfa5 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -115,9 +115,6 @@ file(MAKE_DIRECTORY "${_output_path}") set(_target_suffix "__rs") -ament_index_register_resource("rust_packages") - - # needed to avoid multiple calls to the Rust generator trick copied from # https://github.com/ros2/rosidl/blob/master/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake set(_subdir "${CMAKE_CURRENT_BINARY_DIR}/${rosidl_generate_interfaces_TARGET}${_target_suffix}") @@ -137,6 +134,7 @@ set_property( set(_rsext_suffix "__rsext") if(NOT rosidl_generate_interfaces_SKIP_INSTALL) + ament_index_register_resource("rust_packages") install( DIRECTORY "${_output_path}/rust" DESTINATION "share/${PROJECT_NAME}" From f59d00dd71d4a0cd8b3ade496e24bae15fbc2825 Mon Sep 17 00:00:00 2001 From: Nathan Wiebe Neufeldt Date: Sat, 26 Oct 2024 14:11:57 -0400 Subject: [PATCH 31/32] Action message support (#417) * Added action template * Added action generation * Added basic create_action_client function * dded action generation * checkin * Fix missing exported pre_field_serde field * Removed extra code * Sketch out action server construction and destruction This follows generally the same pattern as the service server. It required adding a typesupport function to the Action trait and pulling in some more rcl_action bindings. * Fix action typesupport function * Add ActionImpl trait with internal messages and services This is incomplete, since the service types aren't yet being generated. * Split srv.rs.em into idiomatic and rmw template files This results in the exact same file being produced for services, except for some whitespace changes. However, it enables actions to invoke the respective service template for its generation, similar to how the it works for services and their underlying messages. * Generate underlying service definitions for actions Not tested * Add runtime trait to get the UUID from a goal request C++ uses duck typing for this, knowing that for any `Action`, the type `Action::Impl::SendGoalService::Request` will always have a `goal_id` field of type `unique_identifier_msgs::msg::UUID` without having to prove this to the compiler. Rust's generics are more strict, requiring that this be proven using type bounds. The `Request` type is also action-specific as it contains a `goal` field containing the `Goal` message type of the action. We therefore cannot enforce that all `Request`s are a specific type in `rclrs`. This seems most easily represented using associated type bounds on the `SendGoalService` associated type within `ActionImpl`. To avoid introducing to `rosidl_runtime_rs` a circular dependency on message packages like `unique_identifier_msgs`, the `ExtractUuid` trait only operates on a byte array rather than a more nicely typed `UUID` message type. I'll likely revisit this as we introduce more similar bounds on the generated types. * Integrate RMW message methods into ActionImpl Rather than having a bunch of standalone traits implementing various message functions like `ExtractUuid` and `SetAccepted`, with the trait bounds on each associated type in `ActionImpl`, we'll instead add these functions directly to the `ActionImpl` trait. This is simpler on both the rosidl_runtime_rs and the rclrs side. * Add rosidl_runtime_rs::ActionImpl::create_feedback_message() Adds a trait method to create a feedback message given the goal ID and the user-facing feedback message type. Depending on how many times we do this, it may end up valuable to define a GoalUuid type in rosidl_runtime_rs itself. We wouldn't be able to utilize the `RCL_ACTION_UUID_SIZE` constant imported from `rcl_action`, but this is pretty much guaranteed to be 16 forever. Defining this method signature also required inverting the super-trait relationship between Action and ActionImpl. Now ActionImpl is the sub-trait as this gives it access to all of Action's associated types. Action doesn't need to care about anything from ActionImpl (hopefully). * Add GetResultService methods to ActionImpl * Implement ActionImpl trait methods in generator These still don't build without errors, but it's close. * Replace set_result_response_status with create_result_response rclrs needs to be able to generically construct result responses, including the user-defined result field. * Implement client-side trait methods for action messages This adds methods to ActionImpl for creating and accessing action-specific message types. These are needed by the rclrs ActionClient to generically read and write RMW messages. Due to issues with qualified paths in certain places (https://github.com/rust-lang/rust/issues/86935), the generator now refers directly to its service and message types rather than going through associated types of the various traits. This also makes the generated code a little easier to read, with the trait method signatures from rosidl_runtime_rs still enforcing type-safety. * Format the rosidl_runtime_rs::ActionImpl trait * Wrap longs lines in rosidl_generator_rs action.rs This at least makes the template easier to read, but also helps with the generated code. In general, the generated code could actually fit on one line for the function signatures, but it's not a big deal to split it across multiple. * Use idiomatic message types in Action trait This is user-facing and so should use the friendly message types. * Cleanup ActionImpl using type aliases Signed-off-by: Michael X. Grey * Formatting * Switch from std::os::raw::c_void to std::ffi::c_void While these are aliases of each other, we might as well use the more appropriate std::ffi version, as requested by reviewers. * Clean up rosidl_generator_rs's cmake files Some of the variables are present but no longer used. Others were not updated with the action changes. * Add a short doc page on the message generation pipeline This should help newcomers orient themselves around the rosidl_*_rs packages. --------- Signed-off-by: Michael X. Grey Co-authored-by: Esteve Fernandez Co-authored-by: Michael X. Grey --- .../cmake/custom_command.cmake | 8 +- ...idl_generator_rs_generate_interfaces.cmake | 25 ++- rosidl_generator_rs/resource/action.rs.em | 205 ++++++++++++++++++ rosidl_generator_rs/resource/lib.rs.em | 4 + rosidl_generator_rs/resource/msg_rmw.rs.em | 4 +- rosidl_generator_rs/resource/srv.rs.em | 69 +----- .../resource/srv_idiomatic.rs.em | 44 ++++ rosidl_generator_rs/resource/srv_rmw.rs.em | 44 ++++ .../rosidl_generator_rs/__init__.py | 27 +++ 9 files changed, 351 insertions(+), 79 deletions(-) create mode 100644 rosidl_generator_rs/resource/action.rs.em create mode 100644 rosidl_generator_rs/resource/srv_idiomatic.rs.em create mode 100644 rosidl_generator_rs/resource/srv_rmw.rs.em diff --git a/rosidl_generator_rs/cmake/custom_command.cmake b/rosidl_generator_rs/cmake/custom_command.cmake index a3f3ff9..d16ccd8 100644 --- a/rosidl_generator_rs/cmake/custom_command.cmake +++ b/rosidl_generator_rs/cmake/custom_command.cmake @@ -14,12 +14,10 @@ add_custom_command( OUTPUT - ${_generated_extension_files} ${_generated_common_rs_files} ${_generated_msg_rs_files} - ${_generated_msg_c_files} ${_generated_srv_rs_files} - ${_generated_srv_c_files} + ${_generated_action_rs_files} COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_rs_BIN} --generator-arguments-file "${generator_arguments_file}" --typesupport-impls "${_typesupport_impls}" @@ -34,11 +32,9 @@ else() add_custom_target( ${rosidl_generate_interfaces_TARGET}${_target_suffix} ALL DEPENDS - ${_generated_extension_files} ${_generated_common_rs_files} ${_generated_msg_rs_files} - ${_generated_msg_c_files} ${_generated_srv_rs_files} - ${_generated_srv_c_files} + ${_generated_action_rs_files} ) endif() diff --git a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake index 9cfdfa5..af42061 100644 --- a/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake +++ b/rosidl_generator_rs/cmake/rosidl_generator_rs_generate_interfaces.cmake @@ -26,9 +26,11 @@ set(_generated_common_rs_files "") set(_generated_msg_rs_files "") set(_generated_srv_rs_files "") +set(_generated_action_rs_files "") set(_has_msg FALSE) set(_has_srv FALSE) +set(_has_action FALSE) foreach(_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) @@ -37,13 +39,13 @@ foreach(_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) if(_parent_folder STREQUAL "msg") set(_has_msg TRUE) - set(_idl_file_without_actions ${_idl_file_without_actions} ${_idl_file}) + set(_idl_files ${_idl_files} ${_idl_file}) elseif(_parent_folder STREQUAL "srv") set(_has_srv TRUE) - set(_idl_file_without_actions ${_idl_file_without_actions} ${_idl_file}) + set(_idl_files ${_idl_files} ${_idl_file}) elseif(_parent_folder STREQUAL "action") set(_has_action TRUE) - message(WARNING "Rust actions generation is not implemented") + set(_idl_files ${_idl_files} ${_idl_file}) else() message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") endif() @@ -67,6 +69,12 @@ if(${_has_srv}) ) endif() +if(${_has_action}) + list(APPEND _generated_action_rs_files + "${_output_path}/rust/src/action.rs" + ) +endif() + set(_dependency_files "") set(_dependencies "") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) @@ -81,12 +89,15 @@ endforeach() set(target_dependencies "${rosidl_generator_rs_BIN}" ${rosidl_generator_rs_GENERATOR_FILES} + "${rosidl_generator_rs_TEMPLATE_DIR}/action.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg_idiomatic.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg_rmw.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/msg.rs.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/srv_idiomatic.rs.em" + "${rosidl_generator_rs_TEMPLATE_DIR}/srv_rmw.rs.em" "${rosidl_generator_rs_TEMPLATE_DIR}/srv.rs.em" ${rosidl_generate_interfaces_ABS_IDL_FILES} - ${_idl_file_without_actions} + ${_idl_files} ${_dependency_files}) foreach(dep ${target_dependencies}) if(NOT EXISTS "${dep}") @@ -99,7 +110,7 @@ rosidl_write_generator_arguments( "${generator_arguments_file}" PACKAGE_NAME "${PROJECT_NAME}" IDL_TUPLES "${rosidl_generate_interfaces_IDL_TUPLES}" - ROS_INTERFACE_FILES "${_idl_file_without_actions}" + ROS_INTERFACE_FILES "${_idl_files}" ROS_INTERFACE_DEPENDENCIES "${_dependencies}" OUTPUT_DIR "${_output_path}" TEMPLATE_DIR "${rosidl_generator_rs_TEMPLATE_DIR}" @@ -130,6 +141,7 @@ set_property( ${_generated_common_rs_files} ${_generated_msg_rs_files} ${_generated_srv_rs_files} + ${_generated_action_rs_files} PROPERTY GENERATED 1) set(_rsext_suffix "__rsext") @@ -144,7 +156,8 @@ endif() if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( NOT _generated_msg_rs_files STREQUAL "" OR - NOT _generated_srv_rs_files STREQUAL "" + NOT _generated_srv_rs_files STREQUAL "" OR + NOT _generated_action_rs_files STREQUAL "" ) # TODO(esteve): add linters for Rust files endif() diff --git a/rosidl_generator_rs/resource/action.rs.em b/rosidl_generator_rs/resource/action.rs.em new file mode 100644 index 0000000..5f463f3 --- /dev/null +++ b/rosidl_generator_rs/resource/action.rs.em @@ -0,0 +1,205 @@ +@{ +from rosidl_parser.definition import ( + ACTION_FEEDBACK_MESSAGE_SUFFIX, + ACTION_FEEDBACK_SUFFIX, + ACTION_GOAL_SERVICE_SUFFIX, + ACTION_GOAL_SUFFIX, + ACTION_RESULT_SERVICE_SUFFIX, + ACTION_RESULT_SUFFIX, + SERVICE_REQUEST_MESSAGE_SUFFIX, + SERVICE_RESPONSE_MESSAGE_SUFFIX, +) + +action_msg_specs = [] + +for subfolder, action in action_specs: + action_msg_specs.append((subfolder, action.goal)) + action_msg_specs.append((subfolder, action.result)) + action_msg_specs.append((subfolder, action.feedback)) + action_msg_specs.append((subfolder, action.feedback_message)) + +action_srv_specs = [] + +for subfolder, action in action_specs: + action_srv_specs.append((subfolder, action.send_goal_service)) + action_srv_specs.append((subfolder, action.get_result_service)) +}@ + +pub mod rmw { +@{ +TEMPLATE( + 'msg_rmw.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=action_msg_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) + +TEMPLATE( + 'srv_rmw.rs.em', + package_name=package_name, interface_path=interface_path, + srv_specs=action_srv_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) +}@ +} // mod rmw + +@{ +TEMPLATE( + 'msg_idiomatic.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=action_msg_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) +}@ + +@{ +TEMPLATE( + 'srv_idiomatic.rs.em', + package_name=package_name, interface_path=interface_path, + srv_specs=action_srv_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) +}@ + +@[for subfolder, action_spec in action_specs] + +@{ +type_name = action_spec.namespaced_type.name +}@ + +#[link(name = "@(package_name)__rosidl_typesupport_c")] +extern "C" { + fn rosidl_typesupport_c__get_action_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; +} + +// Corresponds to @(package_name)__@(subfolder)__@(type_name) +pub struct @(type_name); + +impl rosidl_runtime_rs::Action for @(type_name) { + type Goal = crate::@(subfolder)::@(type_name)@(ACTION_GOAL_SUFFIX); + type Result = crate::@(subfolder)::@(type_name)@(ACTION_RESULT_SUFFIX); + type Feedback = crate::@(subfolder)::@(type_name)@(ACTION_FEEDBACK_SUFFIX); + + fn get_type_support() -> *const std::ffi::c_void { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_action_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } +} + +impl rosidl_runtime_rs::ActionImpl for @(type_name) { + type GoalStatusMessage = action_msgs::msg::rmw::GoalStatusArray; + type FeedbackMessage = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX); + + type SendGoalService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX); + type CancelGoalService = action_msgs::srv::rmw::CancelGoal; + type GetResultService = crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX); + + fn create_goal_request( + goal_id: &[u8; 16], + goal: crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SUFFIX), + ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { + crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { + goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id }, + goal, + } + } + + fn get_goal_request_uuid( + request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX), + ) -> &[u8; 16] { + &request.goal_id.uuid + } + + fn create_goal_response( + accepted: bool, + stamp: (i32, u32), + ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { + crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { + accepted, + stamp: builtin_interfaces::msg::rmw::Time { + sec: stamp.0, + nanosec: stamp.1, + }, + } + } + + fn get_goal_response_accepted( + response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), + ) -> bool { + response.accepted + } + + fn get_goal_response_stamp( + response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_GOAL_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), + ) -> (i32, u32) { + (response.stamp.sec, response.stamp.nanosec) + } + + fn create_feedback_message( + goal_id: &[u8; 16], + feedback: crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX), + ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX) { + let mut message = crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX)::default(); + message.goal_id.uuid = *goal_id; + message.feedback = feedback; + message + } + + fn get_feedback_message_uuid( + feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX), + ) -> &[u8; 16] { + &feedback.goal_id.uuid + } + + fn get_feedback_message_feedback( + feedback: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_MESSAGE_SUFFIX), + ) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_FEEDBACK_SUFFIX) { + &feedback.feedback + } + + fn create_result_request( + goal_id: &[u8; 16], + ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { + crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX) { + goal_id: unique_identifier_msgs::msg::rmw::UUID { uuid: *goal_id }, + } + } + + fn get_result_request_uuid( + request: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_REQUEST_MESSAGE_SUFFIX), + ) -> &[u8; 16] { + &request.goal_id.uuid + } + + fn create_result_response( + status: i8, + result: crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX), + ) -> crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { + crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX) { + status, + result, + } + } + + fn get_result_response_result( + response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), + ) -> &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SUFFIX) { + &response.result + } + + fn get_result_response_status( + response: &crate::@(subfolder)::rmw::@(type_name)@(ACTION_RESULT_SERVICE_SUFFIX)@(SERVICE_RESPONSE_MESSAGE_SUFFIX), + ) -> i8 { + response.status + } +} + +@[end for] diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em index 51e4a5b..79a0e1d 100644 --- a/rosidl_generator_rs/resource/lib.rs.em +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -7,3 +7,7 @@ pub mod msg; @[if len(srv_specs) > 0]@ pub mod srv; @[end if]@ + +@[if len(action_specs) > 0]@ +pub mod action; +@[end if]@ diff --git a/rosidl_generator_rs/resource/msg_rmw.rs.em b/rosidl_generator_rs/resource/msg_rmw.rs.em index c4420a6..fbedd6d 100644 --- a/rosidl_generator_rs/resource/msg_rmw.rs.em +++ b/rosidl_generator_rs/resource/msg_rmw.rs.em @@ -19,7 +19,7 @@ type_name = msg_spec.structure.namespaced_type.name #[link(name = "@(package_name)__rosidl_typesupport_c")] extern "C" { - fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; + fn rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; } #[link(name = "@(package_name)__rosidl_generator_c")] @@ -103,7 +103,7 @@ impl rosidl_runtime_rs::Message for @(type_name) { impl rosidl_runtime_rs::RmwMessage for @(type_name) where Self: Sized { const TYPE_NAME: &'static str = "@(package_name)/@(subfolder)/@(type_name)"; - fn get_type_support() -> *const std::os::raw::c_void { + fn get_type_support() -> *const std::ffi::c_void { // SAFETY: No preconditions for this function. unsafe { rosidl_typesupport_c__get_message_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } } diff --git a/rosidl_generator_rs/resource/srv.rs.em b/rosidl_generator_rs/resource/srv.rs.em index 369696f..dd99e8e 100644 --- a/rosidl_generator_rs/resource/srv.rs.em +++ b/rosidl_generator_rs/resource/srv.rs.em @@ -1,84 +1,23 @@ -@{ -req_res_specs = [] - -for subfolder, service in srv_specs: - req_res_specs.append((subfolder, service.request_message)) - req_res_specs.append((subfolder, service.response_message)) -}@ - @{ TEMPLATE( - 'msg_idiomatic.rs.em', + 'srv_idiomatic.rs.em', package_name=package_name, interface_path=interface_path, - msg_specs=req_res_specs, + srv_specs=srv_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) -}@ - -@[for subfolder, srv_spec in srv_specs] - -@{ -type_name = srv_spec.namespaced_type.name -}@ - -#[link(name = "@(package_name)__rosidl_typesupport_c")] -extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; } -// Corresponds to @(package_name)__@(subfolder)__@(type_name) -pub struct @(type_name); - -impl rosidl_runtime_rs::Service for @(type_name) { - type Request = crate::@(subfolder)::@(type_name)_Request; - type Response = crate::@(subfolder)::@(type_name)_Response; - - fn get_type_support() -> *const std::os::raw::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } -} - -@[end for] - pub mod rmw { @{ TEMPLATE( - 'msg_rmw.rs.em', + 'srv_rmw.rs.em', package_name=package_name, interface_path=interface_path, - msg_specs=req_res_specs, + srv_specs=srv_specs, get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, pre_field_serde=pre_field_serde, get_idiomatic_rs_type=get_idiomatic_rs_type, constant_value_to_rs=constant_value_to_rs) }@ - -@[for subfolder, srv_spec in srv_specs] - -@{ -type_name = srv_spec.namespaced_type.name -}@ - - #[link(name = "@(package_name)__rosidl_typesupport_c")] - extern "C" { - fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::os::raw::c_void; - } - - // Corresponds to @(package_name)__@(subfolder)__@(type_name) - pub struct @(type_name); - - impl rosidl_runtime_rs::Service for @(type_name) { - type Request = crate::@(subfolder)::rmw::@(type_name)_Request; - type Response = crate::@(subfolder)::rmw::@(type_name)_Response; - - fn get_type_support() -> *const std::os::raw::c_void { - // SAFETY: No preconditions for this function. - unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } - } - } - -@[end for] - } // mod rmw diff --git a/rosidl_generator_rs/resource/srv_idiomatic.rs.em b/rosidl_generator_rs/resource/srv_idiomatic.rs.em new file mode 100644 index 0000000..660f1a6 --- /dev/null +++ b/rosidl_generator_rs/resource/srv_idiomatic.rs.em @@ -0,0 +1,44 @@ +@{ +req_res_specs = [] + +for subfolder, service in srv_specs: + req_res_specs.append((subfolder, service.request_message)) + req_res_specs.append((subfolder, service.response_message)) +}@ + +@{ +TEMPLATE( + 'msg_idiomatic.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=req_res_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) +}@ + +@[for subfolder, srv_spec in srv_specs] + +@{ +type_name = srv_spec.namespaced_type.name +}@ + +#[link(name = "@(package_name)__rosidl_typesupport_c")] +extern "C" { + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; +} + +// Corresponds to @(package_name)__@(subfolder)__@(type_name) +pub struct @(type_name); + +impl rosidl_runtime_rs::Service for @(type_name) { + type Request = crate::@(subfolder)::@(type_name)_Request; + type Response = crate::@(subfolder)::@(type_name)_Response; + + fn get_type_support() -> *const std::ffi::c_void { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } +} + +@[end for] diff --git a/rosidl_generator_rs/resource/srv_rmw.rs.em b/rosidl_generator_rs/resource/srv_rmw.rs.em new file mode 100644 index 0000000..6ba55f1 --- /dev/null +++ b/rosidl_generator_rs/resource/srv_rmw.rs.em @@ -0,0 +1,44 @@ +@{ +req_res_specs = [] + +for subfolder, service in srv_specs: + req_res_specs.append((subfolder, service.request_message)) + req_res_specs.append((subfolder, service.response_message)) +}@ + +@{ +TEMPLATE( + 'msg_rmw.rs.em', + package_name=package_name, interface_path=interface_path, + msg_specs=req_res_specs, + get_rs_name=get_rs_name, get_rmw_rs_type=get_rmw_rs_type, + pre_field_serde=pre_field_serde, + get_idiomatic_rs_type=get_idiomatic_rs_type, + constant_value_to_rs=constant_value_to_rs) +}@ + +@[for subfolder, srv_spec in srv_specs] + +@{ +type_name = srv_spec.namespaced_type.name +}@ + + #[link(name = "@(package_name)__rosidl_typesupport_c")] + extern "C" { + fn rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() -> *const std::ffi::c_void; + } + + // Corresponds to @(package_name)__@(subfolder)__@(type_name) + pub struct @(type_name); + + impl rosidl_runtime_rs::Service for @(type_name) { + type Request = crate::@(subfolder)::rmw::@(type_name)_Request; + type Response = crate::@(subfolder)::rmw::@(type_name)_Response; + + fn get_type_support() -> *const std::ffi::c_void { + // SAFETY: No preconditions for this function. + unsafe { rosidl_typesupport_c__get_service_type_support_handle__@(package_name)__@(subfolder)__@(type_name)() } + } + } + +@[end for] diff --git a/rosidl_generator_rs/rosidl_generator_rs/__init__.py b/rosidl_generator_rs/rosidl_generator_rs/__init__.py index 502d1d3..b7850a6 100644 --- a/rosidl_generator_rs/rosidl_generator_rs/__init__.py +++ b/rosidl_generator_rs/rosidl_generator_rs/__init__.py @@ -23,6 +23,11 @@ import rosidl_pycommon from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import AbstractSequence +from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractWString +from rosidl_parser.definition import Action from rosidl_parser.definition import Array from rosidl_parser.definition import BasicType from rosidl_parser.definition import BoundedSequence @@ -86,6 +91,10 @@ def generate_rs(generator_arguments_file, typesupport_impls): os.path.join(template_dir, 'srv.rs.em'): ['rust/src/%s.rs'], } + mapping_actions = { + os.path.join(template_dir, 'action.rs.em'): ['rust/src/%s.rs'], + } + # Ensure the required templates exist for template_file in mapping_msgs.keys(): assert os.path.exists(template_file), \ @@ -93,6 +102,9 @@ def generate_rs(generator_arguments_file, typesupport_impls): for template_file in mapping_srvs.keys(): assert os.path.exists(template_file), \ 'Services template file %s not found' % template_file + for template_file in mapping_actions.keys(): + assert os.path.exists(template_file), \ + 'Actions template file %s not found' % template_file data = { 'pre_field_serde': pre_field_serde, @@ -107,6 +119,7 @@ def generate_rs(generator_arguments_file, typesupport_impls): convert_lower_case_underscore_to_camel_case, 'msg_specs': [], 'srv_specs': [], + 'action_specs': [], 'package_name': args['package_name'], 'typesupport_impls': typesupport_impls, 'interface_path': idl_rel_path, @@ -121,6 +134,9 @@ def generate_rs(generator_arguments_file, typesupport_impls): for service in idl_content.get_elements_of_type(Service): data['srv_specs'].append(('srv', service)) + for action in idl_content.get_elements_of_type(Action): + data['action_specs'].append(('action', action)) + if data['msg_specs']: for template_file, generated_filenames in mapping_msgs.items(): for generated_filename in generated_filenames: @@ -143,6 +159,17 @@ def generate_rs(generator_arguments_file, typesupport_impls): generated_file, minimum_timestamp=latest_target_timestamp) + if data['action_specs']: + for template_file, generated_filenames in mapping_actions.items(): + for generated_filename in generated_filenames: + generated_file = os.path.join(args['output_dir'], + generated_filename % 'action') + rosidl_pycommon.expand_template( + os.path.join(template_dir, template_file), + data.copy(), + generated_file, + minimum_timestamp=latest_target_timestamp) + rosidl_pycommon.expand_template( os.path.join(template_dir, 'lib.rs.em'), data.copy(), From 0c683f574ccb2652f58bb9106351fff745ec201c Mon Sep 17 00:00:00 2001 From: Nathan Wiebe Neufeldt Date: Fri, 15 Nov 2024 12:07:56 -0500 Subject: [PATCH 32/32] Update vendored interface packages (#423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update rclrs vendor_interfaces.py script This updates the vendor_interfaces.py script to also vendor in the action_msgs and unique_identifier_msgs packages. The script is modified to always use a list of package names rather than hard-coding the package names everywhere. * Silence certain clippy lints on generated code In case a user enforces clippy linting on these generated packages, silence expected warnings. This is already the case in rclrs, but should be applied directly to the generated packages for the sake of downstream users. The `clippy::derive_partial_eq_without_eq` lint was already being disabled for the packages vendored by rclrs, but is now moved to the rosidl_generator_rs template instead. This is necessary since we always derive the PartialEq trait, but can't necessary derive Eq, and so don't. The `clippy::upper_case_acronyms` is new and was added to account for message type names being upper-case acrynyms, like unique_identifier_msgs::msg::UUID. * Update vendored message packages This updates the message packages vendored under the rclrs `vendor` module. The vendor_interfaces.py script was used for reproducibility. The existing packages – rcl_interfaces, rosgraph_msgs, and builtin_interfaces – are only modified in that they now use the std::ffi::c_void naming for c_void instead of the std::os::raw::c_void alias and disable certain clippy lints. The action_msgs and unique_identifier_msgs packages are newly added to enable adding action support to rclrs. action_msgs is needed for interaction with action goal info and statuses, and has a dependency on unique_identifier_msgs. --- rosidl_generator_rs/resource/lib.rs.em | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rosidl_generator_rs/resource/lib.rs.em b/rosidl_generator_rs/resource/lib.rs.em index 79a0e1d..1ef7792 100644 --- a/rosidl_generator_rs/resource/lib.rs.em +++ b/rosidl_generator_rs/resource/lib.rs.em @@ -1,4 +1,6 @@ #![allow(non_camel_case_types)] +#![allow(clippy::derive_partial_eq_without_eq)] +#![allow(clippy::upper_case_acronyms)] @[if len(msg_specs) > 0]@ pub mod msg;