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
128
129
130
131
132
133
134
|
#!/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)
# Either None or a tuple like (LINE, TEXT) that describes a needed
# update.
updated = None
if symbol != expected:
force_rewrite = True
if force_rewrite:
contents[i] = "#ifndef " + expected + "\n"
updated = (i, "wrong symbol in ifndef")
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"
if updated is None:
updated = (i, "wrong symbol in define")
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"
if updated is None:
updated = (i, "wrong endif line")
if updated is not None:
if write_files:
write_header(filename, contents)
else:
failure(filename, *updated)
for filename in args:
check_header(filename)
|