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]
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:
41 # #endif /* [!]GUARD */
43 # The preferred form is without the bang character, but either is
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
71 from onbld
.Checks
.Copyright
import is_copyright
73 class HeaderFile(object):
74 def __init__(self
, fh
, filename
=None, lenient
=False):
76 self
.lenient
= lenient
78 self
.has_copyright
= False
82 self
.filename
= filename
84 self
.filename
= fh
.name
87 for line
in self
.file:
89 if not line
or line
.isspace():
92 line
= line
.rstrip('\r\n')
94 # Recursively join continuation lines
95 if line
.endswith('\\'):
96 line
= line
[0:-1] + self
.getline()
104 # Optionally take a line to start skipping/processing with
106 def skipcomments(self
, curline
=None):
107 line
= curline
or self
.getline()
109 # When lenient, allow C++ comments
110 if self
.lenient
and re
.search(r
'^\s*//', line
):
111 line
= self
.getline()
114 if not re
.search(r
'^\s*/\*', 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
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()
135 def err(stream
, msg
, hdr
):
137 stream
.write("%s: line %d: %s\n" %
138 (hdr
.filename
, hdr
.lineno
, msg
))
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.
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
):
161 hdr
= HeaderFile(fh
, filename
=filename
, lenient
=lenient
)
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
)
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
):
182 line
= hdr
.skipcomments()
185 # Step 3: Header guards
187 match
= re
.search(r
'^#ifndef\s([a-zA-Z0-9_]+)$', line
)
189 err(output
, "Invalid or missing header guard", hdr
)
192 guard
= match
.group(1)
195 guardname
= os
.path
.basename(hdr
.filename
)
198 # If we aren't being lenient, validate the name of the
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
)
212 if not re
.search(r
'#define\s%s$' % guard
, line
):
213 err(output
, "Invalid header guard", hdr
)
216 line
= hdr
.skipcomments()
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
)
231 line
= hdr
.skipcomments(line
)
234 # Main processing loop
237 found_endguard
= False
238 found_cplusplus
= False
242 if not (line
.startswith('#') or line
.startswith('using')):
247 match
= re
.search(r
'^#include(.*)$', line
)
250 # For system files, make sure #includes are of the form:
253 if not lenient
and not re
.search(r
'\s<.*>',
255 err(output
, "Bad include", hdr
)
257 elif not in_cplusplus
and re
.search(r
'^#ifdef\s__cplusplus$',
260 # Start of C++ header guard.
261 # Make sure it is of the form:
268 if line
== 'extern "C" {':
271 err(output
, "Bad __cplusplus clause",
276 found_cplusplus
= True
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:
291 err(output
, "Bad __cplusplus clause",
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
)
314 err(output
, "Missing closing #ifdef __cplusplus", hdr
)
317 if not found_endguard
:
318 err(output
, "Missing or invalid ending header guard", hdr
)