diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 4b1c2ea5..1e6c8539 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -67,6 +67,7 @@ if(BUILD_TESTING) set(_deps_library_dirs "") list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}) list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/rosidl_generator_java/msg/) + list_append_unique(_deps_library_dirs ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/rosidl_generator_java/srv/) foreach(testsuite ${${PROJECT_NAME}_testsuites}) ament_add_junit_tests("${PROJECT_NAME}_tests_${testsuite}" diff --git a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake index e0b0ff17..6ca9c9ff 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -70,6 +70,10 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) "${_output_path}/${_parent_folder}/${_idl_name}_Request.java" "${_output_path}/${_parent_folder}/${_idl_name}_Response.java" ) + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Request.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Response.ep.${_typesupport_impl}.cpp") + endforeach() endif() # Actions generate extra files if(_parent_folder STREQUAL "action") @@ -78,6 +82,11 @@ foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) "${_output_path}/${_parent_folder}/${_idl_name}_Result.java" "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.java" ) + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Goal.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Result.ep.${_typesupport_impl}.cpp") + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.ep.${_typesupport_impl}.cpp") + endforeach() endif() foreach(_typesupport_impl ${_typesupport_impls}) diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em index 17e456bf..1fcdd7fb 100644 --- a/rosidl_generator_java/resource/action.cpp.em +++ b/rosidl_generator_java/resource/action.cpp.em @@ -1,23 +1,93 @@ @# Included from rosidl_generator_java/resource/idl.cpp.em @{ +import os + +from rosidl_cmake import expand_template from rosidl_generator_c import idl_structure_type_to_c_include_prefix -action_includes = [ - 'rosidl_generator_c/action_type_support_struct.h', -] +namespaces = action.namespaced_type.namespaces +type_name = action.namespaced_type.name +goal_type_name = action.goal.structure.namespaced_type.name +result_type_name = action.result.structure.namespaced_type.name +feedback_type_name = action.feedback.structure.namespaced_type.name +feedback_message_type_name = action.feedback_message.structure.namespaced_type.name +send_goal_type_name = action.send_goal_service.namespaced_type.name +get_result_type_name = action.get_result_service.namespaced_type.name + +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +# Generate Goal message type +data.update({'message': action.goal}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate Result message type +data.update({'message': action.result}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(result_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate Feedback message type +data.update({'message': action.feedback}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(feedback_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate FeedbackMessage message type +data.update({'message': action.feedback_message}) +output_file = os.path.join( + output_dir, + *namespaces[1:], + '{0}.ep.{1}.cpp'.format(feedback_message_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate SendGoal service type +data.update({'service': action.send_goal_service}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(send_goal_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate SendGoal service type +data.update({'service': action.get_result_service}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(get_result_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) }@ -@[for include in action_includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ -#include "@(include)" -@[end for]@ + +#include + +#include + +#include "rosidl_generator_c/action_type_support_struct.h" #include "@(idl_structure_type_to_c_include_prefix(action.namespaced_type)).h" +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + #ifdef __cplusplus extern "C" { #endif diff --git a/rosidl_generator_java/resource/idl.cpp.em b/rosidl_generator_java/resource/idl.cpp.em index 48098dd9..c909006a 100644 --- a/rosidl_generator_java/resource/idl.cpp.em +++ b/rosidl_generator_java/resource/idl.cpp.em @@ -9,102 +9,90 @@ @# - package_name (string) @# - interface_path (Path relative to the directory named after the package) @# - content (IdlContent, list of elements, e.g. Messages or Services) +@# - output_dir (Path) +@# - template_basepath (Path) +@# - typesupport_impl (string, the typesupport identifier of the generated code) @####################################################################### @{ -include_directives = set() +import os -jni_includes = [ - 'jni.h', -] -include_directives.update(jni_includes) -std_includes = [ - 'cassert', - 'cstdint', - 'string', -] -include_directives.update(std_includes) -rosidl_includes = [ - 'rosidl_generator_c/message_type_support_struct.h', -] -include_directives.update(rosidl_includes) -rcljava_includes = [ - 'rcljava_common/exceptions.h', - 'rcljava_common/signatures.h', -] -include_directives.update(rcljava_includes) -}@ -@[for include in jni_includes]@ -#include <@(include)> -@[end for]@ - -@[for include in std_includes]@ -#include <@(include)> -@[end for]@ - -@[for include in rosidl_includes]@ -#include "@(include)" -@[end for]@ - -@[for include in rcljava_includes]@ -#include "@(include)" -@[end for]@ - -// Ensure that a jlong is big enough to store raw pointers -static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); - -using rcljava_common::exceptions::rcljava_throw_exception; +from rosidl_cmake import expand_template +from rosidl_parser.definition import Action +from rosidl_parser.definition import Message +from rosidl_parser.definition import Service -@{ -jni_package_name = package_name.replace('_', '_1') }@ @ @####################################################################### @# Handle messages @####################################################################### @{ -from rosidl_parser.definition import Message -}@ -@[for message in content.get_elements_of_type(Message)]@ -@{ -TEMPLATE( - 'msg.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - message=message, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for message in content.get_elements_of_type(Message): + data.update({'message': message}) + type_name = message.structure.namespaced_type.name + namespaces = message.structure.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'msg.cpp.em', + data, + output_file, + template_basepath=template_basepath) }@ -@[end for]@ @ @####################################################################### @# Handle services @####################################################################### @{ -from rosidl_parser.definition import Service -}@ -@[for service in content.get_elements_of_type(Service)]@ -@{ -TEMPLATE( - 'srv.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - service=service, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, + 'typesupport_impl': typesupport_impl, +} + +for service in content.get_elements_of_type(Service): + data.update({'service': service}) + type_name = service.namespaced_type.name + namespaces = service.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'srv.cpp.em', + data, + output_file, + template_basepath=template_basepath) + }@ -@[end for]@ @ @####################################################################### @# Handle actions @####################################################################### @{ -from rosidl_parser.definition import Action -}@ -@[for action in content.get_elements_of_type(Action)]@ -@{ -TEMPLATE( - 'action.cpp.em', - package_name=package_name, - jni_package_name=jni_package_name, - action=action, - include_directives=include_directives) +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, + 'typesupport_impl': typesupport_impl, +} + +for action in content.get_elements_of_type(Action): + data.update({'action': action}) + type_name = action.namespaced_type.name + namespaces = action.namespaced_type.namespaces + output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(type_name, typesupport_impl)) + expand_template( + 'action.cpp.em', + data, + output_file, + template_basepath=template_basepath) }@ -@[end for]@ diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 6d1f7877..4907c5d0 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -35,7 +35,7 @@ array_list_jni_type = "java/util/ArrayList" cache = defaultdict(lambda: False) cache[msg_normalized_type] = msg_jni_type namespaced_types = set() -includes = set() +member_includes = set() for member in message.structure.members: type_ = member.type if isinstance(type_, AbstractNestedType): @@ -43,36 +43,78 @@ for member in message.structure.members: cache[array_list_normalized_type] = array_list_jni_type type_ = type_.value_type if isinstance(type_, BasicType): - includes.add('rosidl_generator_c/primitives_sequence.h') - includes.add('rosidl_generator_c/primitives_sequence_functions.h') + member_includes.add('rosidl_generator_c/primitives_sequence.h') + member_includes.add('rosidl_generator_c/primitives_sequence_functions.h') # We do not cache strings because java.lang.String behaves differently if not isinstance(type_, AbstractGenericString): cache[get_normalized_type(type_)] = get_jni_type(type_) if isinstance(type_, AbstractString): - includes.add('rosidl_generator_c/string.h') - includes.add('rosidl_generator_c/string_functions.h') + member_includes.add('rosidl_generator_c/string.h') + member_includes.add('rosidl_generator_c/string_functions.h') if isinstance(type_, AbstractWString): - includes.add('rosidl_generator_c/u16string.h') - includes.add('rosidl_generator_c/u16string_functions.h') + member_includes.add('rosidl_generator_c/u16string.h') + member_includes.add('rosidl_generator_c/u16string_functions.h') if isinstance(type_, NamespacedType): namespaced_types.add(get_jni_type(type_)) - includes.add(idl_structure_type_to_c_include_prefix(type_) + '.h') + include_prefix = idl_structure_type_to_c_include_prefix(type_) + # TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) + # Strip off any service or action suffix + # There are several types that actions and services are composed of, but they are included + # a common header that is based on the action or service name + # ie. there are not separate headers for each type + if include_prefix.endswith('__request'): + include_prefix = include_prefix[:-9] + elif include_prefix.endswith('__response'): + include_prefix = include_prefix[:-10] + if include_prefix.endswith('__goal'): + include_prefix = include_prefix[:-6] + elif include_prefix.endswith('__result'): + include_prefix = include_prefix[:-8] + elif include_prefix.endswith('__feedback'): + include_prefix = include_prefix[:-10] + member_includes.add(include_prefix + '.h') }@ -@[for include in includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ +@{ +# TODO(jacobperron): Remove this logic after https://github.com/ros2/rosidl/pull/432 (Foxy) +message_c_include_prefix = idl_structure_type_to_c_include_prefix(message.structure.namespaced_type) +# Strip off any service or action suffix +if message_c_include_prefix.endswith('__request'): + message_c_include_prefix = message_c_include_prefix[:-9] +elif message_c_include_prefix.endswith('__response'): + message_c_include_prefix = message_c_include_prefix[:-10] +if message_c_include_prefix.endswith('__goal'): + message_c_include_prefix = message_c_include_prefix[:-6] +elif message_c_include_prefix.endswith('__result'): + message_c_include_prefix = message_c_include_prefix[:-8] +elif message_c_include_prefix.endswith('__feedback'): + message_c_include_prefix = message_c_include_prefix[:-10] +}@ + +#include + +#include +#include +#include + +#include "rosidl_generator_c/message_type_support_struct.h" + +#include "rcljava_common/exceptions.h" +#include "rcljava_common/signatures.h" + +@[for include in member_includes]@ #include "@(include)" @[end for]@ -#include "@(idl_structure_type_to_c_include_prefix(message.structure.namespaced_type)).h" +#include "@(message_c_include_prefix).h" + +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + +using rcljava_common::exceptions::rcljava_throw_exception; #ifdef __cplusplus extern "C" { diff --git a/rosidl_generator_java/resource/srv.cpp.em b/rosidl_generator_java/resource/srv.cpp.em index 5c2d2d0b..c7bd40a6 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -1,23 +1,50 @@ @# Included from rosidl_generator_java/resource/idl.cpp.em @{ +import os +from rosidl_cmake import expand_template from rosidl_generator_c import idl_structure_type_to_c_include_prefix -service_includes = [ - 'rosidl_generator_c/service_type_support_struct.h', -] +namespaces = service.namespaced_type.namespaces +type_name = service.namespaced_type.name +request_type_name = service.request_message.structure.namespaced_type.name +response_type_name = service.response_message.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +# Generate request message +data.update({'message': service.request_message}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(request_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) + +# Generate response message +data.update({'message': service.response_message}) +output_file = os.path.join( + output_dir, *namespaces[1:], '{0}.ep.{1}.cpp'.format(response_type_name, typesupport_impl)) +expand_template( + 'msg.cpp.em', + data, + output_file) }@ -@[for include in service_includes]@ -@[ if include in include_directives]@ -// already included above -// @ -@[ else]@ -@{include_directives.add(include)}@ -@[ end if]@ -#include "@(include)" -@[end for]@ + +#include + +#include + +#include "rosidl_generator_c/service_type_support_struct.h" #include "@(idl_structure_type_to_c_include_prefix(service.namespaced_type)).h" +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + #ifdef __cplusplus extern "C" { #endif diff --git a/rosidl_generator_java/rosidl_generator_java/__init__.py b/rosidl_generator_java/rosidl_generator_java/__init__.py index 7941ab69..32a8e366 100644 --- a/rosidl_generator_java/rosidl_generator_java/__init__.py +++ b/rosidl_generator_java/rosidl_generator_java/__init__.py @@ -45,8 +45,9 @@ def generate_java(generator_arguments_file, typesupport_impls): for impl in typesupport_impls: mapping = { - 'idl.cpp.em': '%s.ep.{0}.cpp'.format(impl), + 'idl.cpp.em': '_%s.cpp', } + additional_context.update(typesupport_impl=impl) generate_files( generator_arguments_file, mapping, @@ -203,4 +204,4 @@ def get_jni_signature(type_): def get_jni_mangled_name(fully_qualified_name): # JNI name mangling: # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names - return fully_qualified_name[0].replace('_', '_1') + '_' + '_'.join(fully_qualified_name[1:]) + return '_'.join(list(map(lambda name: name.replace('_', '_1'), fully_qualified_name))) diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index dfea4a97..00638e09 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -532,4 +532,73 @@ public final void testUnboundedSequences() { assertEquals(42, unbounded_seq.getAlignmentCheck()); } + + @Test + public final void testBasicTypesService() { + rosidl_generator_java.srv.BasicTypes_Request basicTypesRequest = + new rosidl_generator_java.srv.BasicTypes_Request(); + rosidl_generator_java.srv.BasicTypes_Response basicTypesResponse = + new rosidl_generator_java.srv.BasicTypes_Response(); + // Set request fields + boolean expectedBool1 = true; + basicTypesRequest.setBoolValue(expectedBool1); + byte expectedByte1 = 123; + basicTypesRequest.setByteValue(expectedByte1); + byte expectedChar1 = 'a'; + basicTypesRequest.setCharValue(expectedChar1); + float expectedFloat1 = 12.34f; + basicTypesRequest.setFloat32Value(expectedFloat1); + double expectedDouble1 = 12.34; + basicTypesRequest.setFloat64Value(expectedDouble1); + byte expectedInt81 = 123; + basicTypesRequest.setInt8Value(expectedInt81); + short expectedInt161 = 1230; + basicTypesRequest.setInt16Value(expectedInt161); + int expectedInt321 = 123000; + basicTypesRequest.setInt32Value(expectedInt321); + long expectedInt641 = 42949672960L; + basicTypesRequest.setInt64Value(expectedInt641); + + // Set response fields + boolean expectedBool2 = false; + basicTypesResponse.setBoolValue(expectedBool2); + byte expectedByte2 = -42; + basicTypesResponse.setByteValue(expectedByte2); + byte expectedChar2 = ' '; + basicTypesResponse.setCharValue(expectedChar2); + float expectedFloat2 = -43.21f; + basicTypesResponse.setFloat32Value(expectedFloat2); + double expectedDouble2 = -43.21; + basicTypesResponse.setFloat64Value(expectedDouble2); + byte expectedInt82 = -42; + basicTypesResponse.setInt8Value(expectedInt82); + short expectedInt162 = -420; + basicTypesResponse.setInt16Value(expectedInt162); + int expectedInt322 = -42000; + basicTypesResponse.setInt32Value(expectedInt322); + long expectedInt642 = -4200000L; + basicTypesResponse.setInt64Value(expectedInt642); + + // Get request fields + assertEquals(expectedBool1, basicTypesRequest.getBoolValue()); + assertEquals(expectedByte1, basicTypesRequest.getByteValue()); + assertEquals(expectedChar1, basicTypesRequest.getCharValue()); + assertEquals(expectedFloat1, basicTypesRequest.getFloat32Value(), 0.01f); + assertEquals(expectedDouble1, basicTypesRequest.getFloat64Value(), 0.01); + assertEquals(expectedInt81, basicTypesRequest.getInt8Value()); + assertEquals(expectedInt161, basicTypesRequest.getInt16Value()); + assertEquals(expectedInt321, basicTypesRequest.getInt32Value()); + assertEquals(expectedInt641, basicTypesRequest.getInt64Value()); + + // Get response fields + assertEquals(expectedBool2, basicTypesResponse.getBoolValue()); + assertEquals(expectedByte2, basicTypesResponse.getByteValue()); + assertEquals(expectedChar2, basicTypesResponse.getCharValue()); + assertEquals(expectedFloat2, basicTypesResponse.getFloat32Value(), 0.01f); + assertEquals(expectedDouble2, basicTypesResponse.getFloat64Value(), 0.01); + assertEquals(expectedInt82, basicTypesResponse.getInt8Value()); + assertEquals(expectedInt162, basicTypesResponse.getInt16Value()); + assertEquals(expectedInt322, basicTypesResponse.getInt32Value()); + assertEquals(expectedInt642, basicTypesResponse.getInt64Value()); + } }