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