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