Print this page
*** NO COMMENTS ***
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/scripts/git-pbchk.py
+++ new/usr/src/tools/scripts/git-pbchk.py
1 -#!/usr/bin/python2.4
1 +#!/usr/bin/python2.6
2 2 #
3 3 # This program is free software; you can redistribute it and/or modify
4 4 # it under the terms of the GNU General Public License version 2
5 5 # as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program; if not, write to the Free Software
14 14 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 15 #
16 16
17 17 #
18 18 # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
19 19 # Copyright 2008, 2012 Richard Lowe
20 20 #
21 21
22 22 import getopt
23 23 import os
24 24 import re
25 25 import subprocess
26 26 import sys
27 27 import tempfile
28 28
29 29 from cStringIO import StringIO
30 30
31 31 # This is necessary because, in a fit of pique, we used hg-format ignore lists
32 32 # for NOT files.
33 33 from mercurial import ignore
34 34
35 35 #
36 36 # Adjust the load path based on our location and the version of python into
37 37 # which it is being loaded. This assumes the normal onbld directory
38 38 # structure, where we are in bin/ and the modules are in
39 39 # lib/python(version)?/onbld/Scm/. If that changes so too must this.
40 40 #
41 41 sys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib",
42 42 "python%d.%d" % sys.version_info[:2]))
43 43
44 44 #
45 45 # Add the relative path to usr/src/tools to the load path, such that when run
46 46 # from the source tree we use the modules also within the source tree.
47 47 #
48 48 sys.path.insert(2, os.path.join(os.path.dirname(__file__), ".."))
49 49
50 50 from onbld.Checks import Comments, Copyright, CStyle, HdrChk
51 51 from onbld.Checks import JStyle, Keywords, Mapfile
52 52
53 53
54 54 class GitError(Exception):
55 55 pass
56 56
57 57 def git(command):
58 58 """Run a command and return a stream containing its stdout (and write its
59 59 stderr to its stdout)"""
60 60
61 61 if type(command) != list:
62 62 command = command.split()
63 63
64 64 command = ["git"] + command
65 65
66 66 try:
67 67 tmpfile = tempfile.TemporaryFile(prefix="git-nits")
68 68 except EnvironmentError, e:
69 69 raise GitError("Could not create temporary file: %s\n" % e)
70 70
71 71 try:
72 72 p = subprocess.Popen(command,
73 73 stdout=tmpfile,
74 74 stderr=subprocess.STDOUT)
75 75 except OSError, e:
76 76 raise GitError("could not execute %s: %s\n" (command, e))
77 77
78 78 err = p.wait()
79 79 if err != 0:
80 80 raise GitError(p.stdout.read())
81 81
82 82 tmpfile.seek(0)
83 83 return tmpfile
84 84
85 85
86 86 def git_root():
87 87 """Return the root of the current git workspace"""
88 88
89 89 p = git('rev-parse --git-dir')
90 90
91 91 if not p:
92 92 sys.stderr.write("Failed finding git workspace\n")
93 93 sys.exit(err)
94 94
95 95 return os.path.abspath(os.path.join(p.readlines()[0],
96 96 os.path.pardir))
97 97
98 98
99 99 def git_branch():
100 100 """Return the current git branch"""
101 101
102 102 p = git('branch')
103 103
104 104 if not p:
105 105 sys.stderr.write("Failed finding git branch\n")
106 106 sys.exit(err)
107 107
108 108 for elt in p:
109 109 if elt[0] == '*':
110 110 if elt.endswith('(no branch)'):
111 111 return None
112 112 return elt.split()[1]
113 113
114 114
115 115 def git_parent_branch(branch):
116 116 """Return the parent of the current git branch.
117 117
118 118 If this branch tracks a remote branch, return the remote branch which is
119 119 tracked. If not, default to origin/master."""
120 120
121 121 if not branch:
122 122 return None
123 123
124 124 p = git("for-each-ref --format=%(refname:short) %(upstream:short) " +
125 125 "refs/heads/")
126 126
127 127 if not p:
128 128 sys.stderr.write("Failed finding git parent branch\n")
129 129 sys.exit(err)
130 130
131 131 for line in p:
132 132 # Git 1.7 will leave a ' ' trailing any non-tracking branch
133 133 if ' ' in line and not line.endswith(' \n'):
134 134 local, remote = line.split()
135 135 if local == branch:
136 136 return remote
137 137 return 'origin/master'
138 138
139 139
140 140 def git_comments(parent):
141 141 """Return a list of any checkin comments on this git branch"""
142 142
143 143 p = git('log --pretty=format:%%B %s..' % parent)
144 144
145 145 if not p:
146 146 sys.stderr.write("Failed getting git comments\n")
147 147 sys.exit(err)
148 148
149 149 return map(lambda x: x.strip(), p.readlines())
150 150
151 151
152 152 def git_file_list(parent, paths=None):
153 153 """Return the set of files which have ever changed on this branch.
154 154
155 155 NB: This includes files which no longer exist, or no longer actually
156 156 differ."""
157 157
158 158 p = git("log --name-only --pretty=format: %s.. %s" %
159 159 (parent, ' '.join(paths)))
160 160
161 161 if not p:
162 162 sys.stderr.write("Failed building file-list from git\n")
163 163 sys.exit(err)
164 164
165 165 ret = set()
166 166 for fname in p:
167 167 if fname and not fname.isspace() and fname not in ret:
168 168 ret.add(fname.strip())
169 169
170 170 return ret
171 171
172 172
173 173 def not_check(root, cmd):
174 174 """Return a function which returns True if a file given as an argument
175 175 should be excluded from the check named by 'cmd'"""
176 176
177 177 ignorefiles = filter(os.path.exists,
178 178 [os.path.join(root, ".git", "%s.NOT" % cmd),
179 179 os.path.join(root, "exception_lists", cmd)])
180 180 if len(ignorefiles) > 0:
181 181 return ignore.ignore(root, ignorefiles, sys.stderr.write)
182 182 else:
183 183 return lambda x: False
184 184
185 185
186 186 def gen_files(root, parent, paths, exclude):
187 187 """Return a function producing file names, relative to the current
188 188 directory, of any file changed on this branch (limited to 'paths' if
189 189 requested), and excluding files for which exclude returns a true value """
190 190
191 191 # Taken entirely from Python 2.6's os.path.relpath which we would use if we
192 192 # could.
193 193 def relpath(path, here):
194 194 c = os.path.abspath(os.path.join(root, path)).split(os.path.sep)
195 195 s = os.path.abspath(here).split(os.path.sep)
196 196 l = len(os.path.commonprefix((s, c)))
197 197 return os.path.join(*[os.path.pardir] * (len(s)-l) + c[l:])
198 198
199 199 def ret(select=None):
200 200 if not select:
201 201 select = lambda x: True
202 202
203 203 for f in git_file_list(parent, paths):
204 204 f = relpath(f, '.')
205 205 if (os.path.exists(f) and select(f) and not exclude(f)):
206 206 yield f
207 207 return ret
208 208
209 209
210 210 def comchk(root, parent, flist, output):
211 211 output.write("Comments:\n")
212 212
213 213 return Comments.comchk(git_comments(parent), check_db=True,
214 214 output=output)
215 215
216 216
217 217 def mapfilechk(root, parent, flist, output):
218 218 ret = 0
219 219
220 220 # We are interested in examining any file that has the following
221 221 # in its final path segment:
222 222 # - Contains the word 'mapfile'
223 223 # - Begins with 'map.'
224 224 # - Ends with '.map'
225 225 # We don't want to match unless these things occur in final path segment
226 226 # because directory names with these strings don't indicate a mapfile.
227 227 # We also ignore files with suffixes that tell us that the files
228 228 # are not mapfiles.
229 229 MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$',
230 230 re.IGNORECASE)
231 231 NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE)
232 232
233 233 output.write("Mapfile comments:\n")
234 234
235 235 for f in flist(lambda x: MapfileRE.match(x) and not
236 236 NotMapSuffixRE.match(x)):
237 237 fh = open(f, 'r')
238 238 ret |= Mapfile.mapfilechk(fh, output=output)
239 239 fh.close()
240 240 return ret
241 241
242 242
243 243 def copyright(root, parent, flist, output):
244 244 ret = 0
245 245 output.write("Copyrights:\n")
246 246 for f in flist():
247 247 fh = open(f, 'r')
248 248 ret |= Copyright.copyright(fh, output=output)
249 249 fh.close()
250 250 return ret
251 251
252 252
253 253 def hdrchk(root, parent, flist, output):
254 254 ret = 0
255 255 output.write("Header format:\n")
256 256 for f in flist(lambda x: x.endswith('.h')):
257 257 fh = open(f, 'r')
258 258 ret |= HdrChk.hdrchk(fh, lenient=True, output=output)
259 259 fh.close()
260 260 return ret
261 261
262 262
263 263 def cstyle(root, parent, flist, output):
264 264 ret = 0
265 265 output.write("C style:\n")
266 266 for f in flist(lambda x: x.endswith('.c') or x.endswith('.h')):
267 267 fh = open(f, 'r')
268 268 ret |= CStyle.cstyle(fh, output=output, picky=True,
269 269 check_posix_types=True,
270 270 check_continuation=True)
271 271 fh.close()
272 272 return ret
273 273
274 274
275 275 def jstyle(root, parent, flist, output):
276 276 ret = 0
277 277 output.write("Java style:\n")
278 278 for f in flist(lambda x: x.endswith('.java')):
279 279 fh = open(f, 'r')
280 280 ret |= JStyle.jstyle(fh, output=output, picky=True)
281 281 fh.close()
282 282 return ret
283 283
284 284
285 285 def keywords(root, parent, flist, output):
286 286 ret = 0
287 287 output.write("SCCS Keywords:\n")
288 288 for f in flist():
289 289 fh = open(f, 'r')
290 290 ret |= Keywords.keywords(fh, output=output)
291 291 fh.close()
292 292 return ret
293 293
294 294
295 295 def run_checks(root, parent, cmds, paths='', opts={}):
296 296 """Run the checks given in 'cmds', expected to have well-known signatures,
297 297 and report results for any which fail.
298 298
299 299 Return failure if any of them did.
300 300
301 301 NB: the function name of the commands passed in is used to name the NOT
302 302 file which excepts files from them."""
303 303
304 304 ret = 0
305 305
306 306 for cmd in cmds:
307 307 s = StringIO()
308 308
309 309 exclude = not_check(root, cmd.func_name)
310 310 result = cmd(root, parent, gen_files(root, parent, paths, exclude),
311 311 output=s)
312 312 ret |= result
313 313
314 314 if result != 0:
315 315 print s.getvalue()
316 316
317 317 return ret
318 318
319 319
320 320 def nits(root, parent, paths):
321 321 cmds = [copyright,
322 322 cstyle,
323 323 hdrchk,
324 324 jstyle,
325 325 keywords,
326 326 mapfilechk]
327 327 run_checks(root, parent, cmds, paths)
328 328
329 329
330 330 def pbchk(root, parent, paths):
331 331 cmds = [comchk,
332 332 copyright,
333 333 cstyle,
334 334 hdrchk,
335 335 jstyle,
336 336 keywords,
337 337 mapfilechk]
338 338 run_checks(root, parent, cmds)
339 339
340 340
341 341 def main(cmd, args):
342 342 parent_branch = None
343 343
344 344 try:
345 345 opts, args = getopt.getopt(args, 'b:')
346 346 except getopt.GetoptError, e:
347 347 sys.stderr.write(str(e) + '\n')
348 348 sys.stderr.write("Usage: %s [-b branch] [path...]\n" % cmd)
349 349 sys.exit(1)
350 350
351 351 for opt, arg in opts:
352 352 if opt == '-b':
353 353 parent_branch = arg
354 354
355 355 if not parent_branch:
356 356 parent_branch = git_parent_branch(git_branch())
357 357
358 358 func = nits
359 359 if cmd == 'git-pbchk':
360 360 func = pbchk
361 361 if args:
362 362 sys.stderr.write("only complete workspaces may be pbchk'd\n");
363 363 sys.exit(1)
364 364
365 365 func(git_root(), parent_branch, args)
366 366
367 367 if __name__ == '__main__':
368 368 try:
369 369 main(os.path.basename(sys.argv[0]), sys.argv[1:])
370 370 except GitError, e:
371 371 sys.stderr.write("failed to run git:\n %s\n" % str(e))
372 372 sys.exit(1)
↓ open down ↓ |
361 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX