Merge https://gitlab.denx.de/u-boot/custodians/u-boot-spi into next
[platform/kernel/u-boot.git] / tools / patman / checkpatch.py
index 83aaf71..98c63af 100644 (file)
@@ -1,31 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0+
 # Copyright (c) 2011 The Chromium OS Authors.
 #
-# See file CREDITS for list of people who contributed to this
-# project.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-# MA 02111-1307 USA
-#
 
 import collections
-import command
-import gitutil
 import os
 import re
 import sys
-import terminal
+
+from patman import command
+from patman import gitutil
+from patman import terminal
+from patman import tools
 
 def FindCheckPatch():
     top_level = gitutil.GetTopLevel()
@@ -50,9 +35,8 @@ def FindCheckPatch():
             return fname
         path = os.path.dirname(path)
 
-    print >> sys.stderr, ('Cannot find checkpatch.pl - please put it in your ' +
-                '~/bin directory or use --no-check')
-    sys.exit(1)
+    sys.exit('Cannot find checkpatch.pl - please put it in your ' +
+             '~/bin directory or use --no-check')
 
 def CheckPatch(fname, verbose=False):
     """Run checkpatch.pl on a file.
@@ -75,36 +59,52 @@ def CheckPatch(fname, verbose=False):
               'stdout']
     result = collections.namedtuple('CheckPatchResult', fields)
     result.ok = False
-    result.errors, result.warning, result.checks = 0, 0, 0
+    result.errors, result.warnings, result.checks = 0, 0, 0
     result.lines = 0
     result.problems = []
     chk = FindCheckPatch()
     item = {}
-    result.stdout = command.Output(chk, '--no-tree', fname)
+    result.stdout = command.Output(chk, '--no-tree', fname,
+                                   raise_on_error=False)
     #pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
     #stdout, stderr = pipe.communicate()
 
     # total: 0 errors, 0 warnings, 159 lines checked
     # or:
     # total: 0 errors, 2 warnings, 7 checks, 473 lines checked
-    re_stats = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)')
-    re_stats_full = re.compile('total: (\\d+) errors, (\d+) warnings, (\d+)'
+    emacs_prefix = '(?:[0-9]{4}.*\.patch:[0-9]+: )?'
+    emacs_stats = '(?:[0-9]{4}.*\.patch )?'
+    re_stats = re.compile(emacs_stats +
+                          'total: (\\d+) errors, (\d+) warnings, (\d+)')
+    re_stats_full = re.compile(emacs_stats +
+                               'total: (\\d+) errors, (\d+) warnings, (\d+)'
                                ' checks, (\d+)')
     re_ok = re.compile('.*has no obvious style problems')
     re_bad = re.compile('.*has style problems, please review')
     re_error = re.compile('ERROR: (.*)')
-    re_warning = re.compile('WARNING: (.*)')
+    re_warning = re.compile(emacs_prefix + 'WARNING:(?:[A-Z_]+:)? (.*)')
     re_check = re.compile('CHECK: (.*)')
     re_file = re.compile('#\d+: FILE: ([^:]*):(\d+):')
-
+    re_note = re.compile('NOTE: (.*)')
+    indent = ' ' * 6
     for line in result.stdout.splitlines():
         if verbose:
-            print line
+            print(line)
 
         # A blank line indicates the end of a message
-        if not line and item:
-            result.problems.append(item)
-            item = {}
+        if not line:
+            if item:
+                result.problems.append(item)
+                item = {}
+            continue
+        if re_note.match(line):
+            continue
+        # Skip lines which quote code
+        if line.startswith(indent):
+            continue
+        # Skip code quotes and #<n>
+        if line.startswith('+') or line.startswith('#'):
+            continue
         match = re_stats_full.match(line)
         if not match:
             match = re_stats.match(line)
@@ -116,14 +116,18 @@ def CheckPatch(fname, verbose=False):
                 result.lines = int(match.group(4))
             else:
                 result.lines = int(match.group(3))
+            continue
         elif re_ok.match(line):
             result.ok = True
+            continue
         elif re_bad.match(line):
             result.ok = False
+            continue
         err_match = re_error.match(line)
         warn_match = re_warning.match(line)
         file_match = re_file.match(line)
         check_match = re_check.match(line)
+        subject_match = line.startswith('Subject:')
         if err_match:
             item['msg'] = err_match.group(1)
             item['type'] = 'error'
@@ -136,6 +140,11 @@ def CheckPatch(fname, verbose=False):
         elif file_match:
             item['file'] = file_match.group(1)
             item['line'] = int(file_match.group(2))
+        elif subject_match:
+            item['file'] = '<patch subject>'
+            item['line'] = None
+        else:
+            print('bad line "%s", %d' % (line, len(line)))
 
     return result
 
@@ -154,7 +163,8 @@ def GetWarningMsg(col, msg_type, fname, line, msg):
         msg_type = col.Color(col.RED, msg_type)
     elif msg_type == 'check':
         msg_type = col.Color(col.MAGENTA, msg_type)
-    return '%s: %s,%d: %s' % (msg_type, fname, line, msg)
+    line_str = '' if line is None else '%d' % line
+    return '%s:%s: %s: %s\n' % (fname, line_str, msg_type, msg)
 
 def CheckPatches(verbose, args):
     '''Run the checkpatch.pl script on each patch'''
@@ -167,17 +177,18 @@ def CheckPatches(verbose, args):
             error_count += result.errors
             warning_count += result.warnings
             check_count += result.checks
-            print '%d errors, %d warnings, %d checks for %s:' % (result.errors,
-                    result.warnings, result.checks, col.Color(col.BLUE, fname))
+            print('%d errors, %d warnings, %d checks for %s:' % (result.errors,
+                    result.warnings, result.checks, col.Color(col.BLUE, fname)))
             if (len(result.problems) != result.errors + result.warnings +
                     result.checks):
-                print "Internal error: some problems lost"
+                print("Internal error: some problems lost")
             for item in result.problems:
-                print GetWarningMsg(col, item.get('type', '<unknown>'),
+                sys.stderr.write(
+                    GetWarningMsg(col, item.get('type', '<unknown>'),
                         item.get('file', '<unknown>'),
-                        item.get('line', 0), item.get('msg', 'message'))
+                        item.get('line', 0), item.get('msg', 'message')))
             print
-            #print stdout
+            #print(stdout)
     if error_count or warning_count or check_count:
         str = 'checkpatch.pl found %d error(s), %d warning(s), %d checks(s)'
         color = col.GREEN
@@ -185,6 +196,6 @@ def CheckPatches(verbose, args):
             color = col.YELLOW
         if error_count:
             color = col.RED
-        print col.Color(color, str % (error_count, warning_count, check_count))
+        print(col.Color(color, str % (error_count, warning_count, check_count)))
         return False
     return True