Skip to content

Commit

Permalink
Merge pull request #331 from ros2/idl-stage-6
Browse files Browse the repository at this point in the history
 add idl pipeline using a separate extension point
  • Loading branch information
dirk-thomas authored Nov 21, 2018
2 parents f56405d + cf3d10f commit 68eb2e1
Show file tree
Hide file tree
Showing 15 changed files with 723 additions and 53 deletions.
9 changes: 8 additions & 1 deletion rosidl_adapter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ if(BUILD_TESTING)
ament_lint_auto_find_test_dependencies()
endif()

ament_package()
ament_package(
CONFIG_EXTRAS "rosidl_adapter-extras.cmake"
)

install(
DIRECTORY cmake
DESTINATION share/${PROJECT_NAME}
)

install(PROGRAMS
scripts/msg2idl.py
Expand Down
67 changes: 67 additions & 0 deletions rosidl_adapter/cmake/rosidl_adapt_interfaces.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2018 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.

#
# Convert non-idl interface files into `.idl` files.
#
# The input file might be a `.msg` or `.srv` file.
#
# :param idl_var: the variable name to return the list of generated `.idl`
# files, each item is a tuple separated by a colon with an absolute base path
# and a path relative to that base path
# :type idl_var: string
# :param arguments_file: the path of the arguments file containing the paths of
# the non-idl files.
# :type arguments_file: string
# :param TARGET: the name of the generation target
# :type TARGET: string
#
# @public
#
function(rosidl_adapt_interfaces idl_var arguments_file)
cmake_parse_arguments(ARG "" "TARGET" ""
${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "rosidl_adapt_interfaces() called with unused "
"arguments: ${ARG_UNPARSED_ARGUMENTS}")
endif()

find_package(PythonInterp REQUIRED)
if(NOT PYTHON_EXECUTABLE)
message(FATAL_ERROR "Variable 'PYTHON_EXECUTABLE' must not be empty")
endif()

set(idl_output "${CMAKE_CURRENT_BINARY_DIR}/rosidl_adapter/${ARG_TARGET}.idls")
set(cmd
"${PYTHON_EXECUTABLE}" -m rosidl_adapter
--package-name ${PROJECT_NAME}
--arguments-file "${arguments_file}"
--output-dir "${CMAKE_CURRENT_BINARY_DIR}/rosidl_adapter/${PROJECT_NAME}"
--output-file "${idl_output}")
execute_process(
COMMAND ${cmd}
OUTPUT_QUIET
ERROR_VARIABLE error
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
string(REPLACE ";" " " cmd_str "${cmd}")
message(FATAL_ERROR
"execute_process(${cmd_str}) returned error code ${result}:\n${error}")
endif()

file(STRINGS "${idl_output}" idl_tuples)

set(${idl_var} ${idl_tuples} PARENT_SCOPE)
endfunction()
17 changes: 17 additions & 0 deletions rosidl_adapter/rosidl_adapter-extras.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2018 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.

# copied from rosidl_adapter/rosidl_adapter-extras.cmake

include("${rosidl_adapter_DIR}/rosidl_adapt_interfaces.cmake")
11 changes: 7 additions & 4 deletions rosidl_adapter/rosidl_adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ def convert_to_idl(package_dir, package_name, interface_file, output_dir):
if interface_file.suffix == '.msg':
from rosidl_adapter.msg import convert_msg_to_idl
return convert_msg_to_idl(
package_dir, package_name, interface_file,
output_dir / package_name / 'msg')
package_dir, package_name, interface_file, output_dir / 'msg')

if interface_file.suffix == '.srv':
from rosidl_adapter.srv import convert_srv_to_idl
return convert_srv_to_idl(
package_dir, package_name, interface_file,
output_dir / package_name / 'srv')
package_dir, package_name, interface_file, output_dir / 'srv')

if interface_file.suffix == '.action':
from rosidl_adapter.action import convert_action_to_idl
return convert_action_to_idl(
package_dir, package_name, interface_file, output_dir / 'action')

assert False, "Unsupported interface type '{interface_file.suffix}'" \
.format_map(locals())
19 changes: 19 additions & 0 deletions rosidl_adapter/rosidl_adapter/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2018 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.

import sys

from rosidl_adapter.main import main

sys.exit(main())
62 changes: 62 additions & 0 deletions rosidl_adapter/rosidl_adapter/action/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright 2018 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.

from rosidl_adapter.parser import parse_action_string
from rosidl_adapter.resource import expand_template


def convert_action_to_idl(package_dir, package_name, input_file, output_dir):
assert package_dir.is_absolute()
assert not input_file.is_absolute()
assert input_file.suffix == '.action'

abs_input_file = package_dir / input_file
print('Reading input file: {abs_input_file}'.format_map(locals()))
abs_input_file = package_dir / input_file
content = abs_input_file.read_text(encoding='utf-8')
action = parse_action_string(package_name, input_file.stem, content)

# HACK as long as the return action specification contains implicitly added
# fields they have to be skipped when generating .idl files
assert len(action.goal_service.request.fields) >= 1
assert action.goal_service.request.fields[0].name == 'uuid'
action.goal_service.request.fields.pop(0)
assert len(action.goal_service.response.fields) >= 2
assert action.goal_service.response.fields[0].name == 'accepted'
assert action.goal_service.response.fields[1].name == 'stamp'
action.goal_service.response.fields.pop(1)
action.goal_service.response.fields.pop(0)

assert len(action.result_service.request.fields) >= 1
assert action.result_service.request.fields[0].name == 'uuid'
action.result_service.request.fields.pop(0)
assert len(action.result_service.response.fields) >= 1
assert action.result_service.response.fields[0].name == 'status'
action.result_service.response.fields.pop(0)

assert len(action.feedback.fields) >= 1
assert action.feedback.fields[0].name == 'uuid'
action.feedback.fields.pop(0)

output_file = output_dir / input_file.with_suffix('.idl').name
abs_output_file = output_file.absolute()
print('Writing output file: {abs_output_file}'.format_map(locals()))
data = {
'pkg_name': package_name,
'relative_input_file': input_file,
'action': action,
}

expand_template('action.idl.em', data, output_file)
return output_file
60 changes: 60 additions & 0 deletions rosidl_adapter/rosidl_adapter/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2018 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.

import argparse
import json
import pathlib
import sys


from rosidl_adapter import convert_to_idl


def main(argv=sys.argv[1:]):
parser = argparse.ArgumentParser(
description='Convert interface files to .idl')
parser.add_argument(
'--package-name', required=True,
help='The name of the package')
parser.add_argument(
'--arguments-file', required=True,
help='The JSON file containing the non-idl tuples to convert to .idl')
parser.add_argument(
'--output-dir', required=True,
help='The base directory to create .idl files in')
parser.add_argument(
'--output-file', required=True,
help='The output file containing the tuples for the generated .idl '
'files')
args = parser.parse_args(argv)
output_dir = pathlib.Path(args.output_dir)
output_file = pathlib.Path(args.output_file)

with open(args.arguments_file, 'r') as h:
data = json.load(h)

idl_tuples = []
for non_idl_tuple in data['non_idl_tuples']:
# only take the filastrst : for separation, since the first tuple
# contains an absolute path which on Windows contains a colon
basepath, relative_path = non_idl_tuple.rsplit(':', 1)
abs_idl_file = convert_to_idl(
pathlib.Path(basepath), args.package_name,
pathlib.Path(relative_path), output_dir)
idl_tuples.append((output_dir, abs_idl_file.relative_to(output_dir)))

output_file.parent.mkdir(exist_ok=True)
with output_file.open('w') as h:
for basepath, relative_path in idl_tuples:
h.write('{basepath}:{relative_path}\n'.format_map(locals()))
42 changes: 42 additions & 0 deletions rosidl_adapter/rosidl_adapter/resource/action.idl.em
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// generated from rosidl_adapter/resource/action.idl.em
// with input from @(pkg_name)/@(relative_input_file)

@{
from rosidl_adapter.msg import get_include_file
include_files = set()
fields = action.goal_service.request.fields + \
action.goal_service.response.fields + \
action.result_service.request.fields + \
action.result_service.response.fields + \
action.feedback.fields
for field in fields:
include_file = get_include_file(field.type)
if include_file is not None:
include_files.add(include_file)
}@
@[for include_file in sorted(include_files)]@
#include "@(include_file)"
@[end for]@

module @(pkg_name) {
module action {
@{
TEMPLATE(
'struct.idl.em',
msg=action.goal_service.request,
)
}@
@{
TEMPLATE(
'struct.idl.em',
msg=action.result_service.response,
)
}@
@{
TEMPLATE(
'struct.idl.em',
msg=action.feedback,
)
}@
};
};
Loading

0 comments on commit 68eb2e1

Please sign in to comment.