|  | 
|  | 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 collections | 
|  | 16 | +import os | 
|  | 17 | +import pathlib | 
|  | 18 | + | 
|  | 19 | +from rosidl_cli.command import Command | 
|  | 20 | + | 
|  | 21 | +from .extensions import load_translate_extensions | 
|  | 22 | + | 
|  | 23 | + | 
|  | 24 | +class TranslateCommand(Command): | 
|  | 25 | +    """Translate interface definition files from one format to another.""" | 
|  | 26 | + | 
|  | 27 | +    name = 'translate' | 
|  | 28 | + | 
|  | 29 | +    def add_arguments(self, parser, cli_name): | 
|  | 30 | +        parser.add_argument( | 
|  | 31 | +            '-o', '--output-path', metavar='PATH', | 
|  | 32 | +            type=pathlib.Path, default=pathlib.Path.cwd(), | 
|  | 33 | +            help=('Path to directory to hold translated interface definition' | 
|  | 34 | +                  "files. Defaults to '.'.")) | 
|  | 35 | +        parser.add_argument( | 
|  | 36 | +            '--use', '--translator', metavar='TRANSLATOR_SPEC', | 
|  | 37 | +            dest='translator_specs', action='append', default=[], | 
|  | 38 | +            help=('Target translators, backed by tool extensions. ' | 
|  | 39 | +                  'Specified by name plus an optional PEP440 version ' | 
|  | 40 | +                  'specifier. If none is given, suitable ones among ' | 
|  | 41 | +                  'all available translators will be chosen.') | 
|  | 42 | +        ) | 
|  | 43 | +        parser.add_argument( | 
|  | 44 | +            '--to', '--output-format', metavar='FORMAT', | 
|  | 45 | +            dest='output_format', required=True, | 
|  | 46 | +            help=('Output format for translate interface definition files. ' | 
|  | 47 | +                  'Specified by name plus an optional PEP440 version.') | 
|  | 48 | +        ) | 
|  | 49 | +        parser.add_argument( | 
|  | 50 | +            '--from', '--input-format', metavar='FORMAT', | 
|  | 51 | +            dest='input_format', default=None, | 
|  | 52 | +            help=('Input format for all source interface definition files. ' | 
|  | 53 | +                  'Specified by name plus an optional PEP440 version. ' | 
|  | 54 | +                  'If not given, file extensions will be used to deduce ' | 
|  | 55 | +                  'the format of each interface definition file.') | 
|  | 56 | +        ) | 
|  | 57 | +        parser.add_argument( | 
|  | 58 | +            '-I', '--include-path', metavar='PATH', type=pathlib.Path, | 
|  | 59 | +            dest='include_paths', action='append', default=[], | 
|  | 60 | +            help='Paths to include dependency interface definition files from.' | 
|  | 61 | +        ) | 
|  | 62 | +        parser.add_argument( | 
|  | 63 | +            'package_name', | 
|  | 64 | +            help='Name of the package all interface files belong to') | 
|  | 65 | +        parser.add_argument( | 
|  | 66 | +            'interface_files', metavar='interface_file', nargs='+', | 
|  | 67 | +            help=('Normalized relative path to an interface definition file. ' | 
|  | 68 | +                  "If prefixed by another path followed by a colon ':', " | 
|  | 69 | +                  'path resolution is performed against such path.') | 
|  | 70 | +        ) | 
|  | 71 | + | 
|  | 72 | +    def main(self, *, parser, args): | 
|  | 73 | +        extensions = load_translate_extensions( | 
|  | 74 | +            specs=args.translator_specs, | 
|  | 75 | +            strict=any(args.translator_specs) | 
|  | 76 | +        ) | 
|  | 77 | +        if not extensions: | 
|  | 78 | +            return 'No translate extensions found' | 
|  | 79 | + | 
|  | 80 | +        if not args.input_format: | 
|  | 81 | +            interface_files_per_format = collections.defaultdict(list) | 
|  | 82 | +            for interface_file in args.interface_files: | 
|  | 83 | +                input_format = os.path.splitext(interface_file)[-1][1:] | 
|  | 84 | +                interface_files_per_format[input_format].append(interface_file) | 
|  | 85 | +        else: | 
|  | 86 | +            interface_files_per_format = { | 
|  | 87 | +                args.input_format: args.interface_files} | 
|  | 88 | + | 
|  | 89 | +        for input_format, interface_files in interface_files_per_format.items(): | 
|  | 90 | +            extension = next(( | 
|  | 91 | +                extension for extension in extensions | 
|  | 92 | +                if extension.input_format == input_format and \ | 
|  | 93 | +                extension.output_format == args.output_format | 
|  | 94 | +            ), None) | 
|  | 95 | + | 
|  | 96 | +            if not extension: | 
|  | 97 | +                return (f"Translation from '{input_format}' to " | 
|  | 98 | +                        f"'{args.output_format}' is not supported") | 
|  | 99 | + | 
|  | 100 | +            extension.translate( | 
|  | 101 | +                args.package_name, interface_files, | 
|  | 102 | +                args.include_paths, args.output_path) | 
0 commit comments