1#!/usr/bin/env python3 2# 3# Arm SCP/MCP Software 4# Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved. 5# 6# SPDX-License-Identifier: BSD-3-Clause 7# 8 9""" 10Check whether the files adhere to the prescribed coding style. Validation 11is performed by checkpatch.pl which is found on the environment path or 12via user supplied path. 13""" 14 15import argparse 16import os 17import fnmatch 18import sys 19import subprocess 20 21# 22# Checkpatch.pl location (assume it is available through the environment path 23# by default) 24# 25script_path = 'checkpatch.pl' 26 27# 28# Directories to scan. Only used when --input-mode is set to "project". 29# 30DIRECTORIES = [ 31 'arch', 32 'framework', 33 'module', 34 'product', 35 'tools', 36] 37 38# 39# Supported file types. Only used when --input-mode is set to "project". 40# 41FILE_TYPES = [ 42 '*.c', 43 '*.h', 44] 45 46# 47# Default ignored types. These are rules within checkpatch that conflict with 48# the SCP/MCP Software coding style and so they should never be enabled. 49# 50IGNORED_TYPES = [ 51 'LEADING_SPACE', # Incompatible with spaces for indentation 52 'CODE_INDENT', # Incompatible with spaces for indentation 53 'SUSPECT_CODE_INDENT', # Incompatible with spaces for indentation 54 'POINTER_LOCATION', # Doesn't agree with our function declaration style 55 'BLOCK_COMMENT_STYLE', # Doesn't tolerate asterisks on each block line 56 'AVOID_EXTERNS', # We use the extern keyword 57 'NEW_TYPEDEFS', # We add new typedefs 58 'VOLATILE', # We use volatile 59 'MACRO_WITH_FLOW_CONTROL', # Some 'capture' macros use do/while loops 60 'LINE_SPACING', # We don't require a blank line after declarations 61 'SPLIT_STRING', # We allow strings to be split across lines 62 'FILE_PATH_CHANGES', # Specific to the kernel development process 63 'PREFER_PACKED', # __packed is not available in Arm Compiler 6 64] 65 66error_count = 0 67 68 69def is_valid_file_type(filename): 70 return any([fnmatch.fnmatch(filename, t) for t in FILE_TYPES]) 71 72 73def check_file(checkpatch_params, filename): 74 global error_count 75 76 cmd = '{} {}'.format(checkpatch_params, filename) 77 78 try: 79 subprocess.check_call(cmd, shell=True, stdin=0) 80 except subprocess.CalledProcessError: 81 error_count += 1 82 83 84def main(argv=[], prog_name=''): 85 global script_path 86 print('Arm SCP/MCP Software Checkpatch Wrapper') 87 parser = argparse.ArgumentParser(prog=prog_name) 88 89 input_mode_list = ['stdin', 'project'] 90 91 # Optional parameters 92 parser.add_argument('-s', '--spacing', action='store_true', 93 help='Check for correct use of spaces', 94 required=False) 95 96 parser.add_argument('-l', '--line-length', action='store_true', 97 dest='length', 98 help='Check for lines longer than 80 characters', 99 required=False) 100 101 parser.add_argument('-i', '--initializers', action='store_true', 102 help='Check for redundant variable initialization', 103 required=False) 104 105 parser.add_argument('-m', '--input-mode', choices=input_mode_list, 106 help='Input mode for the content to be checked. ' 107 'Default: %(default)s', 108 required=False, default=input_mode_list[0]) 109 110 parser.add_argument('-p', '--path', action='store', dest='path', 111 help='Path to checkpatch.pl file. If not specified, ' 112 'the script will be found on the environment ' 113 'path.', 114 required=False) 115 116 args = parser.parse_args(argv) 117 118 # Override path to checkpatch.pl if necessary 119 if args.path: 120 script_path = args.path 121 122 # Print the path to checkpatch.pl as confirmation 123 print('checkpatch.pl path:', script_path, '\n') 124 125 # Enable optional tests 126 if not args.spacing: 127 IGNORED_TYPES.extend(['SPACING', 'MISSING_SPACE', 'BRACKET_SPACE']) 128 129 if not args.length: 130 IGNORED_TYPES.extend(['LONG_LINE', 'LONG_LINE_COMMENT', 131 'LONG_LINE_STRING']) 132 if not args.initializers: 133 IGNORED_TYPES.extend(['GLOBAL_INITIALISERS', 'INITIALISED_STATIC']) 134 135 ignore_list = '--ignore ' + (','.join(map(str, IGNORED_TYPES))) 136 137 checkpatch_params = '{} --show-types --no-tree --no-summary {}'.format( 138 script_path, 139 ignore_list, 140 ) 141 142 if args.input_mode == 'project': 143 print("Checking the coding style of the whole project...") 144 checkpatch_params += ' --terse --file' 145 for directory in DIRECTORIES: 146 for root, dirs, files in os.walk(directory): 147 for file in files: 148 filename = os.path.join(root, file) 149 if is_valid_file_type(file): 150 check_file(checkpatch_params, filename) 151 if error_count > 0: 152 print('{} files contained coding style errors.'. 153 format(error_count)) 154 155 elif args.input_mode == 'stdin': 156 print("Checking content via standard input...") 157 check_file(checkpatch_params, '-') 158 159 else: 160 print('FAILED: Invalid input mode') 161 return 1 162 163 if error_count > 0: 164 print('FAILED: One or more files contained coding style errors.') 165 return 1 166 167 print('PASSED: No files contained coding style errors.') 168 return 0 169 170 171if __name__ == '__main__': 172 sys.exit(main(sys.argv[1:], sys.argv[0])) 173