|  | #!/usr/bin/python | 
|  | ## | 
|  | ## Copyright (c) 2021, Alliance for Open Media. All rights reserved | 
|  | ## | 
|  | ## This source code is subject to the terms of the BSD 3-Clause Clear License and the | 
|  | ## Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License was | 
|  | ## not distributed with this source code in the LICENSE file, you can obtain it | 
|  | ## at aomedia.org/license/software-license/bsd-3-c-c/.  If the Alliance for Open Media Patent | 
|  | ## License 1.0 was not distributed with this source code in the PATENTS file, you | 
|  | ## can obtain it at aomedia.org/license/patent-license/. | 
|  | ## | 
|  | """Performs style checking on each diff hunk.""" | 
|  | import getopt | 
|  | import os | 
|  | import StringIO | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | import diff | 
|  |  | 
|  |  | 
|  | SHORT_OPTIONS = "h" | 
|  | LONG_OPTIONS = ["help"] | 
|  |  | 
|  | TOPLEVEL_CMD = ["git", "rev-parse", "--show-toplevel"] | 
|  | DIFF_CMD = ["git", "diff"] | 
|  | DIFF_INDEX_CMD = ["git", "diff-index", "-u", "HEAD", "--"] | 
|  | SHOW_CMD = ["git", "show"] | 
|  | CPPLINT_FILTERS = ["-readability/casting"] | 
|  |  | 
|  |  | 
|  | class Usage(Exception): | 
|  | pass | 
|  |  | 
|  |  | 
|  | class SubprocessException(Exception): | 
|  | def __init__(self, args): | 
|  | msg = "Failed to execute '%s'"%(" ".join(args)) | 
|  | super(SubprocessException, self).__init__(msg) | 
|  |  | 
|  |  | 
|  | class Subprocess(subprocess.Popen): | 
|  | """Adds the notion of an expected returncode to Popen.""" | 
|  |  | 
|  | def __init__(self, args, expected_returncode=0, **kwargs): | 
|  | self._args = args | 
|  | self._expected_returncode = expected_returncode | 
|  | super(Subprocess, self).__init__(args, **kwargs) | 
|  |  | 
|  | def communicate(self, *args, **kwargs): | 
|  | result = super(Subprocess, self).communicate(*args, **kwargs) | 
|  | if self._expected_returncode is not None: | 
|  | try: | 
|  | ok = self.returncode in self._expected_returncode | 
|  | except TypeError: | 
|  | ok = self.returncode == self._expected_returncode | 
|  | if not ok: | 
|  | raise SubprocessException(self._args) | 
|  | return result | 
|  |  | 
|  |  | 
|  | def main(argv=None): | 
|  | if argv is None: | 
|  | argv = sys.argv | 
|  | try: | 
|  | try: | 
|  | opts, args = getopt.getopt(argv[1:], SHORT_OPTIONS, LONG_OPTIONS) | 
|  | except getopt.error, msg: | 
|  | raise Usage(msg) | 
|  |  | 
|  | # process options | 
|  | for o, _ in opts: | 
|  | if o in ("-h", "--help"): | 
|  | print __doc__ | 
|  | sys.exit(0) | 
|  |  | 
|  | if args and len(args) > 1: | 
|  | print __doc__ | 
|  | sys.exit(0) | 
|  |  | 
|  | # Find the fully qualified path to the root of the tree | 
|  | tl = Subprocess(TOPLEVEL_CMD, stdout=subprocess.PIPE) | 
|  | tl = tl.communicate()[0].strip() | 
|  |  | 
|  | # See if we're working on the index or not. | 
|  | if args: | 
|  | diff_cmd = DIFF_CMD + [args[0] + "^!"] | 
|  | else: | 
|  | diff_cmd = DIFF_INDEX_CMD | 
|  |  | 
|  | # Build the command line to execute cpplint | 
|  | cpplint_cmd = [os.path.join(tl, "tools", "cpplint.py"), | 
|  | "--filter=" + ",".join(CPPLINT_FILTERS), | 
|  | "-"] | 
|  |  | 
|  | # Get a list of all affected lines | 
|  | file_affected_line_map = {} | 
|  | p = Subprocess(diff_cmd, stdout=subprocess.PIPE) | 
|  | stdout = p.communicate()[0] | 
|  | for hunk in diff.ParseDiffHunks(StringIO.StringIO(stdout)): | 
|  | filename = hunk.right.filename[2:] | 
|  | if filename not in file_affected_line_map: | 
|  | file_affected_line_map[filename] = set() | 
|  | file_affected_line_map[filename].update(hunk.right.delta_line_nums) | 
|  |  | 
|  | # Run each affected file through cpplint | 
|  | lint_failed = False | 
|  | for filename, affected_lines in file_affected_line_map.iteritems(): | 
|  | if filename.split(".")[-1] not in ("c", "h", "cc"): | 
|  | continue | 
|  |  | 
|  | if args: | 
|  | # File contents come from git | 
|  | show_cmd = SHOW_CMD + [args[0] + ":" + filename] | 
|  | show = Subprocess(show_cmd, stdout=subprocess.PIPE) | 
|  | lint = Subprocess(cpplint_cmd, expected_returncode=(0, 1), | 
|  | stdin=show.stdout, stderr=subprocess.PIPE) | 
|  | lint_out = lint.communicate()[1] | 
|  | else: | 
|  | # File contents come from the working tree | 
|  | lint = Subprocess(cpplint_cmd, expected_returncode=(0, 1), | 
|  | stdin=subprocess.PIPE, stderr=subprocess.PIPE) | 
|  | stdin = open(os.path.join(tl, filename)).read() | 
|  | lint_out = lint.communicate(stdin)[1] | 
|  |  | 
|  | for line in lint_out.split("\n"): | 
|  | fields = line.split(":") | 
|  | if fields[0] != "-": | 
|  | continue | 
|  | warning_line_num = int(fields[1]) | 
|  | if warning_line_num in affected_lines: | 
|  | print "%s:%d:%s"%(filename, warning_line_num, | 
|  | ":".join(fields[2:])) | 
|  | lint_failed = True | 
|  |  | 
|  | # Set exit code if any relevant lint errors seen | 
|  | if lint_failed: | 
|  | return 1 | 
|  |  | 
|  | except Usage, err: | 
|  | print >>sys.stderr, err | 
|  | print >>sys.stderr, "for help use --help" | 
|  | return 2 | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | sys.exit(main()) |