Skip to content

Commit 55212db

Browse files
mergify[bot]frneerhidmic
authored
rosidl_cli: Add type description support (backport #857) (#867)
* rosidl_cli: Add type description support (#857) * Add type description support to rosidl_cli Signed-off-by: Francisco Rossi <frossi@ekumenlabs.com> * Fix typing annotation Signed-off-by: Francisco Rossi <frossi@ekumenlabs.com> * Please flake8, mypy Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> * Please flake8 take 2 Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> --------- Signed-off-by: Francisco Rossi <frossi@ekumenlabs.com> Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> Co-authored-by: Michel Hidalgo <michel@ekumenlabs.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com> (cherry picked from commit c9a3084) # Conflicts: # rosidl_cli/rosidl_cli/cli.py # rosidl_cli/rosidl_cli/command/generate/api.py # rosidl_cli/rosidl_cli/command/generate/extensions.py # rosidl_cli/rosidl_cli/command/helpers.py * Solve conflicts (#868) Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> * Please flake8 (#870) Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> --------- Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com> Co-authored-by: Francisco Rossi <50388097+frneer@users.noreply.github.com> Co-authored-by: Michel Hidalgo <michel@ekumenlabs.com>
1 parent 1d92ce3 commit 55212db

File tree

15 files changed

+509
-90
lines changed

15 files changed

+509
-90
lines changed

rosidl_cli/rosidl_cli/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import signal
1717

1818
from rosidl_cli.command.generate import GenerateCommand
19+
from rosidl_cli.command.hash import HashCommand
1920
from rosidl_cli.command.translate import TranslateCommand
2021
from rosidl_cli.common import get_first_line_doc
2122

@@ -74,7 +75,7 @@ def main():
7475
formatter_class=argparse.RawDescriptionHelpFormatter
7576
)
7677

77-
commands = [GenerateCommand(), TranslateCommand()]
78+
commands = [GenerateCommand(), TranslateCommand(), HashCommand()]
7879

7980
# add arguments for command extension(s)
8081
add_subparsers(

rosidl_cli/rosidl_cli/command/generate/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def add_arguments(self, parser):
3838
'-ts', '--type-support', metavar='TYPESUPPORT',
3939
dest='typesupports', action='append', default=[],
4040
help='Target type supports for generation.')
41+
parser.add_argument(
42+
'-td', '--type-description-file', metavar='PATH',
43+
dest='type_description_files', action='append', default=[],
44+
help='Target type descriptions for generation.')
4145
parser.add_argument(
4246
'-I', '--include-path', type=pathlib.Path, metavar='PATH',
4347
dest='include_paths', action='append', default=[],
@@ -57,5 +61,6 @@ def main(self, *, args):
5761
include_paths=args.include_paths,
5862
output_path=args.output_path,
5963
types=args.types,
60-
typesupports=args.typesupports
64+
typesupports=args.typesupports,
65+
type_description_files=args.type_description_files
6166
)

rosidl_cli/rosidl_cli/command/generate/api.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import inspect
1516
import os
1617
import pathlib
1718

18-
from .extensions import load_type_extensions
19-
from .extensions import load_typesupport_extensions
19+
from .extensions import load_type_extensions, load_typesupport_extensions
2020

2121

2222
def generate(
@@ -26,7 +26,8 @@ def generate(
2626
include_paths=None,
2727
output_path=None,
2828
types=None,
29-
typesupports=None
29+
typesupports=None,
30+
type_description_files=None
3031
):
3132
"""
3233
Generate source code from interface definition files.
@@ -57,6 +58,7 @@ def generate(
5758
source code files, defaults to the current working directory
5859
:param types: optional list of type representations to generate
5960
:param typesupports: optional list of type supports to generate
61+
:param type_description_files: Optional list of paths to type description files
6062
:returns: list of lists of paths to generated source code files,
6163
one group per type or type support extension invoked
6264
"""
@@ -85,15 +87,39 @@ def generate(
8587
else:
8688
os.makedirs(output_path, exist_ok=True)
8789

88-
if len(extensions) > 1:
89-
return [
90+
def extra_kwargs(func, **kwargs):
91+
matched_kwargs = {}
92+
signature = inspect.signature(func)
93+
for name, value in kwargs.items():
94+
if name in signature.parameters:
95+
if signature.parameters[name].kind not in [
96+
inspect.Parameter.POSITIONAL_ONLY,
97+
inspect.Parameter.VAR_POSITIONAL,
98+
inspect.Parameter.VAR_KEYWORD
99+
]:
100+
matched_kwargs[name] = value
101+
return matched_kwargs
102+
103+
generated_files = []
104+
if len(extensions) == 1:
105+
extension = extensions[0]
106+
generated_files.append(
90107
extension.generate(
91108
package_name, interface_files, include_paths,
92-
output_path=output_path / extension.name)
93-
for extension in extensions
94-
]
95-
96-
return [extensions[0].generate(
97-
package_name, interface_files,
98-
include_paths, output_path
99-
)]
109+
output_path=output_path,
110+
**extra_kwargs(extension.generate, type_description_files=type_description_files)
111+
)
112+
)
113+
else:
114+
for extension in extensions:
115+
generated_files.append(
116+
extension.generate(
117+
package_name, interface_files, include_paths,
118+
output_path=output_path / extension.name,
119+
**extra_kwargs(
120+
extension.generate,
121+
type_description_files=type_description_files
122+
)
123+
)
124+
)
125+
return generated_files

rosidl_cli/rosidl_cli/command/generate/extensions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ def generate(
2929
package_name,
3030
interface_files,
3131
include_paths,
32-
output_path
32+
output_path,
33+
type_description_files
3334
):
3435
"""
3536
Generate source code.
@@ -43,6 +44,7 @@ def generate(
4344
:param include_paths: list of paths to include dependency interface
4445
definition files from.
4546
:param output_path: path to directory to hold generated source code files
47+
:param type_description_files: Optional list of paths to type description files
4648
:returns: list of paths to generated source files
4749
"""
4850
raise NotImplementedError()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright 2021 Open Source Robotics Foundation, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pathlib
16+
17+
from rosidl_cli.command import Command
18+
19+
from .api import generate_type_hashes
20+
21+
22+
class HashCommand(Command):
23+
"""Generate type description hashes from interface definition files."""
24+
25+
name = 'hash'
26+
27+
def add_arguments(self, parser):
28+
parser.add_argument(
29+
'-o', '--output-path', metavar='PATH',
30+
type=pathlib.Path, default=None,
31+
help=('Path to directory to hold generated '
32+
"source code files. Defaults to '.'."))
33+
parser.add_argument(
34+
'-I', '--include-path', type=pathlib.Path, metavar='PATH',
35+
dest='include_paths', action='append', default=[],
36+
help='Paths to include dependency interface definition files from.')
37+
parser.add_argument(
38+
'package_name', help='Name of the package to generate code for')
39+
parser.add_argument(
40+
'interface_files', metavar='interface_file', nargs='+',
41+
help=('Relative path to an interface definition file. '
42+
"If prefixed by another path followed by a colon ':', "
43+
'path resolution is performed against such path.'))
44+
45+
def main(self, *, args):
46+
generate_type_hashes(
47+
package_name=args.package_name,
48+
interface_files=args.interface_files,
49+
include_paths=args.include_paths,
50+
output_path=args.output_path,
51+
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright 2021 Open Source Robotics Foundation, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pathlib
16+
17+
from .extensions import load_hash_extensions
18+
19+
20+
def generate_type_hashes(
21+
*,
22+
package_name,
23+
interface_files,
24+
include_paths=None,
25+
output_path=None,
26+
):
27+
"""
28+
Generate type hashes from interface definition files.
29+
30+
To do so, this function leverages type description hash generation support
31+
as provided by third-party package extensions.
32+
33+
Each path to an interface definition file is a relative path optionally
34+
prefixed by another path followed by a colon ':', against which the first
35+
relative path is to be resolved.
36+
37+
The directory structure that these relative paths exhibit will be replicated
38+
on output (as opposed to the prefix path, which will be ignored).
39+
40+
:param package_name: name of the package to generate hashes for
41+
:param interface_files: list of paths to interface definition files
42+
:param include_paths: optional list of paths to include dependency
43+
interface definition files from
44+
:param output_path: optional path to directory to hold generated
45+
source code files, defaults to the current working directory
46+
:returns: list of lists of paths to generated hashed json files,
47+
one group per type or type support extension invoked
48+
"""
49+
extensions = []
50+
extensions.extend(load_hash_extensions())
51+
52+
if include_paths is None:
53+
include_paths = []
54+
if output_path is None:
55+
output_path = pathlib.Path.cwd()
56+
else:
57+
pathlib.Path.mkdir(output_path, parents=True, exist_ok=True)
58+
59+
generated_hashes = []
60+
for extension in extensions:
61+
generated_hashes.extend(extension.generate_type_hashes(
62+
package_name,
63+
interface_files,
64+
include_paths,
65+
output_path=output_path,
66+
))
67+
68+
return generated_hashes
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2021 Open Source Robotics Foundation, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from rosidl_cli.extensions import Extension
16+
from rosidl_cli.extensions import load_extensions
17+
18+
19+
class HashCommandExtension(Extension):
20+
"""
21+
The extension point for source code generation.
22+
23+
The following methods must be defined:
24+
* `generate_type_hashes`
25+
"""
26+
27+
def generate_type_hashes(
28+
self,
29+
package_name,
30+
interface_files,
31+
include_paths,
32+
output_path,
33+
):
34+
"""
35+
Generate type hashes from interface definition files.
36+
37+
Paths to interface definition files are relative paths optionally
38+
prefixed by an absolute path followed by a colon ':', in which case
39+
path resolution is to be performed against that absolute path.
40+
41+
:param package_name: name of the package to generate source code for
42+
:param interface_files: list of paths to interface definition files
43+
:param include_paths: list of paths to include dependency interface
44+
definition files from.
45+
:param output_path: path to directory to hold generated source code files
46+
:returns: list of paths to generated source files
47+
"""
48+
raise NotImplementedError()
49+
50+
51+
def load_hash_extensions(**kwargs):
52+
"""Load extensions for type hash generation."""
53+
return load_extensions(
54+
'rosidl_cli.command.hash.extensions', **kwargs
55+
)

0 commit comments

Comments
 (0)