3 # Copyright (C) 2024 Free Software Foundation, Inc.
5 # This file is part of GDB.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # This is intended to be run from pre-commit. You can also run it by
21 # hand by passing repository-relative filenames to it, like:
22 # ./gdb/check-include-guards.py [--update] gdb/*.h
23 # When --update is used, rewrite the files in place as needed.
28 from typing
import List
30 DEF
= re
.compile("^#ifndef ([A-Za-z0-9_]+)\n")
31 OLDDEF
= re
.compile("^#if !defined *\\(([A-Za-z0-9_]+)\\)\n")
33 # Some headers -- in particular, ones that aren't maintained by gdb --
34 # should be excluded from the checks.
35 EXCLUDED
= frozenset(["gdbsupport/unordered_dense.h"])
41 if len(args
) > 0 and args
[0] == "--update":
46 def failure(filename
: str, ndx
: int, text
: str):
47 print(filename
+ ":" + str(ndx
+ 1) + ": " + text
, file=sys
.stderr
)
51 def skip_comments_and_blanks(ndx
: int, contents
: List
[str]):
52 while ndx
< len(contents
) and contents
[ndx
].startswith("/*"):
53 while ndx
< len(contents
):
55 if contents
[ndx
- 1].endswith("*/\n"):
58 while ndx
< len(contents
):
59 if contents
[ndx
].strip() != "":
65 def write_header(filename
: str, contents
: List
[str]):
66 with
open(filename
, "w") as f
:
67 f
.writelines(contents
)
70 def check_header(filename
: str):
71 if filename
in EXCLUDED
:
74 # Turn x/y-z.h into X_Y_Z_H.
75 assert filename
.endswith(".h")
76 expected
= filename
.replace("-", "_")
77 expected
= expected
.replace(".", "_")
78 expected
= expected
.replace("/", "_")
79 expected
= expected
.upper()
80 with
open(filename
) as f
:
82 if len(contents
) == 0:
83 # Empty file -- pathological but we can just ignore rather
86 if "THIS FILE IS GENERATED" in contents
[0]:
89 if not contents
[0].startswith("/*"):
90 failure(filename
, 0, "header should start with comment")
91 i
= skip_comments_and_blanks(0, contents
)
92 if i
== len(contents
):
93 failure(filename
, i
, "unterminated intro comment or missing body")
94 m
= DEF
.match(contents
[i
])
97 m
= OLDDEF
.match(contents
[i
])
99 failure(filename
, i
, "no header guard")
103 if symbol
!= expected
:
106 contents
[i
] = "#ifndef " + expected
+ "\n"
109 if i
== len(contents
):
110 failure(filename
, i
, "premature EOF")
111 if not contents
[i
].startswith("#define "):
112 failure(filename
, i
, "no define of header guard")
113 if contents
[i
] != "#define " + expected
+ "\n":
114 contents
[i
] = "#define " + expected
+ "\n"
116 i
= len(contents
) - 1
117 if not contents
[i
].startswith("#endif"):
118 failure(filename
, i
, "no trailing endif")
119 if contents
[i
] != "#endif /* " + expected
+ " */\n":
120 contents
[i
] = "#endif /* " + expected
+ " */\n"
122 if updated
and write_files
:
123 write_header(filename
, contents
)
126 for filename
in args
:
127 check_header(filename
)