garnet: Split network power in ruby.stats
[gem5.git] / util / style.py
1 #! /usr/bin/env python
2 # Copyright (c) 2006 The Regents of The University of Michigan
3 # Copyright (c) 2007 The Hewlett-Packard Development Company
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met: redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer;
10 # redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution;
13 # neither the name of the copyright holders nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #
29 # Authors: Nathan Binkert
30
31 import re
32 import os
33 import sys
34
35 sys.path.insert(0, os.path.dirname(__file__))
36
37 from file_types import lang_type
38
39 tabsize = 8
40 lead = re.compile(r'^([ \t]+)')
41 trail = re.compile(r'([ \t]+)$')
42 any_control = re.compile(r'\b(if|while|for)[ \t]*[(]')
43 good_control = re.compile(r'\b(if|while|for) [(]')
44
45 whitespace_types = set(('C', 'C++', 'swig', 'python', 'asm', 'isa', 'scons'))
46 format_types = set(('C', 'C++'))
47
48 def checkwhite_line(line):
49 match = lead.search(line)
50 if match and match.group(1).find('\t') != -1:
51 return False
52
53 match = trail.search(line)
54 if match:
55 return False
56
57 return True
58
59 def checkwhite(filename):
60 if lang_type(filename) not in whitespace_types:
61 return
62
63 try:
64 f = file(filename, 'r+')
65 except OSError, msg:
66 print 'could not open file %s: %s' % (filename, msg)
67 return
68
69 for num,line in enumerate(f):
70 if not checkwhite_line(line):
71 yield line,num + 1
72
73 def fixwhite_line(line):
74 if lead.search(line):
75 newline = ''
76 for i,c in enumerate(line):
77 if c == ' ':
78 newline += ' '
79 elif c == '\t':
80 newline += ' ' * (tabsize - len(newline) % tabsize)
81 else:
82 newline += line[i:]
83 break
84
85 line = newline
86
87 return line.rstrip() + '\n'
88
89 def fixwhite(filename, fixonly=None):
90 if lang_type(filename) not in whitespace_types:
91 return
92
93 try:
94 f = file(filename, 'r+')
95 except OSError, msg:
96 print 'could not open file %s: %s' % (filename, msg)
97 return
98
99 lines = list(f)
100
101 f.seek(0)
102 f.truncate()
103
104 for i,line in enumerate(lines):
105 if fixonly is None or i in fixonly:
106 line = fixwhite_line(line)
107
108 print >>f, line,
109
110 def linelen(line):
111 tabs = line.count('\t')
112 if not tabs:
113 return len(line)
114
115 count = 0
116 for c in line:
117 if c == '\t':
118 count += tabsize - count % tabsize
119 else:
120 count += 1
121
122 return count
123
124 class ValidationStats(object):
125 def __init__(self):
126 self.toolong = 0
127 self.toolong80 = 0
128 self.leadtabs = 0
129 self.trailwhite = 0
130 self.badcontrol = 0
131 self.cret = 0
132
133 def dump(self):
134 print '''\
135 %d violations of lines over 79 chars. %d of which are 80 chars exactly.
136 %d cases of whitespace at the end of a line.
137 %d cases of tabs to indent.
138 %d bad parens after if/while/for.
139 %d carriage returns found.
140 ''' % (self.toolong, self.toolong80, self.trailwhite, self.leadtabs,
141 self.badcontrol, self.cret)
142
143 def __nonzero__(self):
144 return self.toolong or self.toolong80 or self.leadtabs or \
145 self.trailwhite or self.badcontrol or self.cret
146
147 def validate(filename, stats, verbose, exit_code):
148 if lang_type(filename) not in format_types:
149 return
150
151 def msg(lineno, line, message):
152 print '%s:%d>' % (filename, lineno + 1), message
153 if verbose > 2:
154 print line
155
156 def bad():
157 if exit_code is not None:
158 sys.exit(exit_code)
159
160 try:
161 f = file(filename, 'r')
162 except OSError:
163 if verbose > 0:
164 print 'could not open file %s' % filename
165 bad()
166 return
167
168 for i,line in enumerate(f):
169 line = line.rstrip('\n')
170
171 # no carriage returns
172 if line.find('\r') != -1:
173 self.cret += 1
174 if verbose > 1:
175 msg(i, line, 'carriage return found')
176 bad()
177
178 # lines max out at 79 chars
179 llen = linelen(line)
180 if llen > 79:
181 stats.toolong += 1
182 if llen == 80:
183 stats.toolong80 += 1
184 if verbose > 1:
185 msg(i, line, 'line too long (%d chars)' % llen)
186 bad()
187
188 # no tabs used to indent
189 match = lead.search(line)
190 if match and match.group(1).find('\t') != -1:
191 stats.leadtabs += 1
192 if verbose > 1:
193 msg(i, line, 'using tabs to indent')
194 bad()
195
196 # no trailing whitespace
197 if trail.search(line):
198 stats.trailwhite +=1
199 if verbose > 1:
200 msg(i, line, 'trailing whitespace')
201 bad()
202
203 # for c++, exactly one space betwen if/while/for and (
204 if cpp:
205 match = any_control.search(line)
206 if match and not good_control.search(line):
207 stats.badcontrol += 1
208 if verbose > 1:
209 msg(i, line, 'improper spacing after %s' % match.group(1))
210 bad()
211
212 def modified_lines(old_data, new_data, max_lines):
213 from itertools import count
214 from mercurial import bdiff, mdiff
215
216 modified = set()
217 counter = count()
218 for pbeg, pend, fbeg, fend in bdiff.blocks(old_data, new_data):
219 for i in counter:
220 if i < fbeg:
221 modified.add(i)
222 elif i + 1 >= fend:
223 break
224 elif i > max_lines:
225 break
226 return modified
227
228 def do_check_style(ui, repo, *files, **args):
229 """check files for proper m5 style guidelines"""
230 from mercurial import mdiff, util
231
232 if files:
233 files = frozenset(files)
234
235 def skip(name):
236 return files and name in files
237
238 def prompt(name, func, fixonly=None):
239 if args.get('auto', False):
240 result = 'f'
241 else:
242 while True:
243 result = ui.prompt("(a)bort, (i)gnore, or (f)ix?", default='a')
244 if result in 'aif':
245 break
246
247 if result == 'a':
248 return True
249 elif result == 'f':
250 func(repo.wjoin(name), fixonly)
251
252 return False
253
254 modified, added, removed, deleted, unknown, ignore, clean = repo.status()
255
256 for fname in added:
257 if skip(fname):
258 continue
259
260 ok = True
261 for line,num in checkwhite(repo.wjoin(fname)):
262 ui.write("invalid whitespace in %s:%d\n" % (fname, num))
263 if ui.verbose:
264 ui.write(">>%s<<\n" % line[-1])
265 ok = False
266
267 if not ok:
268 if prompt(fname, fixwhite):
269 return True
270
271 try:
272 wctx = repo.workingctx()
273 except:
274 from mercurial import context
275 wctx = context.workingctx(repo)
276
277 for fname in modified:
278 if skip(fname):
279 continue
280
281 if lang_type(fname) not in whitespace_types:
282 continue
283
284 fctx = wctx.filectx(fname)
285 pctx = fctx.parents()
286
287 file_data = fctx.data()
288 lines = mdiff.splitnewlines(file_data)
289 if len(pctx) in (1, 2):
290 mod_lines = modified_lines(pctx[0].data(), file_data, len(lines))
291 if len(pctx) == 2:
292 m2 = modified_lines(pctx[1].data(), file_data, len(lines))
293 # only the lines that are new in both
294 mod_lines = mod_lines & m2
295 else:
296 mod_lines = xrange(0, len(lines))
297
298 fixonly = set()
299 for i,line in enumerate(lines):
300 if i not in mod_lines:
301 continue
302
303 if checkwhite_line(line):
304 continue
305
306 ui.write("invalid whitespace: %s:%d\n" % (fname, i+1))
307 if ui.verbose:
308 ui.write(">>%s<<\n" % line[:-1])
309 fixonly.add(i)
310
311 if fixonly:
312 if prompt(fname, fixwhite, fixonly):
313 return True
314
315 def do_check_format(ui, repo, **args):
316 modified, added, removed, deleted, unknown, ignore, clean = repo.status()
317
318 verbose = 0
319 stats = ValidationStats()
320 for f in modified + added:
321 validate(f, stats, verbose, None)
322
323 if stats:
324 stats.dump()
325 result = ui.prompt("invalid formatting\n(i)gnore or (a)bort?",
326 "^[ia]$", "a")
327 if result.startswith('i'):
328 pass
329 elif result.startswith('a'):
330 return True
331 else:
332 raise util.Abort(_("Invalid response: '%s'") % result)
333
334 return False
335
336 def check_hook(hooktype):
337 if hooktype not in ('pretxncommit', 'pre-qrefresh'):
338 raise AttributeError, \
339 "This hook is not meant for %s" % hooktype
340
341 def check_style(ui, repo, hooktype, **kwargs):
342 check_hook(hooktype)
343 args = {}
344
345 try:
346 return do_check_style(ui, repo, **args)
347 except Exception, e:
348 import traceback
349 traceback.print_exc()
350 return True
351
352 def check_format(ui, repo, hooktype, **kwargs):
353 check_hook(hooktype)
354 args = {}
355
356 try:
357 return do_check_format(ui, repo, **args)
358 except Exception, e:
359 import traceback
360 traceback.print_exc()
361 return True
362
363 try:
364 from mercurial.i18n import _
365 except ImportError:
366 def _(arg):
367 return arg
368
369 cmdtable = {
370 '^m5style' :
371 ( do_check_style,
372 [ ('a', 'auto', False, _("automatically fix whitespace")) ],
373 _('hg m5style [-a] [FILE]...')),
374 '^m5format' :
375 ( do_check_format,
376 [ ],
377 _('hg m5format [FILE]...')),
378 }
379
380 if __name__ == '__main__':
381 import getopt
382
383 progname = sys.argv[0]
384 if len(sys.argv) < 2:
385 sys.exit('usage: %s <command> [<command args>]' % progname)
386
387 fixwhite_usage = '%s fixwhite [-t <tabsize> ] <path> [...] \n' % progname
388 chkformat_usage = '%s chkformat <path> [...] \n' % progname
389 chkwhite_usage = '%s chkwhite <path> [...] \n' % progname
390
391 command = sys.argv[1]
392 if command == 'fixwhite':
393 flags = 't:'
394 usage = fixwhite_usage
395 elif command == 'chkwhite':
396 flags = 'nv'
397 usage = chkwhite_usage
398 elif command == 'chkformat':
399 flags = 'nv'
400 usage = chkformat_usage
401 else:
402 sys.exit(fixwhite_usage + chkwhite_usage + chkformat_usage)
403
404 opts, args = getopt.getopt(sys.argv[2:], flags)
405
406 code = 1
407 verbose = 1
408 for opt,arg in opts:
409 if opt == '-n':
410 code = None
411 if opt == '-t':
412 tabsize = int(arg)
413 if opt == '-v':
414 verbose += 1
415
416 if command == 'fixwhite':
417 for filename in args:
418 fixwhite(filename, tabsize)
419 elif command == 'chkwhite':
420 for filename in args:
421 for line,num in checkwhite(filename):
422 print 'invalid whitespace: %s:%d' % (filename, num)
423 if verbose:
424 print '>>%s<<' % line[:-1]
425 elif command == 'chkformat':
426 stats = ValidationStats()
427 for filename in args:
428 validate(filename, stats=stats, verbose=verbose, exit_code=code)
429
430 if verbose > 0:
431 stats.dump()
432 else:
433 sys.exit("command '%s' not found" % command)