xref: /illumos-gate/usr/src/tools/onbld/Checks/Comments.py (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
1#! /usr/bin/python
2#
3# CDDL HEADER START
4#
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.
8#
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.
13#
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]
19#
20# CDDL HEADER END
21#
22
23#
24# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27
28# Copyright 2007, 2010 Richard Lowe
29
30#
31# Check delta comments:
32# 	- Have the correct form.
33# 	- Have a synopsis matching that of the bug
34# 	- Appear only once.
35#
36
37import re, sys
38from onbld.Checks.DbLookups import BugDB
39
40
41bugre = re.compile(r'^(\d{2,7}) (.*)$')
42
43
44def isBug(comment):
45	return bugre.match(comment)
46
47
48def comchk(comments, check_db=True, output=sys.stderr):
49	'''Validate checkin comments against ON standards.
50
51	Comments must be a list of one-line comments, with no trailing
52	newline.
53
54	If check_db is True (the default), validate bug synopses against the
55	databases.
56
57	Error messages intended for the user are written to output,
58	which defaults to stderr
59	'''
60	bugnospcre = re.compile(r'^(\d{2,7})([^ ].*)')
61	ignorere = re.compile(r'^(' +
62                              r'Portions contributed by|' +
63                              r'Contributed by|' +
64                              r'Reviewed[ -]by|' +
65                              r'Approved[ -]by|' +
66                              r'back[ -]?out)' +
67                              r'[: ]')
68
69	errors = { 'bugnospc': [],
70		   'mutant': [],
71		   'dup': [],
72		   'nomatch': [],
73		   'nonexistent': [] }
74	bugs = {}
75	ret = 0
76	blanks = False
77
78	for com in comments:
79		# Our input must be newline-free, comments are line-wise.
80		if com.find('\n') != -1:
81			raise ValueError("newline in comment '%s'" % com)
82
83		# Ignore valid comments we can't check
84		if ignorere.search(com):
85			continue
86
87		if not com or com.isspace():
88			blanks = True
89			continue
90
91		match = bugre.search(com)
92		if match:
93			if match.group(1) not in bugs:
94				bugs[match.group(1)] = []
95			bugs[match.group(1)].append(match.group(2))
96			continue
97
98		#
99		# Bugs missing a space after the ID are still bugs
100		# for the purposes of the duplicate ID and synopsis
101		# checks.
102		#
103		match = bugnospcre.search(com)
104		if match:
105			if match.group(1) not in bugs:
106				bugs[match.group(1)] = []
107			bugs[match.group(1)].append(match.group(2))
108			errors['bugnospc'].append(com)
109			continue
110
111		# Anything else is bogus
112		errors['mutant'].append(com)
113
114	if len(bugs) > 0 and check_db:
115		bugdb = BugDB()
116		results = bugdb.lookup(bugs.keys())
117
118	for crid, insts in bugs.iteritems():
119		if len(insts) > 1:
120			errors['dup'].append(crid)
121
122		if not check_db:
123			continue
124
125		if crid not in results:
126			errors['nonexistent'].append(crid)
127			continue
128
129		#
130		# For each synopsis, compare the real synopsis with
131		# that in the comments, allowing for possible '(fix
132		# stuff)'-like trailing text
133		#
134		for entered in insts:
135			synopsis = results[crid]["synopsis"]
136			if not re.search(r'^' + re.escape(synopsis) +
137					r'( \([^)]+\))?$', entered):
138				errors['nomatch'].append([crid, synopsis,
139							entered])
140
141
142	if blanks:
143		output.write("WARNING: Blank line(s) in comments\n")
144		ret = 1
145
146	if errors['dup']:
147		ret = 1
148		output.write("These IDs appear more than once in your "
149			     "comments:\n")
150		for err in errors['dup']:
151			output.write("  %s\n" % err)
152
153	if errors['bugnospc']:
154		ret = 1
155		output.write("These bugs are missing a single space following "
156			     "the ID:\n")
157		for com in errors['bugnospc']:
158			output.write("  %s\n" % com)
159
160	if errors['mutant']:
161		ret = 1
162		output.write("These comments are not valid bugs:\n")
163		for com in errors['mutant']:
164			output.write("  %s\n" % com)
165
166	if errors['nonexistent']:
167		ret = 1
168		output.write("These bugs were not found in the databases:\n")
169		for id in errors['nonexistent']:
170			output.write("  %s\n" % id)
171
172	if errors['nomatch']:
173		ret = 1
174		output.write("These bug synopses don't match "
175			     "the database entries:\n")
176		for err in errors['nomatch']:
177			output.write("Synopsis of %s is wrong:\n" % err[0])
178			output.write("  should be: '%s'\n" % err[1])
179			output.write("         is: '%s'\n" % err[2])
180
181	return ret
182