xref: /aosp_15_r20/external/pigweed/pw_presubmit/py/pw_presubmit/bazel_checks.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"""Bazel related checks."""
15
16from . import build, presubmit, presubmit_context
17
18
19def includes_presubmit_check(
20    targets: str = '//...',
21) -> presubmit.Check:
22    """Presubmit check verifying cc_library targets don't have includes.
23
24    See https://pwbug.dev/378564135 for a discussion of why the includes
25    attribute should be avoided.
26
27    Args:
28      targets: A Bazel target pattern
29        (https://bazel.build/run/build#specifying-build-targets).
30    """
31
32    @presubmit.check(name='no_includes')
33    def includes_check(
34        ctx: presubmit_context.PresubmitContext,
35    ):
36        # This query returns the subset of `targets` which,
37        #
38        # * are of type cc_library, and
39        # * have a nonempty list as the `includes` attribute.
40        #
41        # `bazel query` will print the targets matching these conditions, one
42        # per line.
43        query = (
44            r'attr("includes", "[.{1,}]", kind(cc_library, ' + targets + '))'
45        )
46
47        # Ideally we would just use a io.StringIO here instead of creating the
48        # files, but build.bazel doesn't actually accept _any_ TextIO object as
49        # stdout. It requires an io.TextIOWrapper, which must have a .name
50        # attribute that io.StringIO doesn't possess.
51        ctx.output_dir.mkdir(exist_ok=True, parents=True)
52        stdout_path = ctx.output_dir / 'bazel_includes.stdout'
53        stderr_path = ctx.output_dir / 'bazel_includes.stderr'
54        with open(stdout_path, 'w') as stdout, open(stderr_path, 'w') as stderr:
55            build.bazel(ctx, 'query', query, stdout=stdout, stderr=stderr)
56
57        with open(stdout_path, 'r') as stdout:
58            contents = stdout.readlines()
59
60        if not contents:
61            # The check has passed: the query returned no targets.
62            return
63
64        with open(ctx.failure_summary_log, 'w') as outs:
65            print(
66                'The following cc_library targets contain the `includes` '
67                'attribute. This attribute is forbidden. Use '
68                '`strip_include_prefix` instead.',
69                file=outs,
70            )
71            print('', file=outs)
72            for target in contents:
73                print(target.strip(), file=outs)
74
75        print(ctx.failure_summary_log.read_text(), end=None)
76        raise presubmit_context.PresubmitFailure(
77            'cc_library targets contain includes'
78        )
79
80    return includes_check
81