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# 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, arcPath=None): 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(), arcPath) 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 # 164 # We first try a direct match between the actual case name 165 # and the entered comment. If that fails we remove a possible 166 # trailing (fix nit)-type comment, and re-try. 167 # 168 for entered in insts: 169 if entered == valid[case]: 170 break 171 else: 172 # Try again with trailing (fix ...) removed. 173 dbcom = re.sub(r' \([^)]+\)$', '', entered) 174 if dbcom != valid[case]: 175 errors['nomatch'].append( 176 [' '.join(case), valid[case], 177 entered]) 178 179 if blanks: 180 output.write("WARNING: Blank line(s) in comments\n") 181 ret = 1 182 183 if errors['dup']: 184 ret = 1 185 output.write("These IDs appear more than once in your " 186 "comments:\n") 187 for err in errors['dup']: 188 output.write(" %s\n" % err) 189 190 if errors['bugnospc']: 191 ret = 1 192 output.write("These bugs are missing a single space following " 193 "the ID:\n") 194 for com in errors['bugnospc']: 195 output.write(" %s\n" % com) 196 197 if errors['mutant']: 198 ret = 1 199 output.write("These comments are neither bug nor ARC case:\n") 200 for com in errors['mutant']: 201 output.write(" %s\n" % com) 202 203 if errors['nonexistent']: 204 ret = 1 205 output.write("These bugs/ARC cases were not found in the " 206 "databases:\n") 207 for id in errors['nonexistent']: 208 output.write(" %s\n" % id) 209 210 if errors['nomatch']: 211 ret = 1 212 output.write("These bugs/ARC case synopsis/names don't match " 213 "the database entries:\n") 214 for err in errors['nomatch']: 215 output.write("Synopsis/name of %s is wrong:\n" % err[0]) 216 output.write(" should be: '%s'\n" % err[1]) 217 output.write(" is: '%s'\n" % err[2]) 218 219 return ret 220