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