1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#!/usr/bin/env python3
# Copyright (C) 2024 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This is intended to be run from pre-commit. You can also run it by
# hand by passing repository-relative filenames to it, like:
# ./gdb/check-include-guards.py [--update] gdb/*.h
# When --update is used, rewrite the files in place as needed.
import re
import sys
from typing import List
DEF = re.compile("^#ifndef ([A-Za-z0-9_]+)\n")
OLDDEF = re.compile("^#if !defined *\\(([A-Za-z0-9_]+)\\)\n")
# Some headers -- in particular, ones that aren't maintained by gdb --
# should be excluded from the checks.
EXCLUDED = frozenset(["gdbsupport/unordered_dense.h"])
# See if
write_files = False
args = sys.argv[1:]
if len(args) > 0 and args[0] == "--update":
write_files = True
args = args[1:]
def failure(filename: str, ndx: int, text: str):
print(filename + ":" + str(ndx + 1) + ": " + text, file=sys.stderr)
sys.exit(1)
def skip_comments_and_blanks(ndx: int, contents: List[str]):
while ndx < len(contents) and contents[ndx].startswith("/*"):
while ndx < len(contents):
ndx += 1
if contents[ndx - 1].endswith("*/\n"):
break
# Skip blank lines.
while ndx < len(contents):
if contents[ndx].strip() != "":
break
ndx += 1
return ndx
def write_header(filename: str, contents: List[str]):
with open(filename, "w") as f:
f.writelines(contents)
def check_header(filename: str):
if filename in EXCLUDED:
return
# Turn x/y-z.h into X_Y_Z_H.
assert filename.endswith(".h")
expected = filename.replace("-", "_")
expected = expected.replace(".", "_")
expected = expected.replace("/", "_")
expected = expected.upper()
with open(filename) as f:
contents = list(f)
if len(contents) == 0:
# Empty file -- pathological but we can just ignore rather
# than crashing.
return
if "THIS FILE IS GENERATED" in contents[0]:
# Ignore.
return
if not contents[0].startswith("/*"):
failure(filename, 0, "header should start with comment")
i = skip_comments_and_blanks(0, contents)
if i == len(contents):
failure(filename, i, "unterminated intro comment or missing body")
m = DEF.match(contents[i])
force_rewrite = False
if not m:
m = OLDDEF.match(contents[i])
if not m:
failure(filename, i, "no header guard")
force_rewrite = True
symbol = m.group(1)
updated = False
if symbol != expected:
force_rewrite = True
if force_rewrite:
contents[i] = "#ifndef " + expected + "\n"
updated = True
i += 1
if i == len(contents):
failure(filename, i, "premature EOF")
if not contents[i].startswith("#define "):
failure(filename, i, "no define of header guard")
if contents[i] != "#define " + expected + "\n":
contents[i] = "#define " + expected + "\n"
updated = True
i = len(contents) - 1
if not contents[i].startswith("#endif"):
failure(filename, i, "no trailing endif")
if contents[i] != "#endif /* " + expected + " */\n":
contents[i] = "#endif /* " + expected + " */\n"
updated = True
if updated and write_files:
write_header(filename, contents)
for filename in args:
check_header(filename)
|