1# Copyright 2023 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://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, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Bazel output support.""" 15 16from typing import Any 17 18import pathlib 19 20try: 21 from pw_build_mcuxpresso.components import Project 22except ImportError: 23 # Load from this directory if pw_build_mcuxpresso is not available. 24 from components import Project # type: ignore 25 26 27def _bazel_bool_out(name: str, val: bool, indent: int = 0) -> None: 28 """Outputs boolean in Bazel format.""" 29 print(' ' * indent + f'{name} = "{val}",') 30 31 32def _bazel_int_out(name: str, val: int, indent: int = 0) -> None: 33 """Outputs integer in Bazel format.""" 34 print(' ' * indent + f'{name} = "{val}",') 35 36 37def _bazel_str(val: Any) -> str: 38 """Returns string in Bazel format with correct escaping.""" 39 return str(val).replace('"', r'\"').replace('$', r'\$') 40 41 42def _bazel_str_out(name: str, val: Any, indent: int = 0) -> None: 43 """Outputs string in Bazel format with correct escaping.""" 44 print(' ' * indent + f'{name} = "{_bazel_str(val)}",') 45 46 47def _bazel_str_list_out(name: str, vals: list[Any], indent: int = 0) -> None: 48 """Outputs list of strings in Bazel format with correct escaping.""" 49 if not vals: 50 return 51 52 print(' ' * indent + f'{name} = [') 53 for val in vals: 54 print(' ' * (indent + 1) + f'"{_bazel_str(val)}",') 55 print(' ' * indent + '],') 56 57 58def _bazel_path_list_out( 59 name: str, 60 vals: list[pathlib.Path], 61 path_prefix: str | None = None, 62 indent: int = 0, 63) -> None: 64 """Outputs list of paths in Bazel format with common prefix.""" 65 if path_prefix is not None: 66 str_vals = [f'{path_prefix}{str(val)}' for val in vals] 67 else: 68 str_vals = [str(f) for f in vals] 69 70 _bazel_str_list_out(name, sorted(set(str_vals)), indent=indent) 71 72 73def bazel_output( 74 project: Project, 75 name: str, 76 path_prefix: str | None = None, 77 extra_args: dict[str, Any] | None = None, 78): 79 """Output Bazel target for a project with the specified components. 80 81 Args: 82 project: MCUXpresso project to output. 83 name: target name to output. 84 path_prefix: string prefix to prepend to all paths. 85 extra_args: Dictionary of additional arguments to generated target. 86 """ 87 print('cc_library(') 88 _bazel_str_out('name', name, indent=1) 89 _bazel_path_list_out( 90 'srcs', 91 project.sources + project.libs, 92 path_prefix=path_prefix, 93 indent=1, 94 ) 95 _bazel_path_list_out( 96 'hdrs', project.headers, path_prefix=path_prefix, indent=1 97 ) 98 _bazel_str_list_out('defines', project.defines, indent=1) 99 _bazel_path_list_out( 100 'includes', project.include_dirs, path_prefix=path_prefix, indent=1 101 ) 102 103 for arg_name, arg_value in (extra_args or {}).items(): 104 if isinstance(arg_value, bool): 105 _bazel_bool_out(arg_name, arg_value, indent=1) 106 elif isinstance(arg_value, int): 107 _bazel_int_out(arg_name, arg_value, indent=1) 108 elif isinstance(arg_value, str): 109 _bazel_str_out(arg_name, arg_value, indent=1) 110 elif isinstance(arg_value, list): 111 if all(isinstance(x, str) for x in arg_value): 112 _bazel_str_list_out(arg_name, arg_value, indent=1) 113 else: 114 raise TypeError( 115 f"Can't handle extra arg {arg_name!r}: " 116 f"a list of {type(arg_value[0])}" 117 ) 118 else: 119 raise TypeError( 120 f"Can't handle extra arg {arg_name!r}: {type(arg_value)}" 121 ) 122 123 print(')') 124