xref: /aosp_15_r20/external/pigweed/pw_presubmit/py/pw_presubmit/format/gn.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1# Copyright 2024 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"""Code formatter plugin for GN build files."""
15
16from pathlib import Path
17from typing import Final, Iterable, Iterator, Sequence, Tuple
18
19from pw_presubmit.format.core import (
20    FileFormatter,
21    FormattedFileContents,
22    FormatFixStatus,
23)
24
25
26class GnFormatter(FileFormatter):
27    """A formatter that runs `gn format` on files."""
28
29    DEFAULT_FLAGS: Final[Sequence[str]] = ()
30
31    def __init__(self, tool_flags: Sequence[str] = DEFAULT_FLAGS, **kwargs):
32        super().__init__(**kwargs)
33        self.gn_format_flags = list(tool_flags)
34
35    def format_file_in_memory(
36        self, file_path: Path, file_contents: bytes
37    ) -> FormattedFileContents:
38        """Uses ``gn format`` to check the formatting of the requested file.
39
40        The file at ``file_path`` is NOT modified by this check.
41
42        Returns:
43            A populated
44            :py:class:`pw_presubmit.format.core.FormattedFileContents` that
45            contains either the result of formatting the file, or an error
46            message.
47        """
48        # `gn format --dry-run` only signals which files need to be updated,
49        # not what the effects are. To get the formatted result, we pipe the
50        # file in via stdin and then return the result.
51        proc = self.run_tool(
52            'gn',
53            ['format'] + self.gn_format_flags + ['--stdin'],
54            input=file_contents,
55        )
56        ok = proc.returncode == 0
57        return FormattedFileContents(
58            ok=ok,
59            formatted_file_contents=proc.stdout if ok else b'',
60            # `gn format` emits errors over stdout, not stderr.
61            error_message=None if ok else proc.stdout.decode(),
62        )
63
64    def format_file(self, file_path: Path) -> FormatFixStatus:
65        """Formats the provided file in-place using ``gn format``.
66
67        Returns:
68            A FormatFixStatus that contains relevant errors/warnings.
69        """
70        proc = self.run_tool(
71            'gn',
72            ['format'] + self.gn_format_flags + [file_path],
73        )
74        ok = proc.returncode == 0
75        return FormatFixStatus(
76            ok=ok,
77            # `gn format` emits errors over stdout, not stderr.
78            error_message=None if ok else proc.stdout.decode(),
79        )
80
81    def format_files(
82        self, paths: Iterable[Path], keep_warnings: bool = True
83    ) -> Iterator[Tuple[Path, FormatFixStatus]]:
84        """Uses ``gn format`` to fix formatting of the specified files in-place.
85
86        Returns:
87            An iterator of ``Path`` and
88            :py:class:`pw_presubmit.format.core.FormatFixStatus` pairs for each
89            file that was not successfully formatted. If ``keep_warnings`` is
90            ``True``, any successful format operations with warnings will also
91            be returned.
92        """
93        # Try to format all files in one `gn` command.
94        proc = self.run_tool(
95            'gn',
96            ['format'] + self.gn_format_flags + list(paths),
97        )
98
99        # If there's an error, fall back to per-file formatting to figure out
100        # which file has problems.
101        if proc.returncode != 0:
102            yield from super().format_files(paths, keep_warnings)
103