style.py revision 4794:88afe390fc0f
1360SN/A#! /usr/bin/env python 210850SGiacomo.Gabrielli@arm.com# Copyright (c) 2007 The Regents of The University of Michigan 310796Sbrandon.potter@amd.com# All rights reserved. 410027SChris.Adeniyi-Jones@arm.com# 510027SChris.Adeniyi-Jones@arm.com# Redistribution and use in source and binary forms, with or without 610027SChris.Adeniyi-Jones@arm.com# modification, are permitted provided that the following conditions are 710027SChris.Adeniyi-Jones@arm.com# met: redistributions of source code must retain the above copyright 810027SChris.Adeniyi-Jones@arm.com# notice, this list of conditions and the following disclaimer; 910027SChris.Adeniyi-Jones@arm.com# redistributions in binary form must reproduce the above copyright 1010027SChris.Adeniyi-Jones@arm.com# notice, this list of conditions and the following disclaimer in the 1110027SChris.Adeniyi-Jones@arm.com# documentation and/or other materials provided with the distribution; 1210027SChris.Adeniyi-Jones@arm.com# neither the name of the copyright holders nor the names of its 1310027SChris.Adeniyi-Jones@arm.com# contributors may be used to endorse or promote products derived from 1410027SChris.Adeniyi-Jones@arm.com# this software without specific prior written permission. 151458SN/A# 16360SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17360SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18360SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19360SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20360SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21360SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22360SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23360SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24360SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25360SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26360SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27360SN/A# 28360SN/A# Authors: Nathan Binkert 29360SN/A 30360SN/Aimport re 31360SN/Aimport os 32360SN/Aimport sys 33360SN/A 34360SN/Alead = re.compile(r'^([ \t]+)') 35360SN/Atrail = re.compile(r'([ \t]+)$') 36360SN/Aany_control = re.compile(r'\b(if|while|for)[ \t]*[(]') 37360SN/Agood_control = re.compile(r'\b(if|while|for) [(]') 38360SN/A 39360SN/Alang_types = { 'c' : "C", 402665Ssaidi@eecs.umich.edu 'h' : "C", 412665Ssaidi@eecs.umich.edu 'cc' : "C++", 422665Ssaidi@eecs.umich.edu 'hh' : "C++", 43360SN/A 'cxx' : "C++", 44360SN/A 'hxx' : "C++", 451354SN/A 'cpp' : "C++", 461354SN/A 'hpp' : "C++", 47360SN/A 'C' : "C++", 482764Sstever@eecs.umich.edu 'H' : "C++", 499202Spalle@lyckegaard.dk 'i' : "swig", 509202Spalle@lyckegaard.dk 'py' : "python", 512064SN/A 's' : "asm", 52360SN/A 'S' : "asm", 53360SN/A 'isa' : "isa" } 54360SN/Adef file_type(filename): 55360SN/A extension = filename.split('.') 56360SN/A extension = len(extension) > 1 and extension[-1] 57360SN/A return lang_types.get(extension, None) 581809SN/A 595543Ssaidi@eecs.umich.eduwhitespace_types = ('C', 'C++', 'swig', 'python', 'asm', 'isa') 601809SN/Adef whitespace_file(filename): 613113Sgblack@eecs.umich.edu if file_type(filename) in whitespace_types: 628229Snate@binkert.org return True 638229Snate@binkert.org 643113Sgblack@eecs.umich.edu if filename.startswith("SCons"): 657075Snate@binkert.org return True 668229Snate@binkert.org 677075Snate@binkert.org return False 68360SN/A 692474SN/Aformat_types = ( 'C', 'C++' ) 705543Ssaidi@eecs.umich.edudef format_file(filename): 712462SN/A if file_type(filename) in format_types: 721354SN/A return True 736216Snate@binkert.org 746658Snate@binkert.org return False 752474SN/A 762680Sktlim@umich.edudef checkwhite_line(line): 778232Snate@binkert.org match = lead.search(line) 788229Snate@binkert.org if match and match.group(1).find('\t') != -1: 797678Sgblack@eecs.umich.edu return False 8010496Ssteve.reinhardt@amd.com 818229Snate@binkert.org match = trail.search(line) 8210497Ssteve.reinhardt@amd.com if match: 838766Sgblack@eecs.umich.edu return False 846640Svince@csl.cornell.edu 85360SN/A return True 86360SN/A 87360SN/Adef checkwhite(filename): 88360SN/A if not whitespace_file(filename): 89360SN/A return 90360SN/A 91360SN/A try: 92360SN/A f = file(filename, 'r+') 93378SN/A except OSError, msg: 941450SN/A print 'could not open file %s: %s' % (filename, msg) 953114Sgblack@eecs.umich.edu return 96360SN/A 975543Ssaidi@eecs.umich.edu for num,line in enumerate(f): 985543Ssaidi@eecs.umich.edu if not checkwhite_line(line): 995543Ssaidi@eecs.umich.edu yield line,num + 1 10010831Ssteve.reinhardt@amd.com 101360SN/Adef fixwhite_line(line, tabsize): 102360SN/A if lead.search(line): 103360SN/A newline = '' 104360SN/A for i,c in enumerate(line): 105360SN/A if c == ' ': 1062680Sktlim@umich.edu newline += ' ' 107360SN/A elif c == '\t': 10810831Ssteve.reinhardt@amd.com newline += ' ' * (tabsize - len(newline) % tabsize) 10910831Ssteve.reinhardt@amd.com else: 110360SN/A newline += line[i:] 111360SN/A break 112360SN/A 113360SN/A line = newline 11410831Ssteve.reinhardt@amd.com 115360SN/A return line.rstrip() + '\n' 116360SN/A 117360SN/Adef fixwhite(filename, tabsize, fixonly=None): 118360SN/A if not whitespace_file(filename): 1193114Sgblack@eecs.umich.edu return 12010831Ssteve.reinhardt@amd.com 12110831Ssteve.reinhardt@amd.com try: 12210831Ssteve.reinhardt@amd.com f = file(filename, 'r+') 123360SN/A except OSError, msg: 124360SN/A print 'could not open file %s: %s' % (filename, msg) 125360SN/A return 126360SN/A 127360SN/A lines = list(f) 128360SN/A 129360SN/A f.seek(0) 130360SN/A f.truncate() 131360SN/A 132360SN/A for i,line in enumerate(lines): 133360SN/A if fixonly is None or i in fixonly: 134360SN/A line = fixwhite_line(line, tabsize) 135378SN/A 1361706SN/A print >>f, line, 1373114Sgblack@eecs.umich.edu 138378SN/Adef linelen(line): 139378SN/A tabs = line.count('\t') 140378SN/A if not tabs: 141378SN/A return len(line) 142378SN/A 1431706SN/A count = 0 1443114Sgblack@eecs.umich.edu for c in line: 145360SN/A if c == '\t': 1466109Ssanchezd@stanford.edu count += tabsize - count % tabsize 1471706SN/A else: 1483114Sgblack@eecs.umich.edu count += 1 149378SN/A 1506109Ssanchezd@stanford.edu return count 1516109Ssanchezd@stanford.edu 1526109Ssanchezd@stanford.educlass ValidationStats(object): 1536109Ssanchezd@stanford.edu def __init__(self): 154378SN/A self.toolong = 0 1551706SN/A self.toolong80 = 0 1563114Sgblack@eecs.umich.edu self.leadtabs = 0 157378SN/A self.trailwhite = 0 1585748SSteve.Reinhardt@amd.com self.badcontrol = 0 1595748SSteve.Reinhardt@amd.com self.cret = 0 1605748SSteve.Reinhardt@amd.com 161378SN/A def dump(self): 162378SN/A print '''\ 1631706SN/A%d violations of lines over 79 chars. %d of which are 80 chars exactly. 1643114Sgblack@eecs.umich.edu%d cases of whitespace at the end of a line. 165378SN/A%d cases of tabs to indent. 166378SN/A%d bad parens after if/while/for. 1671706SN/A%d carriage returns found. 1683114Sgblack@eecs.umich.edu''' % (self.toolong, self.toolong80, self.trailwhite, self.leadtabs, 169378SN/A self.badcontrol, self.cret) 170378SN/A 1711706SN/A def __nonzero__(self): 1723114Sgblack@eecs.umich.edu return self.toolong or self.toolong80 or self.leadtabs or \ 173378SN/A self.trailwhite or self.badcontrol or self.cret 174378SN/A 1751706SN/Adef validate(filename, stats, verbose, exit_code): 1763114Sgblack@eecs.umich.edu if not format_file(filename): 177378SN/A return 1784118Sgblack@eecs.umich.edu 1794118Sgblack@eecs.umich.edu def msg(lineno, line, message): 1804118Sgblack@eecs.umich.edu print '%s:%d>' % (filename, lineno + 1), message 1814118Sgblack@eecs.umich.edu if verbose > 2: 182378SN/A print line 1831706SN/A 1843114Sgblack@eecs.umich.edu def bad(): 185378SN/A if exit_code is not None: 186378SN/A sys.exit(exit_code) 1871706SN/A 1883114Sgblack@eecs.umich.edu cpp = filename.endswith('.cc') or filename.endswith('.hh') 189360SN/A py = filename.endswith('.py') 1905513SMichael.Adler@intel.com 1915513SMichael.Adler@intel.com if py + cpp != 1: 1925513SMichael.Adler@intel.com raise AttributeError, \ 1935513SMichael.Adler@intel.com "I don't know how to deal with the file %s" % filename 19410203SAli.Saidi@ARM.com 19510203SAli.Saidi@ARM.com try: 19610203SAli.Saidi@ARM.com f = file(filename, 'r') 19710203SAli.Saidi@ARM.com except OSError: 1985513SMichael.Adler@intel.com if verbose > 0: 1995513SMichael.Adler@intel.com print 'could not open file %s' % filename 2005513SMichael.Adler@intel.com bad() 201511SN/A return 20210633Smichaelupton@gmail.com 20310633Smichaelupton@gmail.com for i,line in enumerate(f): 20410633Smichaelupton@gmail.com line = line.rstrip('\n') 2051706SN/A 2063114Sgblack@eecs.umich.edu # no carriage returns 207511SN/A if line.find('\r') != -1: 2085513SMichael.Adler@intel.com self.cret += 1 2095513SMichael.Adler@intel.com if verbose > 1: 2105513SMichael.Adler@intel.com msg(i, line, 'carriage return found') 2115513SMichael.Adler@intel.com bad() 212511SN/A 2131706SN/A # lines max out at 79 chars 2143114Sgblack@eecs.umich.edu llen = linelen(line) 2151706SN/A if llen > 79: 2161706SN/A stats.toolong += 1 2171706SN/A if llen == 80: 2181706SN/A stats.toolong80 += 1 2193114Sgblack@eecs.umich.edu if verbose > 1: 2201706SN/A msg(i, line, 'line too long (%d chars)' % llen) 2211706SN/A bad() 2221706SN/A 2231706SN/A # no tabs used to indent 2243114Sgblack@eecs.umich.edu match = lead.search(line) 2251706SN/A if match and match.group(1).find('\t') != -1: 226511SN/A stats.leadtabs += 1 2276703Svince@csl.cornell.edu if verbose > 1: 2286703Svince@csl.cornell.edu msg(i, line, 'using tabs to indent') 2296703Svince@csl.cornell.edu bad() 2306703Svince@csl.cornell.edu 2316685Stjones1@inf.ed.ac.uk # no trailing whitespace 2326685Stjones1@inf.ed.ac.uk if trail.search(line): 2336685Stjones1@inf.ed.ac.uk stats.trailwhite +=1 2346685Stjones1@inf.ed.ac.uk if verbose > 1: 2356685Stjones1@inf.ed.ac.uk msg(i, line, 'trailing whitespace') 2365513SMichael.Adler@intel.com bad() 2375513SMichael.Adler@intel.com 2385513SMichael.Adler@intel.com # for c++, exactly one space betwen if/while/for and ( 2395513SMichael.Adler@intel.com if cpp: 2405513SMichael.Adler@intel.com match = any_control.search(line) 2411999SN/A if match and not good_control.search(line): 2421999SN/A stats.badcontrol += 1 2433114Sgblack@eecs.umich.edu if verbose > 1: 2441999SN/A msg(i, line, 'improper spacing after %s' % match.group(1)) 2451999SN/A bad() 2461999SN/A 2471999SN/Adef modified_lines(old_data, new_data, max_lines): 2483114Sgblack@eecs.umich.edu from itertools import count 2491999SN/A from mercurial import bdiff, mdiff 2503079Sstever@eecs.umich.edu 2513079Sstever@eecs.umich.edu modified = set() 2523114Sgblack@eecs.umich.edu counter = count() 2533079Sstever@eecs.umich.edu for pbeg, pend, fbeg, fend in bdiff.blocks(old_data, new_data): 2542093SN/A for i in counter: 2552093SN/A if i < fbeg: 2563114Sgblack@eecs.umich.edu modified.add(i) 2572093SN/A elif i + 1 >= fend: 2582687Sksewell@umich.edu break 2592687Sksewell@umich.edu elif i > max_lines: 2603114Sgblack@eecs.umich.edu break 2612687Sksewell@umich.edu return modified 2622238SN/A 2632238SN/Adef check_whitespace(ui, repo, hooktype, node, parent1, parent2): 2643114Sgblack@eecs.umich.edu from mercurial import mdiff 2652238SN/A 2662238SN/A if hooktype != 'pretxncommit': 2672238SN/A raise AttributeError, \ 2683114Sgblack@eecs.umich.edu "This hook is only meant for pretxncommit, not %s" % hooktype 2692238SN/A 2702238SN/A tabsize = 8 2712238SN/A verbose = ui.configbool('style', 'verbose', False) 2723114Sgblack@eecs.umich.edu def prompt(name, fixonly=None): 2732238SN/A result = ui.prompt("(a)bort, (i)gnore, or (f)ix?", "^[aif]$", "a") 2742238SN/A if result == 'a': 2752238SN/A return True 2763114Sgblack@eecs.umich.edu elif result == 'i': 2772238SN/A pass 2782238SN/A elif result == 'f': 2792238SN/A fixwhite(repo.wjoin(name), tabsize, fixonly) 2803114Sgblack@eecs.umich.edu else: 2812238SN/A raise RepoError, "Invalid response: '%s'" % result 2822238SN/A 2832238SN/A return False 2843114Sgblack@eecs.umich.edu 2852238SN/A modified, added, removed, deleted, unknown, ignore, clean = repo.status() 2862238SN/A 2872238SN/A for fname in added: 2883114Sgblack@eecs.umich.edu ok = True 2892238SN/A for line,num in checkwhite(fname): 2906109Ssanchezd@stanford.edu ui.write("invalid whitespace in %s:%d\n" % (fname, num)) 2916109Ssanchezd@stanford.edu if verbose: 2926109Ssanchezd@stanford.edu ui.write(">>%s<<\n" % line[-1]) 2932238SN/A ok = False 2949455Smitch.hayenga+gem5@gmail.com 2959455Smitch.hayenga+gem5@gmail.com if not ok: 2969455Smitch.hayenga+gem5@gmail.com if prompt(fname): 29710203SAli.Saidi@ARM.com return True 29810203SAli.Saidi@ARM.com 29910203SAli.Saidi@ARM.com wctx = repo.workingctx() 3009455Smitch.hayenga+gem5@gmail.com for fname in modified: 3019112Smarc.orr@gmail.com fctx = wctx.filectx(fname) 3029112Smarc.orr@gmail.com pctx = fctx.parents() 3039112Smarc.orr@gmail.com assert len(pctx) in (1, 2) 3049112Smarc.orr@gmail.com 3059112Smarc.orr@gmail.com file_data = fctx.data() 3069112Smarc.orr@gmail.com lines = mdiff.splitnewlines(file_data) 3079112Smarc.orr@gmail.com mod_lines = modified_lines(pctx[0].data(), file_data, len(lines)) 3089112Smarc.orr@gmail.com if len(pctx) == 2: 3099112Smarc.orr@gmail.com m2 = modified_lines(pctx[1].data(), file_data, len(lines)) 3109112Smarc.orr@gmail.com mod_lines = mod_lines & m2 # only the lines that are new in both 3119112Smarc.orr@gmail.com 3129112Smarc.orr@gmail.com fixonly = set() 3139112Smarc.orr@gmail.com for i,line in enumerate(lines): 3149112Smarc.orr@gmail.com if i not in mod_lines: 3159112Smarc.orr@gmail.com continue 3169112Smarc.orr@gmail.com 3179112Smarc.orr@gmail.com if checkwhite_line(line): 3189112Smarc.orr@gmail.com continue 3199112Smarc.orr@gmail.com 3209112Smarc.orr@gmail.com ui.write("invalid whitespace: %s:%d\n" % (fname, i+1)) 3219112Smarc.orr@gmail.com if verbose: 3229112Smarc.orr@gmail.com ui.write(">>%s<<\n" % line[:-1]) 3239112Smarc.orr@gmail.com fixonly.add(i) 3249112Smarc.orr@gmail.com 3259238Slluc.alvarez@bsc.es if fixonly: 3269112Smarc.orr@gmail.com if prompt(fname, fixonly): 3279112Smarc.orr@gmail.com return True 3289112Smarc.orr@gmail.com 3299112Smarc.orr@gmail.comdef check_format(ui, repo, hooktype, node, parent1, parent2): 3309112Smarc.orr@gmail.com if hooktype != 'pretxncommit': 3319112Smarc.orr@gmail.com raise AttributeError, \ 3329112Smarc.orr@gmail.com "This hook is only meant for pretxncommit, not %s" % hooktype 3339112Smarc.orr@gmail.com 3349112Smarc.orr@gmail.com modified, added, removed, deleted, unknown, ignore, clean = repo.status() 3359112Smarc.orr@gmail.com 3369112Smarc.orr@gmail.com verbose = 0 3379112Smarc.orr@gmail.com stats = ValidationStats() 3389112Smarc.orr@gmail.com for f in modified + added: 3399112Smarc.orr@gmail.com validate(f, stats, verbose, None) 3409112Smarc.orr@gmail.com 3419112Smarc.orr@gmail.com if stats: 3429112Smarc.orr@gmail.com stats.dump() 3439112Smarc.orr@gmail.com result = ui.prompt("invalid formatting\n(i)gnore or (a)bort?", 3449112Smarc.orr@gmail.com "^[ia]$", "a") 3459112Smarc.orr@gmail.com if result.startswith('i'): 3469112Smarc.orr@gmail.com pass 3479112Smarc.orr@gmail.com elif result.startswith('a'): 3489112Smarc.orr@gmail.com return True 3499112Smarc.orr@gmail.com else: 3509112Smarc.orr@gmail.com raise RepoError, "Invalid response: '%s'" % result 3519112Smarc.orr@gmail.com 3529112Smarc.orr@gmail.com return False 3539112Smarc.orr@gmail.com 3549112Smarc.orr@gmail.comif __name__ == '__main__': 3559112Smarc.orr@gmail.com import getopt 3569112Smarc.orr@gmail.com 3579112Smarc.orr@gmail.com progname = sys.argv[0] 3589112Smarc.orr@gmail.com if len(sys.argv) < 2: 3599112Smarc.orr@gmail.com sys.exit('usage: %s <command> [<command args>]' % progname) 3609112Smarc.orr@gmail.com 3619112Smarc.orr@gmail.com fixwhite_usage = '%s fixwhite [-t <tabsize> ] <path> [...] \n' % progname 3629112Smarc.orr@gmail.com chkformat_usage = '%s chkformat <path> [...] \n' % progname 3639112Smarc.orr@gmail.com chkwhite_usage = '%s chkwhite <path> [...] \n' % progname 3649112Smarc.orr@gmail.com 3659112Smarc.orr@gmail.com command = sys.argv[1] 3669112Smarc.orr@gmail.com if command == 'fixwhite': 3679112Smarc.orr@gmail.com flags = 't:' 3689112Smarc.orr@gmail.com usage = fixwhite_usage 3699112Smarc.orr@gmail.com elif command == 'chkwhite': 3709112Smarc.orr@gmail.com flags = 'nv' 3719112Smarc.orr@gmail.com usage = chkwhite_usage 3729112Smarc.orr@gmail.com elif command == 'chkformat': 3739112Smarc.orr@gmail.com flags = 'nv' 3749112Smarc.orr@gmail.com usage = chkformat_usage 3759112Smarc.orr@gmail.com else: 3769112Smarc.orr@gmail.com sys.exit(fixwhite_usage + chkwhite_usage + chkformat_usage) 3779238Slluc.alvarez@bsc.es 3789112Smarc.orr@gmail.com opts, args = getopt.getopt(sys.argv[2:], flags) 3799112Smarc.orr@gmail.com 3809112Smarc.orr@gmail.com code = 1 3819112Smarc.orr@gmail.com verbose = 1 3829112Smarc.orr@gmail.com tabsize = 8 3832238SN/A for opt,arg in opts: 3842238SN/A if opt == '-n': 3852238SN/A code = None 3862238SN/A if opt == '-t': 3873114Sgblack@eecs.umich.edu tabsize = int(arg) 3882238SN/A if opt == '-v': 3892238SN/A verbose += 1 3902238SN/A 3913114Sgblack@eecs.umich.edu if command == 'fixwhite': 3922238SN/A for filename in args: 3932238SN/A fixwhite(filename, tabsize) 3942238SN/A elif command == 'chkwhite': 3953114Sgblack@eecs.umich.edu for filename in args: 3962238SN/A for line,num in checkwhite(filename): 3972238SN/A print 'invalid whitespace: %s:%d' % (filename, num) 3982238SN/A if verbose: 3993114Sgblack@eecs.umich.edu print '>>%s<<' % line[:-1] 4002238SN/A elif command == 'chkformat': 4012238SN/A stats = ValidationStats() 4021354SN/A for filename in files: 4031354SN/A validate(filename, stats=stats, verbose=verbose, exit_code=code) 40410796Sbrandon.potter@amd.com 40510796Sbrandon.potter@amd.com if verbose > 0: 4061354SN/A stats.dump() 4071354SN/A else: 4081354SN/A sys.exit("command '%s' not found" % command) 4091354SN/A