| # Copyright 2022 The Pigweed Authors |
| # |
| # 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 |
| # |
| # https://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. |
| """Options file parsing for proto generation.""" |
| |
| from fnmatch import fnmatchcase |
| from pathlib import Path |
| import re |
| from typing import List, Tuple |
| |
| from google.protobuf import text_format |
| |
| from pw_protobuf_codegen_protos.options_pb2 import Options |
| |
| _MULTI_LINE_COMMENT_RE = re.compile(r'/\*.*?\*/', flags=re.MULTILINE) |
| _SINGLE_LINE_COMMENT_RE = re.compile(r'//.*?$', flags=re.MULTILINE) |
| _SHELL_STYLE_COMMENT_RE = re.compile(r'#.*?$', flags=re.MULTILINE) |
| |
| |
| def load_options_from(options: List[Tuple[str, Options]], |
| options_file_name: Path): |
| """Loads a single .options file for the given .proto""" |
| with open(options_file_name) as options_file: |
| # Read the options file and strip all styles of comments before parsing. |
| options_data = options_file.read() |
| options_data = _MULTI_LINE_COMMENT_RE.sub('', options_data) |
| options_data = _SINGLE_LINE_COMMENT_RE.sub('', options_data) |
| options_data = _SHELL_STYLE_COMMENT_RE.sub('', options_data) |
| |
| for line in options_data.split('\n'): |
| parts = line.strip().split(None, 1) |
| if len(parts) < 2: |
| continue |
| |
| # Parse as a name glob followed by a protobuf text format. |
| try: |
| opts = Options() |
| text_format.Merge(parts[1], opts) |
| options.append((parts[0], opts)) |
| except: # pylint: disable=bare-except |
| continue |
| |
| |
| def load_options(include_paths: List[Path], |
| proto_file_name: Path) -> List[Tuple[str, Options]]: |
| """Loads the .options for the given .proto.""" |
| options: List[Tuple[str, Options]] = [] |
| |
| for include_path in include_paths: |
| options_file_name = include_path / proto_file_name.with_suffix( |
| '.options') |
| if options_file_name.exists(): |
| load_options_from(options, options_file_name) |
| |
| return options |
| |
| |
| def match_options(name: str, options: List[Tuple[str, Options]]) -> Options: |
| """Return the matching options for a name.""" |
| matched = Options() |
| for name_glob, mask_options in options: |
| if fnmatchcase(name, name_glob): |
| matched.MergeFrom(mask_options) |
| |
| return matched |