xref: /aosp_15_r20/external/pigweed/pw_build_mcuxpresso/py/pw_build_mcuxpresso/bazel.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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