3 ## Copyright (c) 2016, Alliance for Open Media. All rights reserved
5 ## This source code is subject to the terms of the BSD 2 Clause License and
6 ## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
7 ## was not distributed with this source code in the LICENSE file, you can
8 ## obtain it at www.aomedia.org/license/software. If the Alliance for Open
9 ## Media Patent License 1.0 was not distributed with this source code in the
10 ## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
12 """Performs style checking on each diff hunk."""
23 LONG_OPTIONS
= ["help"]
25 TOPLEVEL_CMD
= ["git", "rev-parse", "--show-toplevel"]
26 DIFF_CMD
= ["git", "diff"]
27 DIFF_INDEX_CMD
= ["git", "diff-index", "-u", "HEAD", "--"]
28 SHOW_CMD
= ["git", "show"]
29 CPPLINT_FILTERS
= ["-readability/casting"]
32 class Usage(Exception):
36 class SubprocessException(Exception):
37 def __init__(self
, args
):
38 msg
= "Failed to execute '%s'"%(" ".join(args
))
39 super(SubprocessException
, self
).__init
__(msg
)
42 class Subprocess(subprocess
.Popen
):
43 """Adds the notion of an expected returncode to Popen."""
45 def __init__(self
, args
, expected_returncode
=0, **kwargs
):
47 self
._expected
_returncode
= expected_returncode
48 super(Subprocess
, self
).__init
__(args
, **kwargs
)
50 def communicate(self
, *args
, **kwargs
):
51 result
= super(Subprocess
, self
).communicate(*args
, **kwargs
)
52 if self
._expected
_returncode
is not None:
54 ok
= self
.returncode
in self
._expected
_returncode
56 ok
= self
.returncode
== self
._expected
_returncode
58 raise SubprocessException(self
._args
)
67 opts
, args
= getopt
.getopt(argv
[1:], SHORT_OPTIONS
, LONG_OPTIONS
)
68 except getopt
.error
, msg
:
73 if o
in ("-h", "--help"):
77 if args
and len(args
) > 1:
81 # Find the fully qualified path to the root of the tree
82 tl
= Subprocess(TOPLEVEL_CMD
, stdout
=subprocess
.PIPE
)
83 tl
= tl
.communicate()[0].strip()
85 # See if we're working on the index or not.
87 diff_cmd
= DIFF_CMD
+ [args
[0] + "^!"]
89 diff_cmd
= DIFF_INDEX_CMD
91 # Build the command line to execute cpplint
92 cpplint_cmd
= [os
.path
.join(tl
, "tools", "cpplint.py"),
93 "--filter=" + ",".join(CPPLINT_FILTERS
),
96 # Get a list of all affected lines
97 file_affected_line_map
= {}
98 p
= Subprocess(diff_cmd
, stdout
=subprocess
.PIPE
)
99 stdout
= p
.communicate()[0]
100 for hunk
in diff
.ParseDiffHunks(StringIO
.StringIO(stdout
)):
101 filename
= hunk
.right
.filename
[2:]
102 if filename
not in file_affected_line_map
:
103 file_affected_line_map
[filename
] = set()
104 file_affected_line_map
[filename
].update(hunk
.right
.delta_line_nums
)
106 # Run each affected file through cpplint
108 for filename
, affected_lines
in file_affected_line_map
.iteritems():
109 if filename
.split(".")[-1] not in ("c", "h", "cc"):
113 # File contents come from git
114 show_cmd
= SHOW_CMD
+ [args
[0] + ":" + filename
]
115 show
= Subprocess(show_cmd
, stdout
=subprocess
.PIPE
)
116 lint
= Subprocess(cpplint_cmd
, expected_returncode
=(0, 1),
117 stdin
=show
.stdout
, stderr
=subprocess
.PIPE
)
118 lint_out
= lint
.communicate()[1]
120 # File contents come from the working tree
121 lint
= Subprocess(cpplint_cmd
, expected_returncode
=(0, 1),
122 stdin
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
123 stdin
= open(os
.path
.join(tl
, filename
)).read()
124 lint_out
= lint
.communicate(stdin
)[1]
126 for line
in lint_out
.split("\n"):
127 fields
= line
.split(":")
130 warning_line_num
= int(fields
[1])
131 if warning_line_num
in affected_lines
:
132 print "%s:%d:%s"%(filename
, warning_line_num
,
133 ":".join(fields
[2:]))
136 # Set exit code if any relevant lint errors seen
141 print >>sys
.stderr
, err
142 print >>sys
.stderr
, "for help use --help"
145 if __name__
== "__main__":