Avoid "text file busy" in dw2-using-debug-str.exp
[binutils-gdb.git] / gdb / check-include-guards.py
blob1673ab1079a5a57b299fa158f504b39e57aebe82
1 #!/usr/bin/env python3
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.
26 import re
27 import sys
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"])
38 # See if
39 write_files = False
40 args = sys.argv[1:]
41 if len(args) > 0 and args[0] == "--update":
42 write_files = True
43 args = args[1:]
46 def failure(filename: str, ndx: int, text: str):
47 print(filename + ":" + str(ndx + 1) + ": " + text, file=sys.stderr)
48 sys.exit(1)
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):
54 ndx += 1
55 if contents[ndx - 1].endswith("*/\n"):
56 break
57 # Skip blank lines.
58 while ndx < len(contents):
59 if contents[ndx].strip() != "":
60 break
61 ndx += 1
62 return ndx
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:
72 return
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:
81 contents = list(f)
82 if len(contents) == 0:
83 # Empty file -- pathological but we can just ignore rather
84 # than crashing.
85 return
86 if "THIS FILE IS GENERATED" in contents[0]:
87 # Ignore.
88 return
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])
95 force_rewrite = False
96 if not m:
97 m = OLDDEF.match(contents[i])
98 if not m:
99 failure(filename, i, "no header guard")
100 force_rewrite = True
101 symbol = m.group(1)
102 updated = False
103 if symbol != expected:
104 force_rewrite = True
105 if force_rewrite:
106 contents[i] = "#ifndef " + expected + "\n"
107 updated = True
108 i += 1
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"
115 updated = True
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"
121 updated = True
122 if updated and write_files:
123 write_header(filename, contents)
126 for filename in args:
127 check_header(filename)