2 ## Copyright (c) 2012 The WebM project authors. All Rights Reserved.
4 ## Use of this source code is governed by a BSD-style license
5 ## that can be found in the LICENSE file in the root of the source
6 ## tree. An additional intellectual property rights grant can be found
7 ## in the file PATENTS. All contributing project authors may
8 ## be found in the AUTHORS file in the root of the source tree.
10 """Performs style checking on each diff hunk."""
21 LONG_OPTIONS
= ["help"]
23 TOPLEVEL_CMD
= ["git", "rev-parse", "--show-toplevel"]
24 DIFF_CMD
= ["git", "diff"]
25 DIFF_INDEX_CMD
= ["git", "diff-index", "-u", "HEAD", "--"]
26 SHOW_CMD
= ["git", "show"]
27 CPPLINT_FILTERS
= ["-readability/casting"]
30 class Usage(Exception):
34 class SubprocessException(Exception):
35 def __init__(self
, args
):
36 msg
= "Failed to execute '%s'"%(" ".join(args
))
37 super(SubprocessException
, self
).__init
__(msg
)
40 class Subprocess(subprocess
.Popen
):
41 """Adds the notion of an expected returncode to Popen."""
43 def __init__(self
, args
, expected_returncode
=0, **kwargs
):
45 self
._expected
_returncode
= expected_returncode
46 super(Subprocess
, self
).__init
__(args
, **kwargs
)
48 def communicate(self
, *args
, **kwargs
):
49 result
= super(Subprocess
, self
).communicate(*args
, **kwargs
)
50 if self
._expected
_returncode
is not None:
52 ok
= self
.returncode
in self
._expected
_returncode
54 ok
= self
.returncode
== self
._expected
_returncode
56 raise SubprocessException(self
._args
)
65 opts
, args
= getopt
.getopt(argv
[1:], SHORT_OPTIONS
, LONG_OPTIONS
)
66 except getopt
.error
, msg
:
71 if o
in ("-h", "--help"):
75 if args
and len(args
) > 1:
79 # Find the fully qualified path to the root of the tree
80 tl
= Subprocess(TOPLEVEL_CMD
, stdout
=subprocess
.PIPE
)
81 tl
= tl
.communicate()[0].strip()
83 # See if we're working on the index or not.
85 diff_cmd
= DIFF_CMD
+ [args
[0] + "^!"]
87 diff_cmd
= DIFF_INDEX_CMD
89 # Build the command line to execute cpplint
90 cpplint_cmd
= [os
.path
.join(tl
, "tools", "cpplint.py"),
91 "--filter=" + ",".join(CPPLINT_FILTERS
),
94 # Get a list of all affected lines
95 file_affected_line_map
= {}
96 p
= Subprocess(diff_cmd
, stdout
=subprocess
.PIPE
)
97 stdout
= p
.communicate()[0]
98 for hunk
in diff
.ParseDiffHunks(StringIO
.StringIO(stdout
)):
99 filename
= hunk
.right
.filename
[2:]
100 if filename
not in file_affected_line_map
:
101 file_affected_line_map
[filename
] = set()
102 file_affected_line_map
[filename
].update(hunk
.right
.delta_line_nums
)
104 # Run each affected file through cpplint
106 for filename
, affected_lines
in file_affected_line_map
.iteritems():
107 if filename
.split(".")[-1] not in ("c", "h", "cc"):
111 # File contents come from git
112 show_cmd
= SHOW_CMD
+ [args
[0] + ":" + filename
]
113 show
= Subprocess(show_cmd
, stdout
=subprocess
.PIPE
)
114 lint
= Subprocess(cpplint_cmd
, expected_returncode
=(0, 1),
115 stdin
=show
.stdout
, stderr
=subprocess
.PIPE
)
116 lint_out
= lint
.communicate()[1]
118 # File contents come from the working tree
119 lint
= Subprocess(cpplint_cmd
, expected_returncode
=(0, 1),
120 stdin
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
121 stdin
= open(os
.path
.join(tl
, filename
)).read()
122 lint_out
= lint
.communicate(stdin
)[1]
124 for line
in lint_out
.split("\n"):
125 fields
= line
.split(":")
128 warning_line_num
= int(fields
[1])
129 if warning_line_num
in affected_lines
:
130 print "%s:%d:%s"%(filename
, warning_line_num
,
131 ":".join(fields
[2:]))
134 # Set exit code if any relevant lint errors seen
139 print >>sys
.stderr
, err
140 print >>sys
.stderr
, "for help use --help"
143 if __name__
== "__main__":