xref: /illumos-gate/usr/src/tools/onbld/Checks/Comments.py (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 2008 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27
28#
29# Check delta comments:
30# 	- Have the correct form.
31# 	- Have a synopsis matching that of the CR or ARC case.
32# 	- Appear only once.
33#
34
35import re, sys
36from onbld.Checks.DbLookups import BugDB, ARC
37
38arcre = re.compile(r'^([A-Z][A-Z]*ARC[/ \t][12]\d{3}/\d{3}) (.*)$')
39bugre = re.compile(r'^(\d{7}) (.*)$')
40
41def isARC(comment):
42	return arcre.match(comment)
43
44def isBug(comment):
45	return bugre.match(comment)
46
47#
48# Translate any acceptable case number format into "<ARC> <YEAR>/<NUM>"
49# format.
50#
51def normalize_arc(caseid):
52	return re.sub(r'^([A-Z][A-Z]*ARC)[/ \t]', '\\1 ', caseid)
53
54def comchk(comments, check_db=True, output=sys.stderr):
55	'''Validate checkin comments against ON standards.
56
57	Comments must be a list of one-line comments, with no trailing
58	newline.
59
60	If check_db is True (the default), validate CR and ARC
61	synopses against the databases.
62
63	Error messages intended for the user are written to output,
64	which defaults to stderr
65	'''
66	bugnospcre = re.compile(r'^(\d{7})([^ ].*)')
67	ignorere = re.compile(r'^(Portions contributed by |Contributed by |back[ -]?out )')
68
69	errors = { 'bugnospc': [],
70		   'mutant': [],
71		   'dup': [],
72		   'nomatch': [],
73		   'nonexistent': [] }
74	bugs = {}
75	arcs = {}
76	ret = 0
77	blanks = False
78
79	for com in comments:
80		# Our input must be newline-free, comments are line-wise.
81		if com.find('\n') != -1:
82			raise ValueError("newline in comment '%s'" % com)
83
84		# Ignore valid comments we can't check
85		if ignorere.search(com):
86			continue
87
88		if not com or com.isspace():
89			blanks = True
90			continue
91
92		match = bugre.search(com)
93		if match:
94			if match.group(1) not in bugs:
95				bugs[match.group(1)] = []
96			bugs[match.group(1)].append(match.group(2))
97			continue
98
99		#
100		# Bugs missing a space after the ID are still bugs
101		# for the purposes of the duplicate ID and synopsis
102		# checks.
103		#
104		match = bugnospcre.search(com)
105		if match:
106			if match.group(1) not in bugs:
107				bugs[match.group(1)] = []
108			bugs[match.group(1)].append(match.group(2))
109			errors['bugnospc'].append(com)
110			continue
111
112		# ARC case
113		match = arcre.search(com)
114		if match:
115			arc, case = re.split('[/ \t]', match.group(1), 1)
116			arcs.setdefault((arc, case), []).append(match.group(2))
117			continue
118
119		# Anything else is bogus
120		errors['mutant'].append(com)
121
122	if len(bugs) > 0 and check_db:
123		bugdb = BugDB()
124		results = bugdb.lookup(bugs.keys())
125
126	for crid, insts in bugs.iteritems():
127		if len(insts) > 1:
128			errors['dup'].append(crid)
129
130		if not check_db:
131			continue
132
133		if crid not in results:
134			errors['nonexistent'].append(crid)
135			continue
136
137		#
138		# For each synopsis, compare the real synopsis with
139		# that in the comments, allowing for possible '(fix
140		# stuff)'-like trailing text
141		#
142		for entered in insts:
143			synopsis = results[crid]["synopsis"]
144			if not re.search(r'^' + re.escape(synopsis) +
145					r'( \([^)]+\))?$', entered):
146				errors['nomatch'].append([crid, synopsis,
147							entered])
148
149	if check_db:
150		valid = ARC(arcs.keys())
151
152	for case, insts in arcs.iteritems():
153		if len(insts) > 1:
154			errors['dup'].append(' '.join(case))
155
156 		if not check_db:
157			continue
158
159		if not case in valid:
160			errors['nonexistent'].append(' '.join(case))
161			continue
162		#
163		# The opensolaris.org ARC interfaces only give us the
164		# first 40 characters of the case name, so we must limit
165		# our checking similarly.
166		#
167		# We first try a direct match between the actual case name
168		# and the entered comment.  If that fails we remove a possible
169		# trailing (fix nit)-type comment, and re-try.
170		#
171		for entered in insts:
172			if entered[0:40] == valid[case]:
173				break
174			else:
175				# Try again with trailing (fix ...) removed.
176				dbcom = re.sub(r' \([^)]+\)$', '', entered)
177				if dbcom[0:40] != valid[case]:
178					errors['nomatch'].append(
179						[' '.join(case), valid[case],
180						 entered])
181
182	if blanks:
183		output.write("WARNING: Blank line(s) in comments\n")
184		ret = 1
185
186	if errors['dup']:
187		ret = 1
188		output.write("These IDs appear more than once in your "
189			     "comments:\n")
190		for err in errors['dup']:
191			output.write("  %s\n" % err)
192
193	if errors['bugnospc']:
194		ret = 1
195		output.write("These bugs are missing a single space following "
196			     "the ID:\n")
197		for com in errors['bugnospc']:
198			output.write("  %s\n" % com)
199
200	if errors['mutant']:
201		ret = 1
202		output.write("These comments are neither bug nor ARC case:\n")
203		for com in errors['mutant']:
204			output.write("  %s\n" % com)
205
206	if errors['nonexistent']:
207		ret = 1
208		output.write("These bugs/ARC cases were not found in the "
209			     "databases:\n")
210		for id in errors['nonexistent']:
211			output.write("  %s\n" % id)
212
213	if errors['nomatch']:
214		ret = 1
215		output.write("These bugs/ARC case synopsis/names don't match "
216			     "the database entries:\n")
217		for err in errors['nomatch']:
218			output.write("Synopsis/name of %s is wrong:\n" % err[0])
219			output.write("  should be: '%s'\n" % err[1])
220			output.write("         is: '%s'\n" % err[2])
221
222	return ret
223