diff options
author | Xiaobo Gu <xiaobo.gu@amlogic.com> | 2015-05-06 15:03:58 +0800 |
---|---|---|
committer | Xiaobo Gu <xiaobo.gu@amlogic.com> | 2015-05-06 15:07:11 +0800 |
commit | c7d677434f1ea3b9f3df18efcfa50f50453f3900 (patch) | |
tree | acf34b07aaa8ed502fa558907226fe632ec5867e /scripts | |
parent | 8b02a71b75020003df8c8d3ce9ee11b37c0be6a0 (diff) | |
download | u-boot-odroid-c1-c7d677434f1ea3b9f3df18efcfa50f50453f3900.tar.gz |
Add coding style check & fix tool
Change-Id: If37cf1f688e4e99c6b74ad01e98d470e5cf135f3
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/amlogic/coding_style/auto_fix.py | 233 | ||||
-rw-r--r-- | scripts/amlogic/coding_style/checkpatch.py | 346 | ||||
-rw-r--r-- | scripts/amlogic/coding_style/fix_code.sh | 32 |
3 files changed, 611 insertions, 0 deletions
diff --git a/scripts/amlogic/coding_style/auto_fix.py b/scripts/amlogic/coding_style/auto_fix.py new file mode 100644 index 0000000000..5fe68977f0 --- /dev/null +++ b/scripts/amlogic/coding_style/auto_fix.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Amlogic gerrit code auto-fix script +# Author: xiaobo.gu@amlogic.com +# Init version: 2015.05.01 + +import sys, os +import json + +MESSAGE_INFO_1 = "trailing spaces" +MESSAGE_INFO_2 = "spacing around parenthesis" +MESSAGE_INFO_3 = "spacing around ==" +MESSAGE_INFO_4 = "spacing around !=" +MESSAGE_INFO_5 = "do {" +MESSAGE_INFO_6 = "file should not be executable" +MESSAGE_INFO_7 = "possibly incorrect mixed spaces then tabs indentation" +MESSAGE_INFO_8 = "file should not have carriage returns" +MESSAGE_INFO_9 = "make sure indent style matches rest of file" +MESSAGE_INFO_10 = "spacing around &&" + +class fixer(object): + def __init__(self, filename): + self.filename = filename + self.info_review = None + self.info_comment = None + self.info_labels = None + self.cur_file_name = "" + self.cur_file_lines = 0 + self.cur_file_content = "" + self.cur_line_content = "" + self.cur_line = 0 + self.cur_message = "" + self.verified = -1 + + def read_log(self): + self.file_handle = open(self.filename) + self.info_str = self.file_handle.read() + self.file_handle.close() + self.info_review = json.loads(self.info_str) + self.info_comment = self.info_review.get("comments") + self.info_labels = self.info_review.get("labels") + + def fix(self): + self.verified = self.info_labels.get("Verified") + if(1 == self.verified): + print "Verified +1, Quit..." + return + for file_info in self.info_comment: + print file_info + #print self.cur_file_content + #for line in self.cur_file_content: + # print line, + # start fix for each warning line + for message_info in self.info_comment[file_info]: + self.cur_file_name = file_info + self.cur_message = message_info.get("message") + self.cur_line = (int)(message_info.get("line")) - 1 # index-1 + + if (self.cur_line >= 0): + # <0 always means line0 error, + # that means file format error etc.. + # it's no need open the file + cur_file = open(file_info) # open current file + self.cur_file_content = cur_file.readlines() # get all content of current file, and split info lines + cur_file.close() # close current file + self.cur_file_lines = len(self.cur_file_content) + self.cur_line_content = str(self.cur_file_content[self.cur_line]) + self.message_handler() + if (self.cur_line >= 0): + # <0 always means line0 error, + # that means file format error etc.. + # it's no need write back current line + self.cur_file_content[self.cur_line] = self.cur_line_content + cur_file = open(file_info, 'w') # open current file + cur_file.writelines(self.cur_file_content) # save file + cur_file.close() # close current file + + def message_handler(self): + if (self.cur_message.find(MESSAGE_INFO_1) >= 0): + self.message_1() + if (self.cur_message.find(MESSAGE_INFO_2) >= 0): + self.message_2() + if (self.cur_message.find(MESSAGE_INFO_3) >= 0): + self.message_3() + if (self.cur_message.find(MESSAGE_INFO_4) >= 0): + self.message_4() + if (self.cur_message.find(MESSAGE_INFO_5) >= 0): + self.message_5() + if (self.cur_message.find(MESSAGE_INFO_6) >= 0): + self.message_6() + if (self.cur_message.find(MESSAGE_INFO_7) >= 0): + self.message_7() + if (self.cur_message.find(MESSAGE_INFO_8) >= 0): + self.message_8() + if (self.cur_message.find(MESSAGE_INFO_9) >= 0): + self.message_9() + if (self.cur_message.find(MESSAGE_INFO_10) >= 0): + self.message_10() + + def message_1(self): + # acknowledge bug: can not fix last line with last blank character + '''MESSAGE_INFO_1 start''' + #print " -", self.cur_line, self.cur_line_content, + cur_line_length = len(self.cur_line_content) + #print "cur_line_length", cur_line_length + #print self.cur_line_content + for cur_length in range(cur_line_length-1): + #print cur_length + cur_char_pos = cur_line_length-2-cur_length + #print cur_char_pos + #print self.cur_line_content[cur_char_pos] + #print self.cur_line_content + #print self.cur_line_content[0:cur_char_pos], "test" + if (self.cur_line_content[cur_char_pos] == ' ') or \ + (self.cur_line_content[cur_char_pos] == ' ') : + self.cur_line_content = self.cur_line_content[0:cur_char_pos] + '\n' + #print self.cur_line_content + else: + break + '''MESSAGE_INFO_1 end''' + + def message_2(self): + '''MESSAGE_INFO_2 start''' + cur_line_length = len(self.cur_line_content) + # search parenthesis from left + pare_pos_left = self.cur_line_content.find('(') + #print "self.cur_line_content[pare_pos_left]:", self.cur_line_content[pare_pos_left-1] + #print self.cur_line_content + # insert blank char if doesn't have one + if (pare_pos_left > 0) and (' ' != self.cur_line_content[pare_pos_left-1]): + self.cur_line_content = self.cur_line_content[0:pare_pos_left] + ' ' + self.cur_line_content[pare_pos_left:cur_line_length] + #print self.cur_line_content + # re-calculate cur line length, maybe previous operations changed it's content + cur_line_length = len(self.cur_line_content) + # search parenthesis from right + pare_pos_right = self.cur_line_content.rfind(')') + if ((pare_pos_right+1) <= cur_line_length): + #print "self.cur_line_content[pare_pos_right]:", self.cur_line_content[pare_pos_right+1] + #print self.cur_line_content + if (pare_pos_right > 0) and (' ' != self.cur_line_content[pare_pos_right+1]) and \ + ('\n' != self.cur_line_content[pare_pos_right+1]): + self.cur_line_content = self.cur_line_content[0:pare_pos_right+1] + ' ' + self.cur_line_content[pare_pos_right+1:cur_line_length] + #print self.cur_line_content + '''MESSAGE_INFO_2 end''' + + def message_3(self): + self.message_space_around("==") + + def message_4(self): + self.message_space_around("!=") + + def message_5(self): + cur_line_length = len(self.cur_line_content) + msg_pos = self.cur_line_content.find("do") + #print "self.cur_line_content[msg_pos+2]", self.cur_line_content[msg_pos+2] + if (' ' != self.cur_line_content[msg_pos+2]): + self.cur_line_content = self.cur_line_content[0:msg_pos+2] + ' ' + self.cur_line_content[msg_pos+2:cur_line_length] + + def message_6(self): + shell_cmd = "chmod -x " + self.cur_file_name + os.system(shell_cmd) + + def message_7(self): + cur_line_length = len(self.cur_line_content) + cur_line_first_noblank_pos = 0 + # find out first non-blank(' '&' ') char + for cur_char_pos in range(cur_line_length): + if (' ' != self.cur_line_content[cur_char_pos]) and \ + (' ' != self.cur_line_content[cur_char_pos]): + cur_line_first_noblank_pos = cur_char_pos + break + #print self.cur_line_content + # replace these 4' 's with tab, 1,2,3 blanks will be deleted + no_blank_str = self.cur_line_content[0:cur_line_first_noblank_pos] + no_blank_str = no_blank_str.replace(" ", " ") + no_blank_str = no_blank_str.replace(" ", "") + no_blank_str = no_blank_str.replace(" ", "") + no_blank_str = no_blank_str.replace(" ", "") + self.cur_line_content = no_blank_str + self.cur_line_content[cur_line_first_noblank_pos:cur_line_length] + + def message_8(self): + shell_cmd = "dos2unix -o -f " + self.cur_file_name + os.system(shell_cmd) + + def message_9(self): + cur_line_length = len(self.cur_line_content) + cur_line_first_noblank_pos = 0 + # find out first non-blank(' '&' ') char + for cur_char_pos in range(cur_line_length): + if (' ' != self.cur_line_content[cur_char_pos]) and \ + (' ' != self.cur_line_content[cur_char_pos]): + cur_line_first_noblank_pos = cur_char_pos + break + no_blank_str = self.cur_line_content[0:cur_line_first_noblank_pos] + no_blank_str = no_blank_str.replace(" ", " ") + #print self.cur_line_content + self.cur_line_content = no_blank_str + self.cur_line_content[cur_line_first_noblank_pos:cur_line_length] + #print self.cur_line_content + + def message_10(self): + self.message_space_around("&&") + + def message_space_around(self, symbol): + replace_symbol = [] + replace_symbol.append(' ' + symbol + ' ') + replace_symbol.append(' ' + symbol) + replace_symbol.append(symbol + ' ') + #print self.cur_line_content + for rep in range(len(replace_symbol)): + self.cur_line_content = self.cur_line_content.replace(replace_symbol[rep], symbol) + self.cur_line_content = self.cur_line_content.replace(symbol, replace_symbol[0]) + #print self.cur_line_content + + def printf(self): + #print "comment: ", self.info_comment + #print "labels: ", self.info_labels + for file_info in self.info_comment: + print file_info + for message_info in self.info_comment[file_info]: + print " ", message_info + + def run(self): + self.read_log() + #self.printf() + self.fix() + +if __name__=='__main__': + if len(sys.argv) != 2: + print 'auto_fix.py [review_log_file]' + exit(1) + fixer = fixer(sys.argv[1]) + fixer.run() diff --git a/scripts/amlogic/coding_style/checkpatch.py b/scripts/amlogic/coding_style/checkpatch.py new file mode 100644 index 0000000000..15e94abe31 --- /dev/null +++ b/scripts/amlogic/coding_style/checkpatch.py @@ -0,0 +1,346 @@ +#!/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 = 1000 +MAX_MIXED_TABS_MSGS_PER_FILE = 1000 +MAX_SPACING_MSGS_PER_FILE = 1000 +MAX_INDENT_MSGS_PER_FILE = 1000 + +INDENT_UNKNOWN = 0 +INDENT_SPACES = 1 +INDENT_TABS = 2 + +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 + return True # return true directly, doesn't check file type + if self.filename: + b = os.path.basename(self.filename) + if (b and ( + b.startswith("Kconfig") or + b == "Makefile")): + 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 + cur_line = 0 + for line in fp: + if line.startswith("diff"): + if f and f.has_errors(): + f.add_file_comments() + file_sections.append(f) + # 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 + + 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 + + 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 + + # 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 + continue + # do{ -> do { + elif re.match(r"\s*do\{", line[1:]): + f.append_comment(cur_line, 'do {') + spacing_msg_count += 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 + + if f and f.has_errors(): + f.add_file_comments() + file_sections.append(f) + + 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 + +REPLY_MSG = "This is an automated message.\n\nIf you think these comments are incorrect, they can be ignored." +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 + review['labels'] = {'Verified': -1} + review['notify'] = 'OWNER' + else: + del review['comments'] + review['message'] = POSITIVE_REPLY_MSG + review['labels'] = {'Verified': +1} + review['notify'] = 'OWNER' + #return json.dumps(review, indent=2) + return json.dumps(review) + +if __name__ == '__main__': + if len(sys.argv) == 2: + 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/scripts/amlogic/coding_style/fix_code.sh b/scripts/amlogic/coding_style/fix_code.sh new file mode 100644 index 0000000000..b34f0a9b9c --- /dev/null +++ b/scripts/amlogic/coding_style/fix_code.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Amlogic gerrit code auto-fix script +# Author: xiaobo.gu@amlogic.com +# Init version: 2015.05.01 + +# get current dir +CUR_P=$(dirname $0) +CUR_P=${CUR_P/\./$(pwd)} +#echo $CUR_P + +# prepare variables +REVIEW_OUTPUT=$CUR_P/review.txt +PATCH_FILE_NAME=$CUR_P/patch_name +#CUR_P=`abspath $pwd` +#echo $CUR_P + +# get latest patch +git format-patch -s -1 -o $CUR_P > $PATCH_FILE_NAME +PATCH_NAME=`cat $PATCH_FILE_NAME` +#echo $PATCH_NAME + +# check patch and generate review summary +python $CUR_P/checkpatch.py $PATCH_NAME > $REVIEW_OUTPUT + +# fix files by review summary +python $CUR_P/auto_fix.py $REVIEW_OUTPUT + +# cleanup +rm $PATCH_FILE_NAME +rm $REVIEW_OUTPUT +rm $PATCH_NAME
\ No newline at end of file |