tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lint-hunks.py (5292B)


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