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