xref: /aosp_15_r20/external/libaom/tools/lint-hunks.py (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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