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