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