diff --git a/rosidl_adapter_proto/bin/rosidl_adapter_proto b/rosidl_adapter_proto/bin/rosidl_adapter_proto
index bf49b23..f99c314 100644
--- a/rosidl_adapter_proto/bin/rosidl_adapter_proto
+++ b/rosidl_adapter_proto/bin/rosidl_adapter_proto
@@ -23,9 +23,8 @@ import sys
import pathlib
import os
-from rosidl_cmake import read_generator_arguments
-from rosidl_adapter_proto import generate_proto
-from rosidl_adapter_proto import compile_proto
+from rosidl_adapter_proto.cli import convert_to_proto
+
def main(argv=sys.argv[1:]):
parser = argparse.ArgumentParser(
@@ -41,47 +40,12 @@ def main(argv=sys.argv[1:]):
help='Path to the protoc executable')
args = parser.parse_args(argv)
-
- generator_args = read_generator_arguments(args.generator_arguments_file)
-
- # Generate .proto files
- rc = generate_proto(args.generator_arguments_file)
- if rc:
- return rc
-
- # Compile .proto files using protoc
- cpp_out_dir = str(pathlib.Path(generator_args["output_dir"] + "/..").resolve())
- proto_path_list = [str(pathlib.Path(generator_args["output_dir"] + "/..").resolve())]
- proto_files = []
- package_name = generator_args["package_name"]
-
- if "additional_files" in generator_args:
- proto_path_list += generator_args["additional_files"]
-
- pathlib.Path(cpp_out_dir).mkdir(parents=True, exist_ok=True)
-
- for idl_tuple in generator_args.get('idl_tuples', []):
- idl_parts = idl_tuple.rsplit(':', 1)
- assert len(idl_parts) == 2
- idl_rel_path = pathlib.Path(idl_parts[1])
- idl_stem = idl_rel_path.stem
- generated_file = os.path.join(
- generator_args['output_dir'],
- str(idl_rel_path.parent),
- idl_stem + ".proto"
- )
- proto_files.append(str(pathlib.Path(generated_file).resolve()))
-
- # compile proto files with protoc
- rc = compile_proto(protoc_path = args.protoc_path,
- proto_path_list = proto_path_list,
- cpp_out_dir = cpp_out_dir,
- proto_files = proto_files,
- package_name = package_name
- )
+ try:
+ convert_to_proto(args.generator_arguments_file, args.protoc_path)
+ except RuntimeError:
+ return 1
- return rc
-
+ return 0
if __name__ == '__main__':
sys.exit(main())
diff --git a/rosidl_adapter_proto/rosidl_adapter_proto/cli.py b/rosidl_adapter_proto/rosidl_adapter_proto/cli.py
new file mode 100644
index 0000000..7fd452d
--- /dev/null
+++ b/rosidl_adapter_proto/rosidl_adapter_proto/cli.py
@@ -0,0 +1,157 @@
+import argparse
+import os
+import pathlib
+import sys
+
+from ament_index_python import get_package_share_directory
+
+from catkin_pkg.package import package_exists_at
+from catkin_pkg.package import parse_package
+
+from rosidl_adapter.action import convert_action_to_idl
+from rosidl_adapter.msg import convert_msg_to_idl
+from rosidl_adapter.srv import convert_srv_to_idl
+
+from rosidl_cmake import read_generator_arguments
+
+from rosidl_cli.command.helpers import interface_path_as_tuple
+from rosidl_cli.command.helpers import legacy_generator_arguments_file
+from rosidl_cli.command.translate.extensions import TranslateCommandExtension
+from rosidl_cli.command.translate.api import translate
+
+from rosidl_adapter_proto import generate_proto
+from rosidl_adapter_proto import compile_proto
+
+def convert_to_proto(generator_arguments_file, protoc_path):
+ generator_args = read_generator_arguments(generator_arguments_file)
+
+ # Generate .proto files
+ rc = generate_proto(generator_arguments_file)
+ if rc :
+ raise RuntimeError
+
+ # Compile .proto files using protoc
+ cpp_out_dir = str(pathlib.Path(generator_args["output_dir"] + "/..").resolve())
+ proto_path_list = [str(pathlib.Path(generator_args["output_dir"] + "/..").resolve())]
+ proto_files = []
+ package_name = generator_args["package_name"]
+
+ if "additional_files" in generator_args:
+ proto_path_list += generator_args["additional_files"]
+
+ pathlib.Path(cpp_out_dir).mkdir(parents=True, exist_ok=True)
+
+ for idl_tuple in generator_args.get('idl_tuples', []):
+ idl_parts = idl_tuple.rsplit(':', 1)
+ assert len(idl_parts) == 2
+ idl_rel_path = pathlib.Path(idl_parts[1])
+ idl_stem = idl_rel_path.stem
+ generated_file = os.path.join(
+ generator_args['output_dir'],
+ str(idl_rel_path.parent),
+ idl_stem + ".proto"
+ )
+ proto_files.append(str(pathlib.Path(generated_file).resolve()))
+
+ # compile proto files with protoc
+ rc = compile_proto(protoc_path = protoc_path,
+ proto_path_list = proto_path_list,
+ cpp_out_dir = cpp_out_dir,
+ proto_files = proto_files,
+ package_name = package_name
+ )
+ if rc :
+ raise RuntimeError
+
+ return proto_files
+
+
+def convert_files_to_idl(extension, conversion_function, argv=sys.argv[1:]):
+ parser = argparse.ArgumentParser(
+ description=f'Convert {extension} files to .idl')
+ parser.add_argument(
+ 'interface_files', nargs='+',
+ help='The interface files to convert')
+ args = parser.parse_args(argv)
+
+ for interface_file in args.interface_files:
+ interface_file = pathlib.Path(interface_file)
+ package_dir = interface_file.parent.absolute()
+ while (
+ len(package_dir.parents) and
+ not package_exists_at(str(package_dir))
+ ):
+ package_dir = package_dir.parent
+ if not package_dir.parents:
+ print(
+ f"Could not find package for '{interface_file}'",
+ file=sys.stderr)
+ continue
+ warnings = []
+ pkg = parse_package(package_dir, warnings=warnings)
+
+ conversion_function(
+ package_dir, pkg.name,
+ interface_file.absolute().relative_to(package_dir),
+ interface_file.parent)
+
+
+class TranslateToProto(TranslateCommandExtension):
+
+ output_format = 'proto'
+
+ def translate(
+ self,
+ package_name,
+ interface_files,
+ include_paths,
+ output_path
+ ):
+
+ generated_files = []
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('rosidl_adapter_proto'))
+ templates_path = package_share_path / 'resource'
+
+
+ idl_interface_files = []
+ non_idl_interface_files = []
+ for path in interface_files:
+ if not path.endswith('.idl'):
+ non_idl_interface_files.append(path)
+ else:
+ idl_interface_files.append(path)
+ if non_idl_interface_files:
+ idl_interface_files.extend(translate(
+ package_name=package_name,
+ interface_files=non_idl_interface_files,
+ include_paths=include_paths,
+ output_format='idl',
+ output_path=output_path / 'tmp',
+ ))
+
+
+ # Generate code
+ with legacy_generator_arguments_file(
+ package_name=package_name,
+ interface_files=idl_interface_files,
+ include_paths=include_paths,
+ templates_path=templates_path,
+ output_path=output_path
+ ) as path_to_arguments_file:
+ generated_files.extend(convert_to_proto(path_to_arguments_file, '/usr/bin/protoc'))
+ return generated_files
+
+
+
+class TranslateMsgToProto(TranslateToProto):
+ input_format = 'msg'
+
+
+class TranslateSrvToProto(TranslateToProto):
+ input_format = 'srv'
+
+
+class TranslateActionToProto(TranslateToProto):
+ input_format = 'action'
diff --git a/rosidl_adapter_proto/setup.cfg b/rosidl_adapter_proto/setup.cfg
new file mode 100644
index 0000000..b0a21ca
--- /dev/null
+++ b/rosidl_adapter_proto/setup.cfg
@@ -0,0 +1,5 @@
+[options.entry_points]
+rosidl_cli.command.translate.extensions =
+ msg2proto = rosidl_adapter_proto.cli:TranslateMsgToProto
+ srv2proto = rosidl_adapter_proto.cli:TranslateSrvToProto
+ action2proto = rosidl_adapter_proto.cli:TranslateActionToProto
diff --git a/rosidl_adapter_proto/test/test_rosidl_cli_translate.py b/rosidl_adapter_proto/test/test_rosidl_cli_translate.py
new file mode 100644
index 0000000..a996569
--- /dev/null
+++ b/rosidl_adapter_proto/test/test_rosidl_cli_translate.py
@@ -0,0 +1,30 @@
+import pytest
+import os
+import pathlib
+import shutil
+import subprocess
+
+from ament_index_python import get_package_share_directory
+
+def test_ts_files_generation():
+ build_then_install('/tmp/tr')
+ files_exists('/tmp/tr')
+
+def build_then_install(output_path):
+ if os.path.exists(output_path):
+ shutil.rmtree(output_path)
+ os.makedirs(output_path)
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('std_msgs'))
+
+ subprocess.run([
+ 'rosidl', 'translate', '-o', output_path, '--from', 'msg', '--to', 'proto', 'std_msgs', './msg/String.msg'
+ ], cwd=package_share_path, check=True)
+
+def files_exists(output_path):
+ assert pathlib.Path('/tmp/tr/msg/String.pb.cc').exists()
+ assert pathlib.Path('/tmp/tr/msg/String.pb.h').exists()
+ assert pathlib.Path('/tmp/tr/msg/String.proto').exists()
+ assert pathlib.Path('/tmp/tr/tmp/msg/String.idl').exists()
+
\ No newline at end of file
diff --git a/rosidl_typesupport_protobuf_c/CMakeLists.txt b/rosidl_typesupport_protobuf_c/CMakeLists.txt
index dd473d2..97047bf 100644
--- a/rosidl_typesupport_protobuf_c/CMakeLists.txt
+++ b/rosidl_typesupport_protobuf_c/CMakeLists.txt
@@ -115,6 +115,8 @@ if(BUILD_TESTING)
target_link_libraries(test_wstring_conversion
${PROJECT_NAME})
endif()
+ find_package(ament_cmake_pytest REQUIRED)
+ ament_add_pytest_test(test_rosidl_cli_ts_pb_c test/test_rosidl_cli_ts_pb_c.py)
endif()
ament_package(
diff --git a/rosidl_typesupport_protobuf_c/package.xml b/rosidl_typesupport_protobuf_c/package.xml
index f61936a..a98fa3a 100644
--- a/rosidl_typesupport_protobuf_c/package.xml
+++ b/rosidl_typesupport_protobuf_c/package.xml
@@ -30,6 +30,7 @@
rosidl_typesupport_interface
ament_cmake_gtest
+ ament_cmake_pytest
rosidl_typesupport_c_packages
diff --git a/rosidl_typesupport_protobuf_c/rosidl_typesupport_protobuf_c/cli.py b/rosidl_typesupport_protobuf_c/rosidl_typesupport_protobuf_c/cli.py
new file mode 100644
index 0000000..877cfd6
--- /dev/null
+++ b/rosidl_typesupport_protobuf_c/rosidl_typesupport_protobuf_c/cli.py
@@ -0,0 +1,72 @@
+import pathlib
+
+from ament_index_python import get_package_share_directory
+
+from rosidl_cli.command.generate.extensions import GenerateCommandExtension
+from rosidl_cli.command.helpers import generate_visibility_control_file
+from rosidl_cli.command.helpers import legacy_generator_arguments_file
+from rosidl_cli.command.translate.api import translate
+
+from rosidl_typesupport_protobuf_c import generate_typesupport_protobuf_c
+
+
+class GenerateProtobufCTypesupport(GenerateCommandExtension):
+
+ def generate(
+ self,
+ package_name,
+ interface_files,
+ include_paths,
+ output_path
+ ):
+ generated_files = []
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('rosidl_typesupport_protobuf_c'))
+ templates_path = package_share_path / 'resource'
+
+ # Normalize interface definition format to .idl
+ idl_interface_files = []
+ non_idl_interface_files = []
+ for path in interface_files:
+ if not path.endswith('.idl'):
+ non_idl_interface_files.append(path)
+ else:
+ idl_interface_files.append(path)
+ if non_idl_interface_files:
+ idl_interface_files.extend(translate(
+ package_name=package_name,
+ interface_files=non_idl_interface_files,
+ include_paths=include_paths,
+ output_format='idl',
+ output_path=output_path / 'tmp',
+ ))
+
+ # Generate visibility control file
+ visibility_control_file_template_path = \
+ 'rosidl_typesupport_protobuf_c__visibility_control.h.in'
+ visibility_control_file_template_path = \
+ templates_path / visibility_control_file_template_path
+ visibility_control_file_path = \
+ 'rosidl_typesupport_protobuf_c__visibility_control.h'
+ visibility_control_file_path = \
+ output_path / 'msg' / visibility_control_file_path
+
+ generate_visibility_control_file(
+ package_name=package_name,
+ template_path=visibility_control_file_template_path,
+ output_path=visibility_control_file_path
+ )
+ generated_files.append(visibility_control_file_path)
+
+ # Generate code
+ with legacy_generator_arguments_file(
+ package_name=package_name,
+ interface_files=idl_interface_files,
+ include_paths=include_paths,
+ templates_path=templates_path,
+ output_path=output_path
+ ) as path_to_arguments_file:
+ generated_files.extend(generate_typesupport_protobuf_c(path_to_arguments_file))
+
+ return generated_files
diff --git a/rosidl_typesupport_protobuf_c/setup.cfg b/rosidl_typesupport_protobuf_c/setup.cfg
new file mode 100644
index 0000000..80b42be
--- /dev/null
+++ b/rosidl_typesupport_protobuf_c/setup.cfg
@@ -0,0 +1,3 @@
+[options.entry_points]
+rosidl_cli.command.generate.typesupport_extensions =
+ protobuf_c = rosidl_typesupport_protobuf_c.cli:GenerateProtobufCTypesupport
diff --git a/rosidl_typesupport_protobuf_c/test/test_rosidl_cli_ts_pb_c.py b/rosidl_typesupport_protobuf_c/test/test_rosidl_cli_ts_pb_c.py
new file mode 100644
index 0000000..ca69223
--- /dev/null
+++ b/rosidl_typesupport_protobuf_c/test/test_rosidl_cli_ts_pb_c.py
@@ -0,0 +1,30 @@
+import pytest
+import os
+import pathlib
+import shutil
+import subprocess
+
+from ament_index_python import get_package_share_directory
+
+def test_ts_files_generation():
+ build_then_install('/tmp/pb')
+ files_exists('/tmp/pb')
+
+def build_then_install(output_path):
+ if os.path.exists(output_path):
+ shutil.rmtree(output_path)
+ os.makedirs(output_path)
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('std_msgs'))
+
+ subprocess.run([
+ 'rosidl', 'generate', '-ts', 'protobuf_c', '-o', output_path, 'std_msgs', './msg/String.msg'
+ ], cwd=package_share_path, check=True)
+
+def files_exists(output_path):
+ assert pathlib.Path('/tmp/pb/tmp/msg/String.idl').exists()
+ assert pathlib.Path('/tmp/pb/msg/rosidl_typesupport_protobuf_c__visibility_control.h').exists()
+ assert pathlib.Path('/tmp/pb/msg/string__rosidl_typesupport_protobuf_c.hpp').exists()
+ assert pathlib.Path('/tmp/pb/msg/detail/string__type_support.cpp').exists()
+
\ No newline at end of file
diff --git a/rosidl_typesupport_protobuf_cpp/CMakeLists.txt b/rosidl_typesupport_protobuf_cpp/CMakeLists.txt
index ccac6d0..85072b9 100644
--- a/rosidl_typesupport_protobuf_cpp/CMakeLists.txt
+++ b/rosidl_typesupport_protobuf_cpp/CMakeLists.txt
@@ -96,6 +96,8 @@ if(BUILD_TESTING)
target_link_libraries(test_wstring_conversion
${PROJECT_NAME})
endif()
+ find_package(ament_cmake_pytest REQUIRED)
+ ament_add_pytest_test(test_rosidl_cli_ts_pb_cpp test/test_rosidl_cli_ts_pb_cpp.py)
endif()
ament_package(CONFIG_EXTRAS "cmake/rosidl_typesupport_protobuf_cpp-extras.cmake.in")
diff --git a/rosidl_typesupport_protobuf_cpp/package.xml b/rosidl_typesupport_protobuf_cpp/package.xml
index 485900b..b2f1943 100644
--- a/rosidl_typesupport_protobuf_cpp/package.xml
+++ b/rosidl_typesupport_protobuf_cpp/package.xml
@@ -29,6 +29,7 @@
rosidl_typesupport_protobuf
ament_cmake_gtest
+ ament_cmake_pytest
rosidl_typesupport_cpp_packages
diff --git a/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/__init__.py b/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/__init__.py
index 806324c..4252ef8 100644
--- a/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/__init__.py
+++ b/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/__init__.py
@@ -15,9 +15,6 @@
# limitations under the License.
#
# ================================= Apache 2.0 =================================
-
-from rosidl_cmake import generate_files
-
from rosidl_pycommon import generate_files
def get_template_mapping():
diff --git a/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/cli.py b/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/cli.py
new file mode 100644
index 0000000..51239f3
--- /dev/null
+++ b/rosidl_typesupport_protobuf_cpp/rosidl_typesupport_protobuf_cpp/cli.py
@@ -0,0 +1,72 @@
+import pathlib
+
+from ament_index_python import get_package_share_directory
+
+from rosidl_cli.command.generate.extensions import GenerateCommandExtension
+from rosidl_cli.command.helpers import generate_visibility_control_file
+from rosidl_cli.command.helpers import legacy_generator_arguments_file
+from rosidl_cli.command.translate.api import translate
+
+from rosidl_typesupport_protobuf_cpp import generate_cpp
+
+
+class GenerateProtobufCppTypesupport(GenerateCommandExtension):
+
+ def generate(
+ self,
+ package_name,
+ interface_files,
+ include_paths,
+ output_path
+ ):
+ generated_files = []
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('rosidl_typesupport_protobuf_cpp'))
+ templates_path = package_share_path / 'resource'
+
+ # Normalize interface definition format to .idl
+ idl_interface_files = []
+ non_idl_interface_files = []
+ for path in interface_files:
+ if not path.endswith('.idl'):
+ non_idl_interface_files.append(path)
+ else:
+ idl_interface_files.append(path)
+ if non_idl_interface_files:
+ idl_interface_files.extend(translate(
+ package_name=package_name,
+ interface_files=non_idl_interface_files,
+ include_paths=include_paths,
+ output_format='idl',
+ output_path=output_path / 'tmp',
+ ))
+
+ # Generate visibility control file
+ visibility_control_file_template_path = \
+ 'rosidl_typesupport_protobuf_cpp__visibility_control.h.in'
+ visibility_control_file_template_path = \
+ templates_path / visibility_control_file_template_path
+ visibility_control_file_path = \
+ 'rosidl_typesupport_protobuf_cpp__visibility_control.h'
+ visibility_control_file_path = \
+ output_path / 'msg' / visibility_control_file_path
+
+ generate_visibility_control_file(
+ package_name=package_name,
+ template_path=visibility_control_file_template_path,
+ output_path=visibility_control_file_path
+ )
+ generated_files.append(visibility_control_file_path)
+
+ # Generate code
+ with legacy_generator_arguments_file(
+ package_name=package_name,
+ interface_files=idl_interface_files,
+ include_paths=include_paths,
+ templates_path=templates_path,
+ output_path=output_path
+ ) as path_to_arguments_file:
+ generated_files.extend(generate_cpp(path_to_arguments_file))
+
+ return generated_files
diff --git a/rosidl_typesupport_protobuf_cpp/setup.cfg b/rosidl_typesupport_protobuf_cpp/setup.cfg
new file mode 100644
index 0000000..b272e07
--- /dev/null
+++ b/rosidl_typesupport_protobuf_cpp/setup.cfg
@@ -0,0 +1,3 @@
+[options.entry_points]
+rosidl_cli.command.generate.typesupport_extensions =
+ protobuf_cpp = rosidl_typesupport_protobuf_cpp.cli:GenerateProtobufCppTypesupport
diff --git a/rosidl_typesupport_protobuf_cpp/test/test_rosidl_cli_ts_pb_cpp.py b/rosidl_typesupport_protobuf_cpp/test/test_rosidl_cli_ts_pb_cpp.py
new file mode 100644
index 0000000..d4bc740
--- /dev/null
+++ b/rosidl_typesupport_protobuf_cpp/test/test_rosidl_cli_ts_pb_cpp.py
@@ -0,0 +1,31 @@
+import pytest
+import os
+import pathlib
+import shutil
+import subprocess
+
+from ament_index_python import get_package_share_directory
+
+def test_ts_files_generation():
+ build_then_install('/tmp/pb')
+ files_exists('/tmp/pb')
+
+def build_then_install(output_path):
+ if os.path.exists(output_path):
+ shutil.rmtree(output_path)
+ os.makedirs(output_path)
+
+ package_share_path = pathlib.Path(
+ get_package_share_directory('std_msgs'))
+
+ subprocess.run([
+ 'rosidl', 'generate', '-ts', 'protobuf_cpp', '-o', output_path, 'std_msgs', './msg/String.msg'
+ ], cwd=package_share_path, check=True)
+
+def files_exists(output_path):
+ assert pathlib.Path('/tmp/pb/tmp/msg/String.idl').exists()
+ assert pathlib.Path('/tmp/pb/msg/rosidl_typesupport_protobuf_cpp__visibility_control.h').exists()
+ assert pathlib.Path('/tmp/pb/msg/string__rosidl_typesupport_protobuf_cpp.hpp').exists()
+ assert pathlib.Path('/tmp/pb/msg/string__typeadapter_protobuf_cpp.hpp').exists()
+ assert pathlib.Path('/tmp/pb/msg/detail/string__type_support.cpp').exists()
+
\ No newline at end of file