From 2770a783f0b3ddaf2dc65c942c2663bcd02aa0f7 Mon Sep 17 00:00:00 2001 From: Alexis Pojomovsky Date: Tue, 13 Nov 2018 15:14:37 -0300 Subject: [PATCH 1/4] Add _action.py.em; Makes rosidl_generator_py handle action files properly --- rosidl_generator_py/resource/_action.py.em | 61 +++++++++++++++++++ .../_msg_pkg_typesupport_entry_point.c.em | 50 +++++++++++++++ rosidl_generator_py/resource/_srv.py.em | 2 +- .../rosidl_generator_py/generate_py_impl.py | 14 +++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 rosidl_generator_py/resource/_action.py.em diff --git a/rosidl_generator_py/resource/_action.py.em b/rosidl_generator_py/resource/_action.py.em new file mode 100644 index 00000000..e5a69a28 --- /dev/null +++ b/rosidl_generator_py/resource/_action.py.em @@ -0,0 +1,61 @@ +# generated from rosidl_generator_py/resource/_action.py.em +# generated code does not contain a copyright notice + +@####################################################################### +@# EmPy template for generating _.py files +@# +@# Context: +@# - module_name +@# - package_name +@# - spec (rosidl_parser.ActionSpecification) +@# Parsed specification of the .action file +@# - convert_camel_case_to_lower_case_underscore (function) +@####################################################################### +@ +import logging +import traceback + + +class Metaclass(type): + """Metaclass of action '@(spec.action_name)'.""" + + _TYPE_SUPPORT = None + + @@classmethod + def __import_type_support__(cls): + try: + from rosidl_generator_py import import_type_support + module = import_type_support('@(package_name)') + except ImportError: + logger = logging.getLogger('rosidl_generator_py.@(spec.action_name)') + logger.debug( + 'Failed to import needed modules for type support:\n' + traceback.format_exc()) + else: + cls._TYPE_SUPPORT = module.type_support_action__@(subfolder)_@(module_name) +@{ +preffix = '_' + convert_camel_case_to_lower_case_underscore(spec.action_name) + '__' +suffixes = ['feedback', 'goal', 'result'] +for field_name in [preffix + suffix for suffix in suffixes]: + print('%sfrom %s.%s import %s' % (' ' * 4 * 3, package_name, subfolder, field_name)) + print('%sif %s.Metaclass._TYPE_SUPPORT is None:' % (' ' * 4 * 3, field_name)) + print('%s%s.Metaclass.__import_type_support__()' % (' ' * 4 * 4, field_name)) +print('%sfrom %s.%s import %s' % (' ' * 4 * 3, 'action_msgs', 'msg', '_goal_status')) +print('%sif %s.Metaclass._TYPE_SUPPORT is None:' % (' ' * 4 * 3, '_goal_status')) +print('%s%s.Metaclass.__import_type_support__()' % (' ' * 4 * 4, '_goal_status')) +print('%sfrom %s.%s import %s' % (' ' * 4 * 3, 'action_msgs', 'srv', '_cancel_goal')) +print('%sif %s.Metaclass._TYPE_SUPPORT is None:' % (' ' * 4 * 3, '_cancel_goal')) +print('%s%s.Metaclass.__import_type_support__()' % (' ' * 4 * 4, '_cancel_goal')) +}@ + +class @(spec.action_name)(metaclass=Metaclass): + from action_msgs.srv._cancel_goal import CancelGoal as CancelGoalService + from action_msgs.msg._goal_status_array import GoalStatusArray as GoalStatusMessage + from @(package_name).@(subfolder)._@convert_camel_case_to_lower_case_underscore(spec.action_name)__goal import @(spec.action_name)_Goal as GoalRequestService + from @(package_name).@(subfolder)._@convert_camel_case_to_lower_case_underscore(spec.action_name)__result import @(spec.action_name)_Result as GoalResultService + from @(package_name).@(subfolder)._@convert_camel_case_to_lower_case_underscore(spec.action_name)__feedback import @(spec.action_name)_Feedback as Feedback + + Goal = GoalRequestService.Request + Result = GoalResultService.Response + + def __init__(self): + raise NotImplementedError('Action classes can not be instantiated') diff --git a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em index fb22ec38..c1335110 100644 --- a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em +++ b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em @@ -10,6 +10,8 @@ @# Parsed specification of the .msg files @# - service_specs (list of rosidl_parser.ServiceSpecification) @# Parsed specification of the .srv files +@# - action_specs (list of rosidl_parser.ActionSpecification) +@# Parsed specification of the .action files @# - typesupport_impl (string identifying the typesupport used) @# - convert_camel_case_to_lower_case_underscore (function) @####################################################################### @@ -28,6 +30,8 @@ for spec, subfolder in message_specs: static_includes.add('#include ') elif subfolder == 'srv' or subfolder == 'action': static_includes.add('#include ') + if subfolder == 'action': + static_includes.add('#include ') }@ @[for value in sorted(static_includes)]@ @(value) @@ -172,6 +176,42 @@ _register_srv_type__@(subfolder)__@(type_name)(PyObject * pymodule) } @[end for]@ +@[for spec, subfolder in action_specs]@ +@{ +type_name = convert_camel_case_to_lower_case_underscore(spec.action_name) +function_name = 'type_support' +}@ + +ROSIDL_GENERATOR_C_IMPORT +const rosidl_action_type_support_t * +ROSIDL_TYPESUPPORT_INTERFACE__ACTION_SYMBOL_NAME(rosidl_typesupport_c, @(spec.pkg_name), @(subfolder), @(spec.action_name))(); + +int8_t +_register_action_type__@(subfolder)__@(type_name)(PyObject * pymodule) +{ + int8_t err; + PyObject * pyobject_@(function_name) = NULL; + pyobject_@(function_name) = PyCapsule_New( + (void *)ROSIDL_TYPESUPPORT_INTERFACE__ACTION_SYMBOL_NAME(rosidl_typesupport_c, @(spec.pkg_name), @(subfolder), @(spec.action_name))(), + NULL, NULL); + if (!pyobject_@(function_name)) { + // previously added objects will be removed when the module is destroyed + return -1; + } + err = PyModule_AddObject( + pymodule, + "@(function_name)_action__@(subfolder)_@(type_name)", + pyobject_@(function_name)); + if (err) { + // the created capsule needs to be decremented + Py_XDECREF(pyobject_@(function_name)); + // previously added objects will be removed when the module is destroyed + return err; + } + return 0; +} +@[end for]@ + PyMODINIT_FUNC PyInit_@(package_name)_s__@(typesupport_impl)(void) { @@ -201,6 +241,16 @@ type_name = convert_camel_case_to_lower_case_underscore(spec.srv_name) return NULL; } @[end for]@ +@[for spec, subfolder in action_specs]@ +@{ +type_name = convert_camel_case_to_lower_case_underscore(spec.action_name) +}@ + err = _register_action_type__@(subfolder)__@(type_name)(pymodule); + if (err) { + Py_XDECREF(pymodule); + return NULL; + } +@[end for]@ return pymodule; } diff --git a/rosidl_generator_py/resource/_srv.py.em b/rosidl_generator_py/resource/_srv.py.em index 16756abb..8f188140 100644 --- a/rosidl_generator_py/resource/_srv.py.em +++ b/rosidl_generator_py/resource/_srv.py.em @@ -46,4 +46,4 @@ class @(spec.srv_name)(metaclass=Metaclass): from @(package_name).@(subfolder)._@convert_camel_case_to_lower_case_underscore(spec.srv_name)__response import @(spec.srv_name)_Response as Response def __init__(self): - raise NotImplementedError('Service classes can not be instanciated') + raise NotImplementedError('Service classes can not be instantiated') diff --git a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py index 2a87842a..316c5e73 100644 --- a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py +++ b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py @@ -20,6 +20,7 @@ from rosidl_cmake import get_newest_modification_time from rosidl_cmake import read_generator_arguments from rosidl_generator_c import primitive_msg_type_to_c +from rosidl_parser import parse_action_file from rosidl_parser import parse_message_file from rosidl_parser import parse_service_file @@ -44,12 +45,18 @@ def generate_py(generator_arguments_file, typesupport_impls): os.path.join(template_dir, '_srv.py.em'): ['_%s.py'], } + mapping_actions = { + os.path.join(template_dir, '_action.py.em'): ['_%s.py'], + } + for template_file in mapping_msgs.keys(): assert os.path.exists(template_file), 'Could not find template: ' + template_file for template_file in mapping_msg_pkg_extension.keys(): assert os.path.exists(template_file), 'Could not find template: ' + template_file for template_file in mapping_srvs.keys(): assert os.path.exists(template_file), 'Could not find template: ' + template_file + for template_file in mapping_actions.keys(): + assert os.path.exists(template_file), 'Could not find template: ' + template_file functions = { 'constant_value_to_py': constant_value_to_py, @@ -63,6 +70,7 @@ def generate_py(generator_arguments_file, typesupport_impls): modules = defaultdict(list) message_specs = [] service_specs = [] + action_specs = [] 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)) @@ -76,6 +84,11 @@ def generate_py(generator_arguments_file, typesupport_impls): service_specs.append((spec, subfolder)) mapping = mapping_srvs type_name = spec.srv_name + elif extension == '.action': + spec = parse_action_file(args['package_name'], ros_interface_file) + action_specs.append((spec, subfolder)) + mapping = mapping_actions + type_name = spec.action_name else: continue @@ -114,6 +127,7 @@ def generate_py(generator_arguments_file, typesupport_impls): for generated_filename in generated_filenames: data = { 'package_name': args['package_name'], + 'action_specs': action_specs, 'message_specs': message_specs, 'service_specs': service_specs, 'typesupport_impl': type_support_impl_by_filename.get(generated_filename, ''), From 234ce1a1a0769a02329bbb2165aa5016007182a5 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Wed, 9 Jan 2019 16:46:47 -0300 Subject: [PATCH 2/4] Adds CMake pipeline bits for action generation. --- .../cmake/custom_command.cmake | 2 +- rosidl_generator_py/cmake/register_py.cmake | 5 + ...erator_py_generate_action_interfaces.cmake | 241 ++++++++++++++++++ ...idl_generator_py_generate_interfaces.cmake | 2 +- rosidl_generator_py/resource/_action.py.em | 3 +- .../_msg_pkg_typesupport_entry_point.c.em | 17 +- .../rosidl_generator_py/generate_py_impl.py | 37 ++- .../import_type_support_impl.py | 9 +- 8 files changed, 294 insertions(+), 22 deletions(-) create mode 100644 rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake diff --git a/rosidl_generator_py/cmake/custom_command.cmake b/rosidl_generator_py/cmake/custom_command.cmake index c6f55c5c..558716d1 100644 --- a/rosidl_generator_py/cmake/custom_command.cmake +++ b/rosidl_generator_py/cmake/custom_command.cmake @@ -23,7 +23,7 @@ add_custom_command( COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_py_BIN} --generator-arguments-file "${generator_arguments_file}" --typesupport-impls "${_typesupport_impls}" - DEPENDS ${target_dependencies} ${rosidl_generate_interfaces_TARGET} + DEPENDS ${target_dependencies} ${rosidl_generate_interfaces_TARGET} ${extra_generator_dependencies} COMMENT "Generating Python code for ROS interfaces" VERBATIM ) diff --git a/rosidl_generator_py/cmake/register_py.cmake b/rosidl_generator_py/cmake/register_py.cmake index d20d3590..937b3916 100644 --- a/rosidl_generator_py/cmake/register_py.cmake +++ b/rosidl_generator_py/cmake/register_py.cmake @@ -19,6 +19,11 @@ macro(rosidl_generator_py_extras BIN GENERATOR_FILES TEMPLATE_DIR) "rosidl_generator_py" "rosidl_generator_py_generate_interfaces.cmake") + ament_register_extension( + "rosidl_generate_action_interfaces" + "rosidl_generator_py" + "rosidl_generator_py_generate_action_interfaces.cmake") + normalize_path(BIN "${BIN}") set(rosidl_generator_py_BIN "${BIN}") diff --git a/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake b/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake new file mode 100644 index 00000000..92a09698 --- /dev/null +++ b/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake @@ -0,0 +1,241 @@ +# Copyright 2019 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. + +find_package(rmw_implementation_cmake REQUIRED) +find_package(rmw REQUIRED) +find_package(rosidl_generator_c REQUIRED) +find_package(rosidl_typesupport_c REQUIRED) +find_package(rosidl_typesupport_interface REQUIRED) + +find_package(PythonInterp 3.5 REQUIRED) + +find_package(python_cmake_module REQUIRED) +find_package(PythonExtra MODULE REQUIRED) + +set(_typesupport_impls "rosidl_typesupport_c") + +set(_output_path + "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py/${PROJECT_NAME}") + +set(_generated_action_py_files "") +foreach(_idl_file ${rosidl_generate_action_interfaces_IDL_FILES}) + get_filename_component(_extension "${_idl_file}" EXT) + get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + if(_extension STREQUAL ".action") + set(_allowed_parent_folders "action") + if(NOT _parent_folder IN_LIST _allowed_parent_folders) + message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") + endif() + else() + message(FATAL_ERROR "Interface file with unknown extension: ${_idl_file}") + endif() + get_filename_component(_msg_name "${_idl_file}" NAME_WE) + string_camel_case_to_lower_case_underscore("${_msg_name}" _module_name) + list(APPEND _generated_action_py_files + "${_output_path}/${_parent_folder}/_${_module_name}.py" + ) +endforeach() + +foreach(_typesupport_impl ${_typesupport_impls}) + set(_generated_extension_${_typesupport_impl}_files "") +endforeach() +set(_generated_extension_files "") + +if(NOT _generated_action_py_files STREQUAL "") + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/_${PROJECT_NAME}_action_s.ep.${_typesupport_impl}.c") + list(APPEND _generated_extension_files "${_generated_extension_${_typesupport_impl}_files}") + endforeach() + list(GET _generated_action_py_files 0 _action_file) + get_filename_component(_parent_folder "${_action_file}" DIRECTORY) + list(APPEND _generated_action_py_files "${_parent_folder}/__init__.py") +endif() + +set(_dependency_files "") +set(_dependencies "") +foreach(_pkg_name ${rosidl_generate_action_interfaces_DEPENDENCY_PACKAGE_NAMES}) + foreach(_idl_file ${${_pkg_name}_INTERFACE_FILES}) + get_filename_component(_extension "${_idl_file}" EXT) + if(_extension STREQUAL ".msg") + 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}") + endif() + endforeach() +endforeach() + +set(target_dependencies + "${rosidl_generator_py_BIN}" + ${rosidl_generator_py_GENERATOR_FILES} + "${rosidl_generator_py_TEMPLATE_DIR}/_action.py.em" + "${rosidl_generator_py_TEMPLATE_DIR}/_msg_pkg_typesupport_entry_point.c.em" + ${rosidl_generate_action_interfaces_IDL_FILES} + ${_dependency_files}) +foreach(dep ${target_dependencies}) + if(NOT EXISTS "${dep}") + get_property(is_generated SOURCE "${dep}" PROPERTY GENERATED) + if(NOT ${_is_generated}) + message(FATAL_ERROR "Target dependency '${dep}' does not exist") + endif() + endif() +endforeach() + +set(generator_arguments_file "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py__generate_actions__arguments.json") +rosidl_write_generator_arguments( + "${generator_arguments_file}" + PACKAGE_NAME "${PROJECT_NAME}" + ROS_INTERFACE_FILES "${rosidl_generate_action_interfaces_IDL_FILES}" + ROS_INTERFACE_DEPENDENCIES "${_dependencies}" + OUTPUT_DIR "${_output_path}" + TEMPLATE_DIR "${rosidl_generator_py_TEMPLATE_DIR}" + TARGET_DEPENDENCIES ${target_dependencies} +) +# make sure that actions generation run sequentially with messages/services generation +set(extra_generator_dependencies "${rosidl_generate_interfaces_TARGET}__py") + +set(_target_suffix "__action_py") + +# move custom command into a subdirectory to avoid multiple invocations on Windows +set(_subdir "${CMAKE_CURRENT_BINARY_DIR}/${rosidl_generate_interfaces_TARGET}${_target_suffix}") +file(MAKE_DIRECTORY "${_subdir}") +file(READ "${rosidl_generator_py_DIR}/custom_command.cmake" _custom_command) +file(WRITE "${_subdir}/CMakeLists.txt" "${_custom_command}") +add_subdirectory("${_subdir}" ${rosidl_generate_interfaces_TARGET}${_target_suffix}) +set_property( + SOURCE + ${_generated_extension_files} ${_generated_action_py_files} + PROPERTY GENERATED 1) + +macro(set_properties _build_type) + set_target_properties(${_target_name} PROPERTIES + COMPILE_OPTIONS "${_extension_compile_flags}" + PREFIX "" + LIBRARY_OUTPUT_DIRECTORY${_build_type} ${_output_path} + RUNTIME_OUTPUT_DIRECTORY${_build_type} ${_output_path} + OUTPUT_NAME "${PROJECT_NAME}_action_s__${_typesupport_impl}${PythonExtra_EXTENSION_SUFFIX}" + SUFFIX "${PythonExtra_EXTENSION_EXTENSION}") +endmacro() + +foreach(_typesupport_impl ${_typesupport_impls}) + find_package(${_typesupport_impl} REQUIRED) + + set(_pyext_suffix "__pyext") + set(_target_name "${PROJECT_NAME}_action__${_typesupport_impl}${_pyext_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 + ) + + set(_extension_compile_flags "") + set(_PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE}) + if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(_extension_compile_flags -Wall -Wextra) + endif() + if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE_DEBUG}) + endif() + set_properties("") + if(WIN32) + set_properties("_DEBUG") + set_properties("_MINSIZEREL") + set_properties("_RELEASE") + set_properties("_RELWITHDEBINFO") + endif() + target_link_libraries( + ${_target_name} + ${PythonExtra_LIBRARIES} + ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} + ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl}__generate_actions + ) + + target_include_directories(${_target_name} + PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_c + ${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_py + ${PythonExtra_INCLUDE_DIRS} + ) + + rosidl_target_interfaces(${_target_name} + ${rosidl_generate_interfaces_TARGET} rosidl_typesupport_c) + + 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} + ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl}__generate_actions + ) + ament_target_dependencies(${_target_name} + "rosidl_generator_c" + "rosidl_generator_py" + "${rosidl_generate_interfaces_TARGET}__rosidl_generator_c" + ) + set(PYTHON_EXECUTABLE ${_PYTHON_EXECUTABLE}) + + if(NOT rosidl_generate_interfaces_SKIP_INSTALL) + install(TARGETS ${_target_name} + DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}") + endif() +endforeach() + +if(NOT rosidl_generate_action_interfaces_SKIP_INSTALL) + if(NOT _generated_action_py_files STREQUAL "") + list(GET _generated_action_py_files 0 _action_file) + get_filename_component(_action_package_dir "${_action_file}" DIRECTORY) + get_filename_component(_action_package_dir "${_action_package_dir}" NAME) + if(NOT _action_package_dir STREQUAL "") + install(FILES ${_generated_action_py_files} + DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}/${_action_package_dir}" + ) + endif() + endif() +endif() + +if(BUILD_TESTING AND rosidl_generate_action_interfaces_ADD_LINTER_TESTS) + if(NOT _generated_action_py_files STREQUAL "") + find_package(ament_cmake_flake8 REQUIRED) + ament_flake8( + TESTNAME "flake8_rosidl_generated_py_generate_actions" + # the generated code might contain longer lines for templated types + MAX_LINE_LENGTH 999 + "${_output_path}") + + find_package(ament_cmake_pep257 REQUIRED) + ament_pep257( + TESTNAME "pep257_rosidl_generated_py_generate_actions" + "${_output_path}") + + find_package(ament_cmake_uncrustify REQUIRED) + ament_uncrustify( + TESTNAME "uncrustify_rosidl_generated_py_generate_actions" + # the generated code might contain longer lines for templated types + MAX_LINE_LENGTH 0 + "${_output_path}") + endif() +endif() diff --git a/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake b/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake index 3fe17b02..5c0d6773 100644 --- a/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake +++ b/rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake @@ -151,7 +151,7 @@ rosidl_write_generator_arguments( TEMPLATE_DIR "${rosidl_generator_py_TEMPLATE_DIR}" TARGET_DEPENDENCIES ${target_dependencies} ) - +set(extra_generator_dependencies "") if(NOT _generated_msg_py_files STREQUAL "") list(GET _generated_msg_py_files 0 _msg_file) diff --git a/rosidl_generator_py/resource/_action.py.em b/rosidl_generator_py/resource/_action.py.em index e5a69a28..0c8e6e62 100644 --- a/rosidl_generator_py/resource/_action.py.em +++ b/rosidl_generator_py/resource/_action.py.em @@ -25,13 +25,14 @@ class Metaclass(type): def __import_type_support__(cls): try: from rosidl_generator_py import import_type_support - module = import_type_support('@(package_name)') + module = import_type_support('@(package_name)', '@(package_name)_action') except ImportError: logger = logging.getLogger('rosidl_generator_py.@(spec.action_name)') logger.debug( 'Failed to import needed modules for type support:\n' + traceback.format_exc()) else: cls._TYPE_SUPPORT = module.type_support_action__@(subfolder)_@(module_name) + @{ preffix = '_' + convert_camel_case_to_lower_case_underscore(spec.action_name) + '__' suffixes = ['feedback', 'goal', 'result'] diff --git a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em index c1335110..eba391c5 100644 --- a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em +++ b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em @@ -22,16 +22,17 @@ @{ static_includes = set([ - '#include ', '#include ', ]) -for spec, subfolder in message_specs: - if subfolder == 'msg': - static_includes.add('#include ') - elif subfolder == 'srv' or subfolder == 'action': - static_includes.add('#include ') - if subfolder == 'action': - static_includes.add('#include ') + +if message_specs: + static_includes.add('#include ') + +if service_specs: + static_includes.add('#include ') + +if action_specs: + static_includes.add('#include ') }@ @[for value in sorted(static_includes)]@ @(value) diff --git a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py index 316c5e73..47841d07 100644 --- a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py +++ b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py @@ -14,6 +14,7 @@ from collections import defaultdict import os +import re from rosidl_cmake import convert_camel_case_to_lower_case_underscore from rosidl_cmake import expand_template @@ -117,16 +118,37 @@ def generate_py(generator_arguments_file, typesupport_impls): import_list['%s # noqa\n' % type_] = 'from %s.%s._%s import %s\n' % \ (args['package_name'], subfolder, module_name, type_) - with open(os.path.join(args['output_dir'], subfolder, '__init__.py'), 'w') as f: - for import_line in sorted(import_list.values()): - f.write(import_line) - for noqa_line in sorted(import_list.keys()): - f.write(noqa_line) + path_to_module = os.path.join(args['output_dir'], subfolder, '__init__.py') + + content = "" + if os.path.isfile(path_to_module): + with open(path_to_module, 'r') as f: + content = f.read() + with open(path_to_module, 'w') as f: + block_name = args['package_name'] + if action_specs: + block_name += '_action' + content = re.sub( + r"# BEGIN %s$.*^# END %s" % (block_name, block_name), + '', content, 0, re.M | re.S + ) + content = re.sub(r"^\s*$", '', content, 0, re.M) + content += ''.join( + ['# BEGIN %s\n' % block_name] + + sorted(import_list.values()) + # import_line + sorted(import_list.keys()) + # noqa_line + ['# END %s\n' % block_name] + ) + print(content) + f.write(content) for template_file, generated_filenames in mapping_msg_pkg_extension.items(): for generated_filename in generated_filenames: + package_name = args['package_name'] + if action_specs: + package_name += '_action' data = { - 'package_name': args['package_name'], + 'package_name': package_name, 'action_specs': action_specs, 'message_specs': message_specs, 'service_specs': service_specs, @@ -134,7 +156,8 @@ def generate_py(generator_arguments_file, typesupport_impls): } data.update(functions) generated_file = os.path.join( - args['output_dir'], generated_filename % args['package_name']) + args['output_dir'], generated_filename % package_name + ) expand_template( template_file, data, generated_file, minimum_timestamp=latest_target_timestamp) diff --git a/rosidl_generator_py/rosidl_generator_py/import_type_support_impl.py b/rosidl_generator_py/rosidl_generator_py/import_type_support_impl.py index 65ef1a25..b53608db 100644 --- a/rosidl_generator_py/rosidl_generator_py/import_type_support_impl.py +++ b/rosidl_generator_py/rosidl_generator_py/import_type_support_impl.py @@ -24,7 +24,7 @@ def __init__(self, pkg_name): self.pkg_name = pkg_name -def import_type_support(pkg_name): +def import_type_support(pkg_name, mod_prefix=None): """ Import the rosidl_typesupport_c module of a package. @@ -33,11 +33,12 @@ def import_type_support(pkg_name): be converted to and from message structures used by the rmw implementation. :param pkg_name str: name of the package + :param mod_prefix str: optional typesupport module prefix :returns: the typesupport Python module for the specified package """ - module_name = '.{pkg_name}_s__rosidl_typesupport_c'.format( - pkg_name=pkg_name, - ) + if not mod_prefix: + mod_prefix = pkg_name + module_name = '.{}_s__rosidl_typesupport_c'.format(mod_prefix) try: return importlib.import_module(module_name, package=pkg_name) except ImportError: From 161faf02a5369654b6d3621ecaf95a8e44fc901a Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 11 Jan 2019 12:49:55 -0300 Subject: [PATCH 3/4] Fixes all linter issues. --- ...dl_generator_py_generate_action_interfaces.cmake | 2 +- rosidl_generator_py/resource/_action.py.em | 2 +- .../resource/_msg_pkg_typesupport_entry_point.c.em | 9 +++------ .../rosidl_generator_py/generate_py_impl.py | 13 ++++++------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake b/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake index 92a09698..177592d4 100644 --- a/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake +++ b/rosidl_generator_py/cmake/rosidl_generator_py_generate_action_interfaces.cmake @@ -212,7 +212,7 @@ if(NOT rosidl_generate_action_interfaces_SKIP_INSTALL) if(NOT _action_package_dir STREQUAL "") install(FILES ${_generated_action_py_files} DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}/${_action_package_dir}" - ) + ) endif() endif() endif() diff --git a/rosidl_generator_py/resource/_action.py.em b/rosidl_generator_py/resource/_action.py.em index 0c8e6e62..6cb18a61 100644 --- a/rosidl_generator_py/resource/_action.py.em +++ b/rosidl_generator_py/resource/_action.py.em @@ -32,7 +32,6 @@ class Metaclass(type): 'Failed to import needed modules for type support:\n' + traceback.format_exc()) else: cls._TYPE_SUPPORT = module.type_support_action__@(subfolder)_@(module_name) - @{ preffix = '_' + convert_camel_case_to_lower_case_underscore(spec.action_name) + '__' suffixes = ['feedback', 'goal', 'result'] @@ -48,6 +47,7 @@ print('%sif %s.Metaclass._TYPE_SUPPORT is None:' % (' ' * 4 * 3, '_cancel_goal') print('%s%s.Metaclass.__import_type_support__()' % (' ' * 4 * 4, '_cancel_goal')) }@ + class @(spec.action_name)(metaclass=Metaclass): from action_msgs.srv._cancel_goal import CancelGoal as CancelGoalService from action_msgs.msg._goal_status_array import GoalStatusArray as GoalStatusMessage diff --git a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em index eba391c5..37ecb9d7 100644 --- a/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em +++ b/rosidl_generator_py/resource/_msg_pkg_typesupport_entry_point.c.em @@ -24,20 +24,16 @@ static_includes = set([ '#include ', ]) - if message_specs: static_includes.add('#include ') - if service_specs: static_includes.add('#include ') - if action_specs: static_includes.add('#include ') }@ @[for value in sorted(static_includes)]@ @(value) @[end for]@ - @{ includes = {} for spec, subfolder in message_specs: @@ -57,7 +53,7 @@ for spec, subfolder in service_specs: @[for v in sorted(includes.values())]@ @(v) @[end for]@ - +@ @[for spec, subfolder in message_specs]@ @{ pkg_name = spec.base_type.pkg_name @@ -141,6 +137,7 @@ _register_msg_type__@(subfolder)__@(type_name)(PyObject * pymodule) return 0; } @[end for]@ +@ @[for spec, subfolder in service_specs]@ @{ type_name = convert_camel_case_to_lower_case_underscore(spec.srv_name) @@ -176,7 +173,7 @@ _register_srv_type__@(subfolder)__@(type_name)(PyObject * pymodule) return 0; } @[end for]@ - +@ @[for spec, subfolder in action_specs]@ @{ type_name = convert_camel_case_to_lower_case_underscore(spec.action_name) diff --git a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py index 47841d07..89351c84 100644 --- a/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py +++ b/rosidl_generator_py/rosidl_generator_py/generate_py_impl.py @@ -120,7 +120,7 @@ def generate_py(generator_arguments_file, typesupport_impls): path_to_module = os.path.join(args['output_dir'], subfolder, '__init__.py') - content = "" + content = '' if os.path.isfile(path_to_module): with open(path_to_module, 'r') as f: content = f.read() @@ -129,17 +129,16 @@ def generate_py(generator_arguments_file, typesupport_impls): if action_specs: block_name += '_action' content = re.sub( - r"# BEGIN %s$.*^# END %s" % (block_name, block_name), + r'# BEGIN %s$.*^# END %s' % (block_name, block_name), '', content, 0, re.M | re.S ) - content = re.sub(r"^\s*$", '', content, 0, re.M) - content += ''.join( + content = re.sub(r'^\s*$', '', content, 0, re.M) + content = ''.join( ['# BEGIN %s\n' % block_name] + - sorted(import_list.values()) + # import_line + sorted(import_list.values()) + # import_line sorted(import_list.keys()) + # noqa_line ['# END %s\n' % block_name] - ) - print(content) + ) + content f.write(content) for template_file, generated_filenames in mapping_msg_pkg_extension.items(): From 3e212cb8711a648f55cb6b532f91bdc1142444ff Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Fri, 11 Jan 2019 17:53:10 -0300 Subject: [PATCH 4/4] Attempts to fix CMake warnings in Windows. --- python_cmake_module/cmake/Modules/FindPythonExtra.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python_cmake_module/cmake/Modules/FindPythonExtra.cmake b/python_cmake_module/cmake/Modules/FindPythonExtra.cmake index be3d469f..96010381 100644 --- a/python_cmake_module/cmake/Modules/FindPythonExtra.cmake +++ b/python_cmake_module/cmake/Modules/FindPythonExtra.cmake @@ -39,6 +39,9 @@ set(PythonExtra_FOUND FALSE) +# Prevent find_package(PythonLibs) from getting confused. +unset(PYTHON_LIBRARY) + find_package(PythonInterp 3.5 REQUIRED) if(PYTHONINTERP_FOUND)