hgstyle.py revision 11404:72b399971cbc
1#! /usr/bin/env python 2# Copyright (c) 2014 ARM Limited 3# All rights reserved 4# 5# The license below extends only to copyright in the software and shall 6# not be construed as granting a license to any other intellectual 7# property including but not limited to intellectual property relating 8# to a hardware implementation of the functionality of the software 9# licensed hereunder. You may use the software subject to the license 10# terms below provided that you ensure that this notice is replicated 11# unmodified and in its entirety in all distributions of the software, 12# modified or unmodified, in source code or in binary form. 13# 14# Copyright (c) 2006 The Regents of The University of Michigan 15# Copyright (c) 2007,2011 The Hewlett-Packard Development Company 16# Copyright (c) 2016 Advanced Micro Devices, Inc. 17# All rights reserved. 18# 19# Redistribution and use in source and binary forms, with or without 20# modification, are permitted provided that the following conditions are 21# met: redistributions of source code must retain the above copyright 22# notice, this list of conditions and the following disclaimer; 23# redistributions in binary form must reproduce the above copyright 24# notice, this list of conditions and the following disclaimer in the 25# documentation and/or other materials provided with the distribution; 26# neither the name of the copyright holders nor the names of its 27# contributors may be used to endorse or promote products derived from 28# this software without specific prior written permission. 29# 30# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 31# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 32# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 33# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 34# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 36# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 37# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 39# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 40# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41# 42# Authors: Nathan Binkert 43# Steve Reinhardt 44 45import sys 46import os 47from os.path import join as joinpath 48 49current_dir = os.path.dirname(__file__) 50sys.path.insert(0, current_dir) 51 52from style.verifiers import all_verifiers 53from style.validators import all_validators 54from style.file_types import lang_type 55from style.style import MercurialUI, check_ignores 56from style.region import * 57 58from mercurial import bdiff, mdiff, commands 59 60def modified_regions(old_data, new_data): 61 regions = Regions() 62 beg = None 63 for pbeg, pend, fbeg, fend in bdiff.blocks(old_data, new_data): 64 if beg is not None and beg != fbeg: 65 regions.append(beg, fbeg) 66 beg = fend 67 return regions 68 69def modregions(wctx, fname): 70 fctx = wctx.filectx(fname) 71 pctx = fctx.parents() 72 73 file_data = fctx.data() 74 lines = mdiff.splitnewlines(file_data) 75 if len(pctx) in (1, 2): 76 mod_regions = modified_regions(pctx[0].data(), file_data) 77 if len(pctx) == 2: 78 m2 = modified_regions(pctx[1].data(), file_data) 79 # only the lines that are new in both 80 mod_regions &= m2 81 else: 82 mod_regions = Regions() 83 mod_regions.append(0, len(lines)) 84 85 return mod_regions 86 87 88def validate(filename, verbose, exit_code): 89 lang = lang_type(filename) 90 if lang not in ('C', 'C++'): 91 return 92 93 def bad(): 94 if exit_code is not None: 95 sys.exit(exit_code) 96 97 try: 98 f = file(filename, 'r') 99 except OSError: 100 if verbose > 0: 101 print 'could not open file %s' % filename 102 bad() 103 return None 104 105 vals = [ v(filename, verbose=(verbose > 1), language=lang) 106 for v in all_validators ] 107 108 for i, line in enumerate(f): 109 line = line.rstrip('\n') 110 for v in vals: 111 v.validate_line(i, line) 112 113 114 return vals 115 116 117def _modified_regions(repo, patterns, **kwargs): 118 opt_all = kwargs.get('all', False) 119 opt_no_ignore = kwargs.get('no_ignore', False) 120 121 # Import the match (repository file name matching helper) 122 # function. Different versions of Mercurial keep it in different 123 # modules and implement them differently. 124 try: 125 from mercurial import scmutil 126 m = scmutil.match(repo[None], patterns, kwargs) 127 except ImportError: 128 from mercurial import cmdutil 129 m = cmdutil.match(repo, patterns, kwargs) 130 131 modified, added, removed, deleted, unknown, ignore, clean = \ 132 repo.status(match=m, clean=opt_all) 133 134 if not opt_all: 135 try: 136 wctx = repo.workingctx() 137 except: 138 from mercurial import context 139 wctx = context.workingctx(repo) 140 141 files = [ (fn, all_regions) for fn in added ] + \ 142 [ (fn, modregions(wctx, fn)) for fn in modified ] 143 else: 144 files = [ (fn, all_regions) for fn in added + modified + clean ] 145 146 for fname, mod_regions in files: 147 if opt_no_ignore or not check_ignores(fname): 148 yield fname, mod_regions 149 150 151def do_check_style(hgui, repo, *pats, **opts): 152 """check files for proper m5 style guidelines 153 154 Without an argument, checks all modified and added files for gem5 155 coding style violations. A list of files can be specified to limit 156 the checker to a subset of the repository. The style rules are 157 normally applied on a diff of the repository state (i.e., added 158 files are checked in their entirety while only modifications of 159 modified files are checked). 160 161 The --all option can be specified to include clean files and check 162 modified files in their entirety. 163 164 The --fix-<check>, --ignore-<check>, and --skip-<check> options 165 can be used to control individual style checks: 166 167 --fix-<check> will perform the check and automatically attempt to 168 fix sny style error (printing a warning if unsuccessful) 169 170 --ignore-<check> will perform the check but ignore any errors 171 found (other than printing a message for each) 172 173 --skip-<check> will skip performing the check entirely 174 175 If none of these options are given, all checks will be performed 176 and the user will be prompted on how to handle each error. 177 178 --fix-all, --ignore-all, and --skip-all are equivalent to specifying 179 --fix-<check>, --ignore-<check>, or --skip-<check> for all checks, 180 respectively. However, option settings for specific checks take 181 precedence. Thus --skip-all --fix-white can be used to skip every 182 check other than whitespace errors, which will be checked and 183 automatically fixed. 184 185 The -v/--verbose flag will display the offending line(s) as well 186 as their location. 187 """ 188 189 ui = MercurialUI(hgui, verbose=hgui.verbose) 190 191 # instantiate varifier objects 192 verifiers = [v(ui, opts, base=repo.root) for v in all_verifiers] 193 194 for fname, mod_regions in _modified_regions(repo, pats, **opts): 195 for verifier in verifiers: 196 if verifier.apply(joinpath(repo.root, fname), mod_regions): 197 return True 198 199 return False 200 201def do_check_format(hgui, repo, *pats, **opts): 202 """check files for gem5 code formatting violations 203 204 Without an argument, checks all modified and added files for gem5 205 code formatting violations. A list of files can be specified to 206 limit the checker to a subset of the repository. The style rules 207 are normally applied on a diff of the repository state (i.e., 208 added files are checked in their entirety while only modifications 209 of modified files are checked). 210 211 The --all option can be specified to include clean files and check 212 modified files in their entirety. 213 """ 214 ui = MercurialUI(hgui, hgui.verbose) 215 216 verbose = 0 217 for fname, mod_regions in _modified_regions(repo, pats, **opts): 218 vals = validate(joinpath(repo.root, fname), verbose, None) 219 if vals is None: 220 return True 221 elif any([not v for v in vals]): 222 print "%s:" % fname 223 for v in vals: 224 v.dump() 225 result = ui.prompt("invalid formatting\n(i)gnore or (a)bort?", 226 'ai', 'a') 227 if result == 'a': 228 return True 229 230 return False 231 232def check_hook(hooktype): 233 if hooktype not in ('pretxncommit', 'pre-qrefresh'): 234 raise AttributeError, \ 235 "This hook is not meant for %s" % hooktype 236 237# This function provides a hook that is called before transaction 238# commit and on qrefresh 239def check_style(ui, repo, hooktype, **kwargs): 240 check_hook(hooktype) 241 args = {} 242 243 try: 244 return do_check_style(ui, repo, **args) 245 except Exception, e: 246 import traceback 247 traceback.print_exc() 248 return True 249 250def check_format(ui, repo, hooktype, **kwargs): 251 check_hook(hooktype) 252 args = {} 253 254 try: 255 return do_check_format(ui, repo, **args) 256 except Exception, e: 257 import traceback 258 traceback.print_exc() 259 return True 260 261try: 262 from mercurial.i18n import _ 263except ImportError: 264 def _(arg): 265 return arg 266 267_common_region_options = [ 268 ('a', 'all', False, 269 _("include clean files and unmodified parts of modified files")), 270 ('', 'no-ignore', False, _("ignore the style ignore list")), 271 ] 272 273 274fix_opts = [('f', 'fix-all', False, _("fix all style errors"))] + \ 275 [('', 'fix-' + v.opt_name, False, 276 _('fix errors in ' + v.test_name)) for v in all_verifiers] 277ignore_opts = [('', 'ignore-all', False, _("ignore all style errors"))] + \ 278 [('', 'ignore-' + v.opt_name, False, 279 _('ignore errors in ' + v.test_name)) for v in all_verifiers] 280skip_opts = [('', 'skip-all', False, _("skip all style error checks"))] + \ 281 [('', 'skip-' + v.opt_name, False, 282 _('skip checking for ' + v.test_name)) for v in all_verifiers] 283all_opts = fix_opts + ignore_opts + skip_opts 284 285 286cmdtable = { 287 '^m5style' : ( 288 do_check_style, all_opts + _common_region_options + commands.walkopts, 289 _('hg m5style [-a] [FILE]...')), 290 '^m5format' : 291 ( do_check_format, [ 292 ] + _common_region_options + commands.walkopts, 293 _('hg m5format [FILE]...')), 294} 295 296if __name__ == '__main__': 297 import argparse 298 299 parser = argparse.ArgumentParser( 300 description="Check a file for style violations") 301 302 parser.add_argument("--verbose", "-v", action="count", 303 help="Produce verbose output") 304 305 parser.add_argument("file", metavar="FILE", nargs="+", 306 type=str, 307 help="Source file to inspect") 308 309 args = parser.parse_args() 310 311 for filename in args.file: 312 vals = validate(filename, verbose=args.verbose, 313 exit_code=1) 314 315 if args.verbose > 0 and vals is not None: 316 for v in vals: 317 v.dump() 318