-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathcheck-header-guards.py
executable file
·151 lines (116 loc) · 4.32 KB
/
check-header-guards.py
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python
# Copyright 2016 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script to check C and C++ file header guards.
This script accepts a list of file or directory arguments. If a given
path is a file, it runs the checker on it. If the path is a directory,
it runs the checker on all files in that directory.
In addition, this script checks for potential header guard
collisions. This is useful since we munge / to _, and so
lib/abc/xyz/xyz.h
and
lib/abc_xyz/xyz.h
both want to use LIB_ABC_XYZ_XYZ_H_ as a header guard.
"""
import argparse
import collections
import os.path
import paths
import re
import sys
all_header_guards = collections.defaultdict(list)
pragma_once = re.compile('^#pragma once$')
def check_file(path):
"""Check whether the file has a correct header guard.
A header guard can either be a #pragma once, or else a matching set of
#ifndef PATH_TO_FILE_
#define PATH_TO_FILE_
...
#endif // PATH_TO_FILE_
preprocessor directives, where both '.' and '/' in the path are
mapped to '_', and a trailing '_' is appended.
In either the #pragma once case or the header guard case, it is
assumed that there is no trailing or leading whitespace.
"""
# Only check .h files
if path[-2:] != '.h':
return True
assert(path.startswith(paths.FUCHSIA_ROOT))
relative_path = path[len(paths.FUCHSIA_ROOT):].strip('/')
upper_path = relative_path.upper()
header_guard = upper_path.replace('.', '_').replace('/', '_') + '_'
all_header_guards[header_guard].append(path)
ifndef = re.compile('^#ifndef %s$' % header_guard)
define = re.compile('^#define %s$' % header_guard)
endif = re.compile('^#endif +// %s$' % header_guard)
found_pragma_once = False
found_ifndef = False
found_define = False
found_endif = False
with open(path, 'r') as f:
for line in f.readlines():
match = pragma_once.match(line)
if match:
if found_pragma_once:
print('%s contains multiple #pragma once' % path)
return False
found_pragma_once = True
match = ifndef.match(line)
if match:
if found_ifndef:
print('%s contains multiple ifndef header guards' % path)
return False
found_ifndef = True
match = define.match(line)
if match:
if found_define:
print('%s contains multiple define header guards' % path)
return False
found_define = True
match = endif.match(line)
if match:
if found_endif:
print('%s contains multiple endif header guards' % path)
return False
found_endif = True
if found_pragma_once:
if found_ifndef or found_define or found_endif:
print('%s contains both #pragma once and header guards' % path)
return False
return True
if found_ifndef and found_define and found_endif:
return True
if found_ifndef or found_define or found_endif:
print('%s contained only part of a header guard' % path)
return False
print('%s contained neither a header guard nor #pragma once' % path)
return False
def check_dir(p):
""" Walk recursively over a directory checking .h files"""
def prune(d):
if d[0] == '.':
return True
return False
for root, dirs, paths in os.walk(p):
# Prune dot directories like .git
[dirs.remove(d) for d in list(dirs) if prune(d)]
for path in paths:
check_file(os.path.join(root, path))
def check_collisions():
for header_guard, paths in all_header_guards.iteritems():
if len(paths) == 1:
continue
print('Multiple files could use %s as a header guard:' % header_guard)
for path in paths:
print(' %s' % path)
def main():
for p in sys.argv[1:]:
p = os.path.abspath(p)
if os.path.isdir(p):
check_dir(p)
else:
check_file(p)
check_collisions()
if __name__ == "__main__":
sys.exit(main())