| #!/usr/bin/env python3 | 
 | ## | 
 | ## Copyright (c) 2016, Alliance for Open Media. All rights reserved. | 
 | ## | 
 | ## This source code is subject to the terms of the BSD 2 Clause License and | 
 | ## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | 
 | ## was not distributed with this source code in the LICENSE file, you can | 
 | ## obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent. | 
 | ## | 
 | """Performs style checking on each diff hunk.""" | 
 | import getopt | 
 | import os | 
 | import io | 
 | 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 as 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, text=True) | 
 |         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, text=True) | 
 |         stdout = p.communicate()[0] | 
 |         for hunk in diff.ParseDiffHunks(io.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.items(): | 
 |             if filename.split(".")[-1] not in ("c", "h", "cc"): | 
 |                 continue | 
 |             if filename.startswith("third_party"): | 
 |                 continue | 
 |  | 
 |             if args: | 
 |                 # File contents come from git | 
 |                 show_cmd = SHOW_CMD + [args[0] + ":" + filename] | 
 |                 show = Subprocess(show_cmd, stdout=subprocess.PIPE, text=True) | 
 |                 lint = Subprocess(cpplint_cmd, expected_returncode=(0, 1), | 
 |                                   stdin=show.stdout, stderr=subprocess.PIPE, | 
 |                                   text=True) | 
 |                 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, | 
 |                                   text=True) | 
 |                 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 as err: | 
 |         print(err, file=sys.stderr) | 
 |         print("for help use --help", file=sys.stderr) | 
 |         return 2 | 
 |  | 
 | if __name__ == "__main__": | 
 |     sys.exit(main()) |