xref: /openbmc/u-boot/tools/patman/checkpatch.py (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
11a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+
2*83d290c5STom Rini# Copyright (c) 2011 The Chromium OS Authors.
30d24de9dSSimon Glass#
40d24de9dSSimon Glass
5d29fe6e2SSimon Glassimport collections
60d24de9dSSimon Glassimport command
70d24de9dSSimon Glassimport gitutil
80d24de9dSSimon Glassimport os
90d24de9dSSimon Glassimport re
1099adf6edSVadim Bendeburyimport sys
110d24de9dSSimon Glassimport terminal
120d24de9dSSimon Glass
130d24de9dSSimon Glassdef FindCheckPatch():
14d96ef37dSDoug Anderson    top_level = gitutil.GetTopLevel()
150d24de9dSSimon Glass    try_list = [
160d24de9dSSimon Glass        os.getcwd(),
170d24de9dSSimon Glass        os.path.join(os.getcwd(), '..', '..'),
18d96ef37dSDoug Anderson        os.path.join(top_level, 'tools'),
19d96ef37dSDoug Anderson        os.path.join(top_level, 'scripts'),
200d24de9dSSimon Glass        '%s/bin' % os.getenv('HOME'),
210d24de9dSSimon Glass        ]
220d24de9dSSimon Glass    # Look in current dir
230d24de9dSSimon Glass    for path in try_list:
240d24de9dSSimon Glass        fname = os.path.join(path, 'checkpatch.pl')
250d24de9dSSimon Glass        if os.path.isfile(fname):
260d24de9dSSimon Glass            return fname
270d24de9dSSimon Glass
280d24de9dSSimon Glass    # Look upwwards for a Chrome OS tree
290d24de9dSSimon Glass    while not os.path.ismount(path):
300d24de9dSSimon Glass        fname = os.path.join(path, 'src', 'third_party', 'kernel', 'files',
310d24de9dSSimon Glass                'scripts', 'checkpatch.pl')
320d24de9dSSimon Glass        if os.path.isfile(fname):
330d24de9dSSimon Glass            return fname
340d24de9dSSimon Glass        path = os.path.dirname(path)
3599adf6edSVadim Bendebury
3631e2141dSMasahiro Yamada    sys.exit('Cannot find checkpatch.pl - please put it in your ' +
3799adf6edSVadim Bendebury             '~/bin directory or use --no-check')
380d24de9dSSimon Glass
390d24de9dSSimon Glassdef CheckPatch(fname, verbose=False):
400d24de9dSSimon Glass    """Run checkpatch.pl on a file.
410d24de9dSSimon Glass
420d24de9dSSimon Glass    Returns:
43d29fe6e2SSimon Glass        namedtuple containing:
44d29fe6e2SSimon Glass            ok: False=failure, True=ok
450d24de9dSSimon Glass            problems: List of problems, each a dict:
460d24de9dSSimon Glass                'type'; error or warning
470d24de9dSSimon Glass                'msg': text message
480d24de9dSSimon Glass                'file' : filename
490d24de9dSSimon Glass                'line': line number
50d29fe6e2SSimon Glass            errors: Number of errors
51d29fe6e2SSimon Glass            warnings: Number of warnings
52d29fe6e2SSimon Glass            checks: Number of checks
530d24de9dSSimon Glass            lines: Number of lines
54d29fe6e2SSimon Glass            stdout: Full output of checkpatch
550d24de9dSSimon Glass    """
56d29fe6e2SSimon Glass    fields = ['ok', 'problems', 'errors', 'warnings', 'checks', 'lines',
57d29fe6e2SSimon Glass              'stdout']
58d29fe6e2SSimon Glass    result = collections.namedtuple('CheckPatchResult', fields)
59d29fe6e2SSimon Glass    result.ok = False
60d29fe6e2SSimon Glass    result.errors, result.warning, result.checks = 0, 0, 0
61d29fe6e2SSimon Glass    result.lines = 0
62d29fe6e2SSimon Glass    result.problems = []
630d24de9dSSimon Glass    chk = FindCheckPatch()
640d24de9dSSimon Glass    item = {}
65785f1548SSimon Glass    result.stdout = command.Output(chk, '--no-tree', fname,
66785f1548SSimon Glass                                   raise_on_error=False)
670d24de9dSSimon Glass    #pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
680d24de9dSSimon Glass    #stdout, stderr = pipe.communicate()
690d24de9dSSimon Glass
700d24de9dSSimon Glass    # total: 0 errors, 0 warnings, 159 lines checked
71d29fe6e2SSimon Glass    # or:
72d29fe6e2SSimon Glass    # total: 0 errors, 2 warnings, 7 checks, 473 lines checked
730d24de9dSSimon Glass    re_stats = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)')
74d29fe6e2SSimon Glass    re_stats_full = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)'
75d29fe6e2SSimon Glass                               ' checks, (\d+)')
760d24de9dSSimon Glass    re_ok = re.compile('.*has no obvious style problems')
770d24de9dSSimon Glass    re_bad = re.compile('.*has style problems, please review')
780d24de9dSSimon Glass    re_error = re.compile('ERROR: (.*)')
790d24de9dSSimon Glass    re_warning = re.compile('WARNING: (.*)')
80d29fe6e2SSimon Glass    re_check = re.compile('CHECK: (.*)')
810d24de9dSSimon Glass    re_file = re.compile('#\d+: FILE: ([^:]*):(\d+):')
820d24de9dSSimon Glass
83d29fe6e2SSimon Glass    for line in result.stdout.splitlines():
840d24de9dSSimon Glass        if verbose:
85a920a17bSPaul Burton            print(line)
860d24de9dSSimon Glass
870d24de9dSSimon Glass        # A blank line indicates the end of a message
880d24de9dSSimon Glass        if not line and item:
89d29fe6e2SSimon Glass            result.problems.append(item)
900d24de9dSSimon Glass            item = {}
91d29fe6e2SSimon Glass        match = re_stats_full.match(line)
92d29fe6e2SSimon Glass        if not match:
930d24de9dSSimon Glass            match = re_stats.match(line)
940d24de9dSSimon Glass        if match:
95d29fe6e2SSimon Glass            result.errors = int(match.group(1))
96d29fe6e2SSimon Glass            result.warnings = int(match.group(2))
97d29fe6e2SSimon Glass            if len(match.groups()) == 4:
98d29fe6e2SSimon Glass                result.checks = int(match.group(3))
99d29fe6e2SSimon Glass                result.lines = int(match.group(4))
100d29fe6e2SSimon Glass            else:
101d29fe6e2SSimon Glass                result.lines = int(match.group(3))
1020d24de9dSSimon Glass        elif re_ok.match(line):
103d29fe6e2SSimon Glass            result.ok = True
1040d24de9dSSimon Glass        elif re_bad.match(line):
105d29fe6e2SSimon Glass            result.ok = False
106d29fe6e2SSimon Glass        err_match = re_error.match(line)
107d29fe6e2SSimon Glass        warn_match = re_warning.match(line)
108d29fe6e2SSimon Glass        file_match = re_file.match(line)
109d29fe6e2SSimon Glass        check_match = re_check.match(line)
110d29fe6e2SSimon Glass        if err_match:
111d29fe6e2SSimon Glass            item['msg'] = err_match.group(1)
1120d24de9dSSimon Glass            item['type'] = 'error'
113d29fe6e2SSimon Glass        elif warn_match:
114d29fe6e2SSimon Glass            item['msg'] = warn_match.group(1)
1150d24de9dSSimon Glass            item['type'] = 'warning'
116d29fe6e2SSimon Glass        elif check_match:
117d29fe6e2SSimon Glass            item['msg'] = check_match.group(1)
118d29fe6e2SSimon Glass            item['type'] = 'check'
119d29fe6e2SSimon Glass        elif file_match:
120d29fe6e2SSimon Glass            item['file'] = file_match.group(1)
121d29fe6e2SSimon Glass            item['line'] = int(file_match.group(2))
1220d24de9dSSimon Glass
123d29fe6e2SSimon Glass    return result
1240d24de9dSSimon Glass
1250d24de9dSSimon Glassdef GetWarningMsg(col, msg_type, fname, line, msg):
1260d24de9dSSimon Glass    '''Create a message for a given file/line
1270d24de9dSSimon Glass
1280d24de9dSSimon Glass    Args:
1290d24de9dSSimon Glass        msg_type: Message type ('error' or 'warning')
1300d24de9dSSimon Glass        fname: Filename which reports the problem
1310d24de9dSSimon Glass        line: Line number where it was noticed
1320d24de9dSSimon Glass        msg: Message to report
1330d24de9dSSimon Glass    '''
1340d24de9dSSimon Glass    if msg_type == 'warning':
1350d24de9dSSimon Glass        msg_type = col.Color(col.YELLOW, msg_type)
1360d24de9dSSimon Glass    elif msg_type == 'error':
1370d24de9dSSimon Glass        msg_type = col.Color(col.RED, msg_type)
138d29fe6e2SSimon Glass    elif msg_type == 'check':
139d29fe6e2SSimon Glass        msg_type = col.Color(col.MAGENTA, msg_type)
1408aa41363SSimon Glass    return '%s:%d: %s: %s\n' % (fname, line, msg_type, msg)
1410d24de9dSSimon Glass
1420d24de9dSSimon Glassdef CheckPatches(verbose, args):
1430d24de9dSSimon Glass    '''Run the checkpatch.pl script on each patch'''
144d29fe6e2SSimon Glass    error_count, warning_count, check_count = 0, 0, 0
1450d24de9dSSimon Glass    col = terminal.Color()
1460d24de9dSSimon Glass
1470d24de9dSSimon Glass    for fname in args:
148d29fe6e2SSimon Glass        result = CheckPatch(fname, verbose)
149d29fe6e2SSimon Glass        if not result.ok:
150d29fe6e2SSimon Glass            error_count += result.errors
151d29fe6e2SSimon Glass            warning_count += result.warnings
152d29fe6e2SSimon Glass            check_count += result.checks
153a920a17bSPaul Burton            print('%d errors, %d warnings, %d checks for %s:' % (result.errors,
154a920a17bSPaul Burton                    result.warnings, result.checks, col.Color(col.BLUE, fname)))
155d29fe6e2SSimon Glass            if (len(result.problems) != result.errors + result.warnings +
156d29fe6e2SSimon Glass                    result.checks):
157a920a17bSPaul Burton                print("Internal error: some problems lost")
158d29fe6e2SSimon Glass            for item in result.problems:
1598aa41363SSimon Glass                sys.stderr.write(
1608aa41363SSimon Glass                    GetWarningMsg(col, item.get('type', '<unknown>'),
161afb9bf55SSimon Glass                        item.get('file', '<unknown>'),
162a920a17bSPaul Burton                        item.get('line', 0), item.get('msg', 'message')))
163d29fe6e2SSimon Glass            print
164a920a17bSPaul Burton            #print(stdout)
165d29fe6e2SSimon Glass    if error_count or warning_count or check_count:
166d29fe6e2SSimon Glass        str = 'checkpatch.pl found %d error(s), %d warning(s), %d checks(s)'
1670d24de9dSSimon Glass        color = col.GREEN
1680d24de9dSSimon Glass        if warning_count:
1690d24de9dSSimon Glass            color = col.YELLOW
1700d24de9dSSimon Glass        if error_count:
1710d24de9dSSimon Glass            color = col.RED
172a920a17bSPaul Burton        print(col.Color(color, str % (error_count, warning_count, check_count)))
1730d24de9dSSimon Glass        return False
1740d24de9dSSimon Glass    return True
175