Skip to content

Commit

Permalink
Adds CMake pipeline bits for action generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
hidmic committed Jan 11, 2019
1 parent 2770a78 commit 234ce1a
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 22 deletions.
2 changes: 1 addition & 1 deletion rosidl_generator_py/cmake/custom_command.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
5 changes: 5 additions & 0 deletions rosidl_generator_py/cmake/register_py.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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}")

Expand Down
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion rosidl_generator_py/resource/_action.py.em
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@

@{
static_includes = set([
'#include <rosidl_generator_c/message_type_support_struct.h>',
'#include <rosidl_generator_c/visibility_control.h>',
])
for spec, subfolder in message_specs:
if subfolder == 'msg':
static_includes.add('#include <rosidl_generator_c/message_type_support_struct.h>')
elif subfolder == 'srv' or subfolder == 'action':
static_includes.add('#include <rosidl_generator_c/service_type_support_struct.h>')
if subfolder == 'action':
static_includes.add('#include <rosidl_generator_c/action_type_support_struct.h>')

if message_specs:
static_includes.add('#include <rosidl_generator_c/message_type_support_struct.h>')

if service_specs:
static_includes.add('#include <rosidl_generator_c/service_type_support_struct.h>')

if action_specs:
static_includes.add('#include <rosidl_generator_c/action_type_support_struct.h>')
}@
@[for value in sorted(static_includes)]@
@(value)
Expand Down
37 changes: 30 additions & 7 deletions rosidl_generator_py/rosidl_generator_py/generate_py_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,24 +118,46 @@ 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,
'typesupport_impl': type_support_impl_by_filename.get(generated_filename, ''),
}
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)
Expand Down
Loading

0 comments on commit 234ce1a

Please sign in to comment.