dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / tools / onbld / Checks / HdrChk.py
blobc2697dcaf202c0636669860e22620af55a8131c7
1 #! /usr/bin/python
3 # CDDL HEADER START
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
20 # CDDL HEADER END
24 # Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 # Use is subject to license terms.
28 # Copyright 2008, 2010, Richard Lowe
31 # Check that header files conform to our standards
33 # Standards for all header files (lenient):
35 # 1) Begin with a comment containing a copyright message
37 # 2) Enclosed in a guard of the form:
39 # #ifndef GUARD
40 # #define GUARD
41 # #endif /* [!]GUARD */
43 # The preferred form is without the bang character, but either is
44 # acceptable.
46 # 3) Has a valid ident declaration
48 # Additional standards for system header files:
50 # 1) The file guard must take the form '_FILENAME_H[_]', where FILENAME
51 # matches the basename of the file. If it is installed in a
52 # subdirectory, it must be of the form _DIR_FILENAME_H. The form
53 # without the trailing underscore is preferred.
55 # 2) All #include directives must use the <> form.
57 # 3) If the header file contains anything besides comments and
58 # preprocessor directives, then it must be enclosed in a C++ guard of
59 # the form:
61 # #ifdef __cplusplus
62 # extern "C" {
63 # #endif
65 # #ifdef __cplusplus
66 # }
67 # #endif
70 import re, os, sys
71 from onbld.Checks.Copyright import is_copyright
73 class HeaderFile(object):
74 def __init__(self, fh, filename=None, lenient=False):
75 self.file = fh
76 self.lenient = lenient
77 self.lineno = 0
78 self.has_copyright = False
79 self.eof = False
81 if filename:
82 self.filename = filename
83 else:
84 self.filename = fh.name
86 def getline(self):
87 for line in self.file:
88 self.lineno += 1
89 if not line or line.isspace():
90 continue
91 else:
92 line = line.rstrip('\r\n')
94 # Recursively join continuation lines
95 if line.endswith('\\'):
96 line = line[0:-1] + self.getline()
98 return line
99 else:
100 self.eof = True
101 return ''
104 # Optionally take a line to start skipping/processing with
106 def skipcomments(self, curline=None):
107 line = curline or self.getline()
108 while line:
109 # When lenient, allow C++ comments
110 if self.lenient and re.search(r'^\s*//', line):
111 line = self.getline()
112 continue
114 if not re.search(r'^\s*/\*', line):
115 return line
117 while not re.search(r'\*/', line):
119 # We explicitly exclude the form used in the
120 # CDDL header rather than attempting to craft
121 # a match for every possibly valid copyright
122 # notice
124 if is_copyright(line):
125 self.has_copyright = True
126 line = self.getline()
128 if is_copyright(line):
129 self.has_copyright = True
130 line = self.getline()
132 return line
135 def err(stream, msg, hdr):
136 if not hdr.eof:
137 stream.write("%s: line %d: %s\n" %
138 (hdr.filename, hdr.lineno, msg))
139 else:
140 stream.write("%s: %s\n" % (hdr.filename, msg))
144 # Keyword strings (both expanded and literal) for the various SCMs
145 # Be certain to wrap each full expression in parens.
147 idents = [
148 # SCCS
149 r'((\%Z\%(\%M\%)\t\%I\%|\%W\%)\t\%E\% SMI)',
150 r'(@\(#\)(\w[-\.\w]+\.h)\t\d+\.\d+(\.\d+\.\d+)?\t\d\d/\d\d/\d\d SMI)',
153 IDENT = re.compile(r'(%s)' % '|'.join(idents))
156 def hdrchk(fh, filename=None, lenient=False, output=sys.stderr):
157 found_ident = False
158 guard = None
159 ret = 0
161 hdr = HeaderFile(fh, filename=filename, lenient=lenient)
164 # Step 1:
166 # Headers must begin with a comment containing a copyright notice. We
167 # don't validate the contents of the copyright, only that it exists
169 line = hdr.skipcomments()
171 if not hdr.has_copyright:
172 err(output, "Missing copyright in opening comment", hdr)
173 ret = 1
176 # Step 2:
178 # For application header files only, allow the ident string to appear
179 # before the header guard.
180 if lenient and line.startswith("#pragma ident") and IDENT.search(line):
181 found_ident = 1
182 line = hdr.skipcomments()
185 # Step 3: Header guards
187 match = re.search(r'^#ifndef\s([a-zA-Z0-9_]+)$', line)
188 if not match:
189 err(output, "Invalid or missing header guard", hdr)
190 ret = 1
191 else:
192 guard = match.group(1)
194 if not lenient:
195 guardname = os.path.basename(hdr.filename)
198 # If we aren't being lenient, validate the name of the
199 # guard
202 guardname = guardname.upper()
203 guardname = guardname.replace('.', '_').replace('-','_')
204 guardname = guardname.replace('+', "_PLUS")
206 if not re.search(r'^_.*%s[_]?$' % guardname, guard):
207 err(output, "Header guard does not match "
208 "suggested style (_FILEPATH_H_)", hdr)
209 ret = 1
211 line = hdr.getline()
212 if not re.search(r'#define\s%s$' % guard, line):
213 err(output, "Invalid header guard", hdr)
214 ret = 1
215 if not line:
216 line = hdr.skipcomments()
217 else:
218 line = hdr.skipcomments()
222 # Step 4: ident string
224 # We allow both the keyword and extracted versions
226 if (not found_ident and line.startswith("#pragma ident") and
227 not IDENT.search(line)):
228 err(output, "Invalid #pragma ident", hdr)
229 ret = 1
230 else:
231 line = hdr.skipcomments(line)
234 # Main processing loop
236 in_cplusplus = False
237 found_endguard = False
238 found_cplusplus = False
239 found_code = False
241 while line:
242 if not (line.startswith('#') or line.startswith('using')):
243 found_code = True
244 line = hdr.getline()
245 continue
247 match = re.search(r'^#include(.*)$', line)
248 if match:
250 # For system files, make sure #includes are of the form:
251 # '#include <file>'
253 if not lenient and not re.search(r'\s<.*>',
254 match.group(1)):
255 err(output, "Bad include", hdr)
256 ret = 1
257 elif not in_cplusplus and re.search(r'^#ifdef\s__cplusplus$',
258 line):
260 # Start of C++ header guard.
261 # Make sure it is of the form:
263 # #ifdef __cplusplus
264 # extern "C" {
265 # #endif
267 line = hdr.getline()
268 if line == 'extern "C" {':
269 line = hdr.getline()
270 if line != '#endif':
271 err(output, "Bad __cplusplus clause",
272 hdr)
273 ret = 1
274 else:
275 in_cplusplus = True
276 found_cplusplus = True
277 else:
278 continue
279 elif in_cplusplus and re.search(r'^#ifdef\s__cplusplus$', line):
281 # End of C++ header guard. Make sure it is of the form:
283 # #ifdef __cplusplus
285 # #endif
287 line = hdr.getline()
288 if line == '}':
289 line = hdr.getline()
290 if line != '#endif':
291 err(output, "Bad __cplusplus clause",
292 hdr)
293 ret = 1
294 else:
295 in_cplusplus = False
296 else:
297 continue
298 elif re.search(r'^#endif\s/\* [!]?%s \*/$' % guard, line):
300 # Ending header guard
302 found_endguard = True
304 line = hdr.skipcomments()
307 # Check for missing end clauses
309 if (not lenient) and (not found_cplusplus) and found_code:
310 err(output, "Missing __cplusplus guard", hdr)
311 ret = 1
313 if in_cplusplus:
314 err(output, "Missing closing #ifdef __cplusplus", hdr)
315 ret = 1
317 if not found_endguard:
318 err(output, "Missing or invalid ending header guard", hdr)
319 ret = 1
321 return ret