1*77c1e3ccSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*77c1e3ccSAndroid Build Coastguard Worker## 3*77c1e3ccSAndroid Build Coastguard Worker## Copyright (c) 2016, Alliance for Open Media. All rights reserved. 4*77c1e3ccSAndroid Build Coastguard Worker## 5*77c1e3ccSAndroid Build Coastguard Worker## This source code is subject to the terms of the BSD 2 Clause License and 6*77c1e3ccSAndroid Build Coastguard Worker## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 7*77c1e3ccSAndroid Build Coastguard Worker## was not distributed with this source code in the LICENSE file, you can 8*77c1e3ccSAndroid Build Coastguard Worker## obtain it at www.aomedia.org/license/software. If the Alliance for Open 9*77c1e3ccSAndroid Build Coastguard Worker## Media Patent License 1.0 was not distributed with this source code in the 10*77c1e3ccSAndroid Build Coastguard Worker## PATENTS file, you can obtain it at www.aomedia.org/license/patent. 11*77c1e3ccSAndroid Build Coastguard Worker## 12*77c1e3ccSAndroid Build Coastguard Worker"""Performs style checking on each diff hunk.""" 13*77c1e3ccSAndroid Build Coastguard Workerimport getopt 14*77c1e3ccSAndroid Build Coastguard Workerimport os 15*77c1e3ccSAndroid Build Coastguard Workerimport io 16*77c1e3ccSAndroid Build Coastguard Workerimport subprocess 17*77c1e3ccSAndroid Build Coastguard Workerimport sys 18*77c1e3ccSAndroid Build Coastguard Worker 19*77c1e3ccSAndroid Build Coastguard Workerimport diff 20*77c1e3ccSAndroid Build Coastguard Worker 21*77c1e3ccSAndroid Build Coastguard Worker 22*77c1e3ccSAndroid Build Coastguard WorkerSHORT_OPTIONS = "h" 23*77c1e3ccSAndroid Build Coastguard WorkerLONG_OPTIONS = ["help"] 24*77c1e3ccSAndroid Build Coastguard Worker 25*77c1e3ccSAndroid Build Coastguard WorkerTOPLEVEL_CMD = ["git", "rev-parse", "--show-toplevel"] 26*77c1e3ccSAndroid Build Coastguard WorkerDIFF_CMD = ["git", "diff"] 27*77c1e3ccSAndroid Build Coastguard WorkerDIFF_INDEX_CMD = ["git", "diff-index", "-u", "HEAD", "--"] 28*77c1e3ccSAndroid Build Coastguard WorkerSHOW_CMD = ["git", "show"] 29*77c1e3ccSAndroid Build Coastguard WorkerCPPLINT_FILTERS = ["-readability/casting"] 30*77c1e3ccSAndroid Build Coastguard Worker 31*77c1e3ccSAndroid Build Coastguard Worker 32*77c1e3ccSAndroid Build Coastguard Workerclass Usage(Exception): 33*77c1e3ccSAndroid Build Coastguard Worker pass 34*77c1e3ccSAndroid Build Coastguard Worker 35*77c1e3ccSAndroid Build Coastguard Worker 36*77c1e3ccSAndroid Build Coastguard Workerclass SubprocessException(Exception): 37*77c1e3ccSAndroid Build Coastguard Worker def __init__(self, args): 38*77c1e3ccSAndroid Build Coastguard Worker msg = "Failed to execute '%s'"%(" ".join(args)) 39*77c1e3ccSAndroid Build Coastguard Worker super(SubprocessException, self).__init__(msg) 40*77c1e3ccSAndroid Build Coastguard Worker 41*77c1e3ccSAndroid Build Coastguard Worker 42*77c1e3ccSAndroid Build Coastguard Workerclass Subprocess(subprocess.Popen): 43*77c1e3ccSAndroid Build Coastguard Worker """Adds the notion of an expected returncode to Popen.""" 44*77c1e3ccSAndroid Build Coastguard Worker 45*77c1e3ccSAndroid Build Coastguard Worker def __init__(self, args, expected_returncode=0, **kwargs): 46*77c1e3ccSAndroid Build Coastguard Worker self._args = args 47*77c1e3ccSAndroid Build Coastguard Worker self._expected_returncode = expected_returncode 48*77c1e3ccSAndroid Build Coastguard Worker super(Subprocess, self).__init__(args, **kwargs) 49*77c1e3ccSAndroid Build Coastguard Worker 50*77c1e3ccSAndroid Build Coastguard Worker def communicate(self, *args, **kwargs): 51*77c1e3ccSAndroid Build Coastguard Worker result = super(Subprocess, self).communicate(*args, **kwargs) 52*77c1e3ccSAndroid Build Coastguard Worker if self._expected_returncode is not None: 53*77c1e3ccSAndroid Build Coastguard Worker try: 54*77c1e3ccSAndroid Build Coastguard Worker ok = self.returncode in self._expected_returncode 55*77c1e3ccSAndroid Build Coastguard Worker except TypeError: 56*77c1e3ccSAndroid Build Coastguard Worker ok = self.returncode == self._expected_returncode 57*77c1e3ccSAndroid Build Coastguard Worker if not ok: 58*77c1e3ccSAndroid Build Coastguard Worker raise SubprocessException(self._args) 59*77c1e3ccSAndroid Build Coastguard Worker return result 60*77c1e3ccSAndroid Build Coastguard Worker 61*77c1e3ccSAndroid Build Coastguard Worker 62*77c1e3ccSAndroid Build Coastguard Workerdef main(argv=None): 63*77c1e3ccSAndroid Build Coastguard Worker if argv is None: 64*77c1e3ccSAndroid Build Coastguard Worker argv = sys.argv 65*77c1e3ccSAndroid Build Coastguard Worker try: 66*77c1e3ccSAndroid Build Coastguard Worker try: 67*77c1e3ccSAndroid Build Coastguard Worker opts, args = getopt.getopt(argv[1:], SHORT_OPTIONS, LONG_OPTIONS) 68*77c1e3ccSAndroid Build Coastguard Worker except getopt.error as msg: 69*77c1e3ccSAndroid Build Coastguard Worker raise Usage(msg) 70*77c1e3ccSAndroid Build Coastguard Worker 71*77c1e3ccSAndroid Build Coastguard Worker # process options 72*77c1e3ccSAndroid Build Coastguard Worker for o, _ in opts: 73*77c1e3ccSAndroid Build Coastguard Worker if o in ("-h", "--help"): 74*77c1e3ccSAndroid Build Coastguard Worker print(__doc__) 75*77c1e3ccSAndroid Build Coastguard Worker sys.exit(0) 76*77c1e3ccSAndroid Build Coastguard Worker 77*77c1e3ccSAndroid Build Coastguard Worker if args and len(args) > 1: 78*77c1e3ccSAndroid Build Coastguard Worker print(__doc__) 79*77c1e3ccSAndroid Build Coastguard Worker sys.exit(0) 80*77c1e3ccSAndroid Build Coastguard Worker 81*77c1e3ccSAndroid Build Coastguard Worker # Find the fully qualified path to the root of the tree 82*77c1e3ccSAndroid Build Coastguard Worker tl = Subprocess(TOPLEVEL_CMD, stdout=subprocess.PIPE, text=True) 83*77c1e3ccSAndroid Build Coastguard Worker tl = tl.communicate()[0].strip() 84*77c1e3ccSAndroid Build Coastguard Worker 85*77c1e3ccSAndroid Build Coastguard Worker # See if we're working on the index or not. 86*77c1e3ccSAndroid Build Coastguard Worker if args: 87*77c1e3ccSAndroid Build Coastguard Worker diff_cmd = DIFF_CMD + [args[0] + "^!"] 88*77c1e3ccSAndroid Build Coastguard Worker else: 89*77c1e3ccSAndroid Build Coastguard Worker diff_cmd = DIFF_INDEX_CMD 90*77c1e3ccSAndroid Build Coastguard Worker 91*77c1e3ccSAndroid Build Coastguard Worker # Build the command line to execute cpplint 92*77c1e3ccSAndroid Build Coastguard Worker cpplint_cmd = [os.path.join(tl, "tools", "cpplint.py"), 93*77c1e3ccSAndroid Build Coastguard Worker "--filter=" + ",".join(CPPLINT_FILTERS), 94*77c1e3ccSAndroid Build Coastguard Worker "-"] 95*77c1e3ccSAndroid Build Coastguard Worker 96*77c1e3ccSAndroid Build Coastguard Worker # Get a list of all affected lines 97*77c1e3ccSAndroid Build Coastguard Worker file_affected_line_map = {} 98*77c1e3ccSAndroid Build Coastguard Worker p = Subprocess(diff_cmd, stdout=subprocess.PIPE, text=True) 99*77c1e3ccSAndroid Build Coastguard Worker stdout = p.communicate()[0] 100*77c1e3ccSAndroid Build Coastguard Worker for hunk in diff.ParseDiffHunks(io.StringIO(stdout)): 101*77c1e3ccSAndroid Build Coastguard Worker filename = hunk.right.filename[2:] 102*77c1e3ccSAndroid Build Coastguard Worker if filename not in file_affected_line_map: 103*77c1e3ccSAndroid Build Coastguard Worker file_affected_line_map[filename] = set() 104*77c1e3ccSAndroid Build Coastguard Worker file_affected_line_map[filename].update(hunk.right.delta_line_nums) 105*77c1e3ccSAndroid Build Coastguard Worker 106*77c1e3ccSAndroid Build Coastguard Worker # Run each affected file through cpplint 107*77c1e3ccSAndroid Build Coastguard Worker lint_failed = False 108*77c1e3ccSAndroid Build Coastguard Worker for filename, affected_lines in file_affected_line_map.items(): 109*77c1e3ccSAndroid Build Coastguard Worker if filename.split(".")[-1] not in ("c", "h", "cc"): 110*77c1e3ccSAndroid Build Coastguard Worker continue 111*77c1e3ccSAndroid Build Coastguard Worker if filename.startswith("third_party"): 112*77c1e3ccSAndroid Build Coastguard Worker continue 113*77c1e3ccSAndroid Build Coastguard Worker 114*77c1e3ccSAndroid Build Coastguard Worker if args: 115*77c1e3ccSAndroid Build Coastguard Worker # File contents come from git 116*77c1e3ccSAndroid Build Coastguard Worker show_cmd = SHOW_CMD + [args[0] + ":" + filename] 117*77c1e3ccSAndroid Build Coastguard Worker show = Subprocess(show_cmd, stdout=subprocess.PIPE, text=True) 118*77c1e3ccSAndroid Build Coastguard Worker lint = Subprocess(cpplint_cmd, expected_returncode=(0, 1), 119*77c1e3ccSAndroid Build Coastguard Worker stdin=show.stdout, stderr=subprocess.PIPE, 120*77c1e3ccSAndroid Build Coastguard Worker text=True) 121*77c1e3ccSAndroid Build Coastguard Worker lint_out = lint.communicate()[1] 122*77c1e3ccSAndroid Build Coastguard Worker else: 123*77c1e3ccSAndroid Build Coastguard Worker # File contents come from the working tree 124*77c1e3ccSAndroid Build Coastguard Worker lint = Subprocess(cpplint_cmd, expected_returncode=(0, 1), 125*77c1e3ccSAndroid Build Coastguard Worker stdin=subprocess.PIPE, stderr=subprocess.PIPE, 126*77c1e3ccSAndroid Build Coastguard Worker text=True) 127*77c1e3ccSAndroid Build Coastguard Worker stdin = open(os.path.join(tl, filename)).read() 128*77c1e3ccSAndroid Build Coastguard Worker lint_out = lint.communicate(stdin)[1] 129*77c1e3ccSAndroid Build Coastguard Worker 130*77c1e3ccSAndroid Build Coastguard Worker for line in lint_out.split("\n"): 131*77c1e3ccSAndroid Build Coastguard Worker fields = line.split(":") 132*77c1e3ccSAndroid Build Coastguard Worker if fields[0] != "-": 133*77c1e3ccSAndroid Build Coastguard Worker continue 134*77c1e3ccSAndroid Build Coastguard Worker warning_line_num = int(fields[1]) 135*77c1e3ccSAndroid Build Coastguard Worker if warning_line_num in affected_lines: 136*77c1e3ccSAndroid Build Coastguard Worker print("%s:%d:%s"%(filename, warning_line_num, 137*77c1e3ccSAndroid Build Coastguard Worker ":".join(fields[2:]))) 138*77c1e3ccSAndroid Build Coastguard Worker lint_failed = True 139*77c1e3ccSAndroid Build Coastguard Worker 140*77c1e3ccSAndroid Build Coastguard Worker # Set exit code if any relevant lint errors seen 141*77c1e3ccSAndroid Build Coastguard Worker if lint_failed: 142*77c1e3ccSAndroid Build Coastguard Worker return 1 143*77c1e3ccSAndroid Build Coastguard Worker 144*77c1e3ccSAndroid Build Coastguard Worker except Usage as err: 145*77c1e3ccSAndroid Build Coastguard Worker print(err, file=sys.stderr) 146*77c1e3ccSAndroid Build Coastguard Worker print("for help use --help", file=sys.stderr) 147*77c1e3ccSAndroid Build Coastguard Worker return 2 148*77c1e3ccSAndroid Build Coastguard Worker 149*77c1e3ccSAndroid Build Coastguard Workerif __name__ == "__main__": 150*77c1e3ccSAndroid Build Coastguard Worker sys.exit(main()) 151