Deleted Added
sdiff udiff text old ( 11402:ac9e1a3bed79 ) new ( 11403:e8949ea6961f )
full compact
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()