summaryrefslogtreecommitdiff
authorXindong Xu <xindong.xu@amlogic.com>2017-07-13 01:29:55 (GMT)
committer Xindong Xu <xindong.xu@amlogic.com>2017-07-13 06:48:21 (GMT)
commitd5b311401c8b2c1ea82e3a3bb74d79cf9819572d (patch)
tree6fbce6a5be52907086c9ca56837fcda889af86e3
parent90f4c2fad4366b942696e8f63d06210cc0bd131a (diff)
downloadscripts-d5b311401c8b2c1ea82e3a3bb74d79cf9819572d.zip
scripts-d5b311401c8b2c1ea82e3a3bb74d79cf9819572d.tar.gz
scripts-d5b311401c8b2c1ea82e3a3bb74d79cf9819572d.tar.bz2
scripts: add for check patch [1/5]
PD# NONE we check the patch before commit it. 1. source files file should not be executable 2. trailing spaces 3. not space tab and tab space tab 4. spacing around parenthesis if|while|for|switch 5. make sure indent style matches rest of file 6. spacing around ==|!=|<=|>=|\|\||&& Change-Id: Ic92958195ca4be91cbef834065266bff68b2b3de
Diffstat
-rwxr-xr-xcheck_patch.py365
-rwxr-xr-xcheckpatch.pl44
2 files changed, 409 insertions, 0 deletions
diff --git a/check_patch.py b/check_patch.py
new file mode 100755
index 0000000..414e2f2
--- a/dev/null
+++ b/check_patch.py
@@ -0,0 +1,365 @@
+#!/usr/bin/env python2
+
+import json
+import logging
+import os.path
+import re
+import pprint
+import sys
+
+__author__ = 'lawrence'
+
+MAX_TRAILING_SPACES_MSGS_PER_FILE = 5
+MAX_MIXED_TABS_MSGS_PER_FILE = 5
+MAX_SPACING_MSGS_PER_FILE = 5
+MAX_INDENT_MSGS_PER_FILE = 1
+MAX_FILES_WITH_MSGS = 10
+
+INDENT_UNKNOWN = 0
+INDENT_SPACES = 1
+INDENT_TABS = 2
+
+reply_msg_extra = ''
+
+class ChangedFile:
+ SOURCE_EXT = ['.c', '.cpp', '.cc', '.h', '.java', '.mk', '.xml']
+ C_JAVA_EXT = ['.c', '.cpp', '.java']
+ TEXT_RESOURCE_EXT = ['.rc', '.prop', '.te', '.kl', '.cfg', '.conf', '.dtd']
+ BINARY_RESOURCE_EXT = ['.txt', '.so', '.ko', '.apk', '.png', '.jpg', '.jpeg', '.gif']
+
+ def __init__(self, filename=None, is_new=False, mode=None):
+ self.filename = filename
+ self.file_ext = None
+ if filename:
+ self.on_update_filename()
+ self.is_new = is_new
+ self.mode = mode
+ self.formattable_carriage_returns = False
+ self.comments = {}
+
+ def on_update_filename(self):
+ if not self.filename:
+ logging.error("couldn't get filename")
+ return
+ self.file_ext = os.path.splitext(self.filename)[1].lower()
+
+ def is_source(self):
+ if self.file_ext in self.SOURCE_EXT:
+ return True
+ if self.filename:
+ b = os.path.basename(self.filename)
+ if (b and (
+ b.startswith("Kconfig") or
+ b == "Makefile") or
+ b.endswith("_defconfig")):
+ return True
+ return False
+
+ def is_binary_resource(self):
+ if self.file_ext in self.BINARY_RESOURCE_EXT:
+ return True
+ return False
+
+ def is_text_resource(self):
+ if self.file_ext in self.TEXT_RESOURCE_EXT:
+ return True
+ return False
+
+ def has_errors(self):
+ if self.comments:
+ return True
+ # same as add_file_comments:
+ if self.mode == 755 and self.should_not_be_executable():
+ return True
+ if self.formattable_carriage_returns and self.should_not_have_carriage_return():
+ return True
+ return False
+
+ def should_check_line_diff(self):
+ if self.is_source() or self.is_text_resource():
+ return True
+ return False
+
+ def should_not_be_executable(self):
+ return self.is_source() or self.is_text_resource() or self.is_binary_resource()
+
+ def should_not_have_carriage_return(self):
+ if self.is_new:
+ if self.is_source() or self.is_text_resource():
+ return True
+ return False
+
+ def should_check_statement_spacing(self):
+ if self.file_ext in self.C_JAVA_EXT:
+ return True
+ return False
+
+ def should_check_indent(self):
+ if self.file_ext in self.C_JAVA_EXT:
+ return True
+ return False
+
+ def add_file_comments(self):
+ if self.mode == 755 and self.should_not_be_executable():
+ self.append_comment(0, "{} file should not be executable".format(self.file_ext))
+ if self.formattable_carriage_returns and self.should_not_have_carriage_return():
+ self.append_comment(0, "{} file should not have carriage returns (DOS line endings)".format(self.file_ext))
+
+ def append_comment(self, line, msg):
+ if line in self.comments:
+ self.comments[line] += "\n\n"
+ self.comments[line] += msg
+ else:
+ self.comments[line] = msg
+
+
+ # types of files/checks
+ # source/resource:
+ # should be non-executable (new/changed source + .ko, etc)
+ # source:
+ # should not have carriage return (new source + text resources)
+ # text resource:
+ # should not have trailing spaces (source + text resources)
+ # should not have mixed spaces/tabs (source + text resources)
+ # source + syntax
+ # should have space in if statements (source c/java)
+ # added line indent should match context
+ # *could be imported code - warn only..?
+
+
+def check(filename):
+ """
+ Checks unified diff.
+ :param filename: diff file to check
+ :return: 0 on patch errors, 1 on no patch errors, < 0 on other errors
+ """
+ if not filename:
+ return -1
+
+ try:
+ with open(filename) as fp:
+ return check_fp(fp)
+ except OSError:
+ logging.error(" failed to open? OSError %s", filename)
+ return -2
+ except IOError:
+ logging.error(" failed to open? IOError %s", filename)
+ return -3
+ return -4
+
+
+# TODO split checks into separate functions
+def check_fp(fp):
+ file_sections = []
+ f = None
+ check_lines = False
+ check_statement_spacing = False
+ trailing_sp_msg_count = 0
+ mixed_tabs_msg_count = 0
+ spacing_msg_count = 0
+ in_line_diff = False
+ section_line_start = 0
+ section_line_start_err = False
+ files_with_msgs = 0
+ cur_line = 0
+ error_num = 0
+ for line in fp:
+ if line.startswith("diff"):
+ if f and f.has_errors():
+ f.add_file_comments()
+ error_num += 1
+ file_sections.append(f)
+ if len(file_sections) >= MAX_FILES_WITH_MSGS:
+ global reply_msg_extra
+ reply_msg_extra += '\n\nStopped code style check at {} files.'.format(MAX_FILES_WITH_MSGS)
+ break;
+ # start of new file
+ f = ChangedFile()
+ check_lines = False
+ trailing_sp_msg_count = 0
+ mixed_tabs_msg_count = 0
+ spacing_msg_count = 0
+ indent_msg_count = 0
+ context_indent = INDENT_UNKNOWN
+ in_line_diff = False
+
+ # get filename
+ # might fail on paths like "dir b/file.txt"
+ m = re.match(r"^diff --git a/(.*) b/.*", line)
+ if m:
+ f.filename = m.group(1)
+ f.on_update_filename()
+ check_lines = f.should_check_line_diff()
+ check_statement_spacing = f.should_check_statement_spacing()
+ check_indent = f.should_check_indent()
+ elif line.startswith("new file mode "):
+ f.is_new = True
+ if line.startswith("100755", len("new file mode ")):
+ f.mode = 755
+ elif line.startswith("new mode 100755"):
+ f.mode = 755
+ elif f and not f.filename and line.startswith("+++ b/"):
+ # get filename if previously failed for some reason
+ f.filename = line[len("+++ b/"):].rstrip('\r\n ')
+ f.on_update_filename()
+ check_lines = f.should_check_line_diff()
+ check_statement_spacing = f.should_check_statement_spacing()
+ check_indent = f.should_check_indent()
+ else:
+ if not check_lines:
+ continue
+ if line.startswith("@@ "):
+ # keep track of line numbers
+ # @@ -584,7 +681,7 @@
+ m = re.match(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)?\ @@", line)
+ try:
+ section_line_start = int(m.group(1))
+ except ValueError:
+ logging.error("failed to parse section line start")
+ section_line_start_err = True
+ in_line_diff = True
+ cur_line = section_line_start - 1 # next line is the start
+ continue
+ if in_line_diff:
+ # keep track of line numbers
+ if line[0] in ' +':
+ cur_line += 1
+ # get last context line's indent
+ if line[0] == " ":
+ if line.startswith(" ", 1):
+ context_indent = INDENT_SPACES
+ elif line.startswith("\t", 1):
+ context_indent = INDENT_TABS
+ if line[0] == '+' and line[1] != '+':
+ if check_lines and not section_line_start_err:
+ if (f.is_new and
+ not f.formattable_carriage_returns and
+ line[-2] == '\r'):
+ f.formattable_carriage_returns = True
+
+ if trailing_sp_msg_count < MAX_TRAILING_SPACES_MSGS_PER_FILE:
+ if (line.endswith(" \n") or
+ line.endswith(" \r\n") or
+ line.endswith("\t\n") or
+ line.endswith("\t\r\n")):
+ f.append_comment(cur_line, "trailing spaces")
+ trailing_sp_msg_count += 1
+ error_num += 1
+
+ if mixed_tabs_msg_count < MAX_MIXED_TABS_MSGS_PER_FILE:
+ if re.match(r" +\t", line[1:]) or re.match(r"\t+ +\t", line[1:]):
+ # tab space can be correct, but not space tab and tab space tab
+ f.append_comment(cur_line, "possibly incorrect mixed spaces then tabs indentation")
+ mixed_tabs_msg_count += 1
+ error_num += 1
+
+ if check_statement_spacing and spacing_msg_count < MAX_SPACING_MSGS_PER_FILE:
+ m = re.match(r"\s*(if|while|for|switch)", line[1:])
+ if (m):
+ # line starts with if|while|for|switch
+ keyword = m.group(1)
+ # check parenthesis/brace spacing. if( -> if ( or ){ -> ) {
+ m = re.match(r"\s*(?:if|while|for|switch)( ?)\(.*\)( ?)(\{?)", line[1:])
+ if (m):
+ keyword_sp, brace_space, brace = m.groups()
+ if keyword_sp != ' ' or (
+ brace == '{' and brace_space != ' '):
+ f.append_comment(cur_line,
+ "%s (...) %s // spacing around parenthesis" % (keyword, brace))
+ spacing_msg_count += 1
+ error_num += 1
+
+ # check binary operator spacing on if|while line
+ # cpplint.py: match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line
+ if keyword in ['if', 'while']:
+ m = re.search(r"[^<>=!\s](==|!=|<=|>=|\|\||&&)[^<>=!\s,;\)]", line[1:])
+ if (m):
+ f.append_comment(cur_line, "spacing around %s" % m.group(1))
+ spacing_msg_count += 1
+ error_num += 1
+ continue
+ # do{ -> do {
+ elif re.match(r"\s*do\{", line[1:]):
+ f.append_comment(cur_line, 'do {')
+ spacing_msg_count += 1
+ error_num += 1
+
+ if check_indent and indent_msg_count < MAX_INDENT_MSGS_PER_FILE:
+ if ((context_indent == INDENT_SPACES and line.startswith("\t", 1)) or
+ (context_indent == INDENT_TABS and line.startswith(" ", 1))):
+ f.append_comment(cur_line, "make sure indent style matches rest of file")
+ indent_msg_count += 1
+ error_num += 1
+
+ if f and f.has_errors():
+ f.add_file_comments()
+ file_sections.append(f)
+ error_num += 1
+
+ if False:
+ for f in file_sections:
+ assert isinstance(f, ChangedFile)
+ if f.comments:
+ print f.filename
+ pprint.pprint(f.comments)
+ print "---"
+ json_ret = file_comments_to_review(file_sections)
+ if json_ret:
+ print json_ret
+ #return 0
+ else:
+ return 1
+
+ #print error_num
+ if error_num > 0:
+ return 1
+ else:
+ return 0
+
+REPLY_MSG = "This is an automated message.\n\nPlease check it & commit again."
+POSITIVE_REPLY_MSG = "This is an automated message.\n\nNo problems found."
+
+def file_comments_to_array(changed_file):
+ """
+ Return a list of comments for a CommentInput entry from a ChangedFile
+ :param changed_file: a ChangedFile object
+ :return: a list of comments for CommentInput
+ """
+ ret = []
+ assert isinstance(changed_file, ChangedFile)
+ for line, msg in changed_file.comments.iteritems():
+ ret.append({"line": line,
+ "message": msg})
+ return ret
+
+def file_comments_to_review(changed_files):
+ """
+ Create a JSON ReviewInput from a list of ChangedFiles
+ :param changed_files: list of ChangedFiles
+ :return: JSON ReviewInput string
+ """
+ review = {}
+ review['comments'] = {}
+ for f in changed_files:
+ if f.filename and f.comments:
+
+ c = file_comments_to_array(f)
+ if not c:
+ logging.error("no comments for file")
+ review['comments'][f.filename] = c
+ if review['comments']:
+ review['message'] = REPLY_MSG + reply_msg_extra
+ else:
+ del review['comments']
+ review['message'] = POSITIVE_REPLY_MSG
+ #return json.dumps(review, indent=2)
+ return json.dumps(review)
+
+if __name__ == '__main__':
+ if len(sys.argv) == 2:
+ sys.stderr.write("%s <patch filename>....\n" % sys.argv[0])
+ r = check(sys.argv[1])
+ sys.exit(r)
+ else:
+ sys.stderr.write("%s <patch filename>\n" % sys.argv[0])
+ sys.exit(0)
diff --git a/checkpatch.pl b/checkpatch.pl
new file mode 100755
index 0000000..a8880bf
--- a/dev/null
+++ b/checkpatch.pl
@@ -0,0 +1,44 @@
+#!/usr/bin/perl -w
+
+use File::Spec;
+my $path_curf = File::Spec->rel2abs(__FILE__);
+my ($vol, $dirs, $file) = File::Spec->splitpath($path_curf);
+print "C Dir = ", $dirs,"\n";
+
+my $name;
+my $dpath;
+$name = `whoami`;
+chomp $name;
+
+use POSIX;
+my $time=strftime("%Y%m%d_%H%M%S",localtime());
+$ddir = "/tmp/".$name;
+$dpath = "$ddir"."/".$time.".diff";
+print "dpath:", $dpath, "\n";
+
+my $FILE;
+my $FILE_O;
+open($FILE, '<&STDIN');
+open ($FILE_O,">",$dpath);
+while (<$FILE>) {
+ chomp;
+ my $line = $_;
+ print $FILE_O "$line";
+ print $FILE_O "\n";
+}
+close $FILE;
+close $FILE_O;
+
+system("python $dirs/check_patch.py $dpath");
+$exitcode = $?;
+system("rm $dpath");
+if($exitcode == 0)
+{
+ exit 0;
+}
+else
+{
+ exit 1;
+}
+
+