#!/usr/bin/env python3 # # Copyright (c) 2022 Project CHIP Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Check #include files. Reads from standard input the output of a `grep -n` for `#include`s; see `check_includes.sh`. Uses the conditions defined in `check_includes_config.py`. """ import re import sys from typing import Iterable, Pattern import check_includes_config as config # The input comes from `grep -n` and has the form # filename:line:include-directive # # This RE does not handle C-style comments before the file name. # So don't do that. _MATCH_SPLIT_RE = re.compile(r""" (?P.+) : (?P\d+) : (?P \s* \# \s* include \s* (?P[<"]) (?P[^">]+) [">]) (?P .*) """, re.VERBOSE) # Allow a temporary override so that a PR will not be blocked # on first updating `check_includes_config.py`. OVERRIDE = 'T' + 'ODO: update check_includes_config.py' def any_re(res: Iterable[str]) -> Pattern: """Given a list of RE strings, return an RE to match any of them.""" return re.compile('|'.join((f'({i})' for i in res))) def main(): ignore_re = any_re(config.IGNORE) n = 0 for line in sys.stdin: s = line.strip() m = _MATCH_SPLIT_RE.fullmatch(s) if not m: print(f"Unrecognized input '{s}'", file=sys.stderr) return 2 if OVERRIDE in m.group('trailing'): continue filename, include = m.group('file', 'include') if ignore_re.search(filename): continue if include not in config.DENY: continue if include in config.ALLOW.get(filename, []): continue n += 1 if n == 1: print('Disallowed:\n') line_number, directive = m.group('line', 'directive') print(f' {filename}:{line_number}: {directive}') if n > 0: print('\nIf a disallowed #include is legitimate, add an ALLOW rule to') print(f' {config.__file__}') print('and/or temporarily suppress this error by adding exactly') print(f' {OVERRIDE}') print('in a comment at the end of the #include.') return 1 return 0 if __name__ == '__main__': sys.exit(main())