|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# |
| 3 | +# Copyright (c) 2018-2022 The Bitcoin Core developers |
| 4 | +# Distributed under the MIT software license, see the accompanying |
| 5 | +# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 6 | + |
| 7 | +""" |
| 8 | +Check include guards. |
| 9 | +""" |
| 10 | + |
| 11 | +import re |
| 12 | +import sys |
| 13 | +from subprocess import check_output |
| 14 | +from typing import List |
| 15 | + |
| 16 | + |
| 17 | +HEADER_ID_PREFIX = 'BITCOIN_' |
| 18 | +HEADER_ID_SUFFIX = '_H' |
| 19 | + |
| 20 | +EXCLUDE_FILES_WITH_PREFIX = ['src/crypto/ctaes', |
| 21 | + 'src/leveldb', |
| 22 | + 'src/crc32c', |
| 23 | + 'src/secp256k1', |
| 24 | + 'src/minisketch', |
| 25 | + 'src/univalue', |
| 26 | + 'src/tinyformat.h', |
| 27 | + 'src/bench/nanobench.h', |
| 28 | + 'src/test/fuzz/FuzzedDataProvider.h'] |
| 29 | + |
| 30 | + |
| 31 | +def _get_header_file_lst() -> List[str]: |
| 32 | + """ Helper function to get a list of header filepaths to be |
| 33 | + checked for include guards. |
| 34 | + """ |
| 35 | + git_cmd_lst = ['git', 'ls-files', '--', '*.h'] |
| 36 | + header_file_lst = check_output( |
| 37 | + git_cmd_lst).decode('utf-8').splitlines() |
| 38 | + |
| 39 | + header_file_lst = [hf for hf in header_file_lst |
| 40 | + if not any(ef in hf for ef |
| 41 | + in EXCLUDE_FILES_WITH_PREFIX)] |
| 42 | + |
| 43 | + return header_file_lst |
| 44 | + |
| 45 | + |
| 46 | +def _get_header_id(header_file: str) -> str: |
| 47 | + """ Helper function to get the header id from a header file |
| 48 | + string. |
| 49 | +
|
| 50 | + eg: 'src/wallet/walletdb.h' -> 'BITCOIN_WALLET_WALLETDB_H' |
| 51 | +
|
| 52 | + Args: |
| 53 | + header_file: Filepath to header file. |
| 54 | +
|
| 55 | + Returns: |
| 56 | + The header id. |
| 57 | + """ |
| 58 | + header_id_base = header_file.split('/')[1:] |
| 59 | + header_id_base = '_'.join(header_id_base) |
| 60 | + header_id_base = header_id_base.replace('.h', '').replace('-', '_') |
| 61 | + header_id_base = header_id_base.upper() |
| 62 | + |
| 63 | + header_id = f'{HEADER_ID_PREFIX}{header_id_base}{HEADER_ID_SUFFIX}' |
| 64 | + |
| 65 | + return header_id |
| 66 | + |
| 67 | + |
| 68 | +def main(): |
| 69 | + exit_code = 0 |
| 70 | + |
| 71 | + header_file_lst = _get_header_file_lst() |
| 72 | + for header_file in header_file_lst: |
| 73 | + header_id = _get_header_id(header_file) |
| 74 | + |
| 75 | + regex_pattern = f'^#(ifndef|define|endif //) {header_id}' |
| 76 | + |
| 77 | + with open(header_file, 'r', encoding='utf-8') as f: |
| 78 | + header_file_contents = f.readlines() |
| 79 | + |
| 80 | + count = 0 |
| 81 | + for header_file_contents_string in header_file_contents: |
| 82 | + include_guard_lst = re.findall( |
| 83 | + regex_pattern, header_file_contents_string) |
| 84 | + |
| 85 | + count += len(include_guard_lst) |
| 86 | + |
| 87 | + if count != 3: |
| 88 | + print(f'{header_file} seems to be missing the expected ' |
| 89 | + 'include guard:') |
| 90 | + print(f' #ifndef {header_id}') |
| 91 | + print(f' #define {header_id}') |
| 92 | + print(' ...') |
| 93 | + print(f' #endif // {header_id}\n') |
| 94 | + exit_code = 1 |
| 95 | + |
| 96 | + sys.exit(exit_code) |
| 97 | + |
| 98 | + |
| 99 | +if __name__ == '__main__': |
| 100 | + main() |
0 commit comments