1#!/usr/bin/env python3 2# 3# Arm SCP/MCP Software 4# Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. 5# 6# SPDX-License-Identifier: BSD-3-Clause 7# 8""" 9 Check for tabs in the source code. 10""" 11import argparse 12import os 13import re 14import shutil 15import subprocess 16import sys 17import tempfile 18import fnmatch 19import glob 20 21# 22# Directories to exclude 23# 24 25# Exclude all mod_test "mocks" directories 26UNIT_TEST_MOCKS = glob.glob('module/*/test/**/mocks', recursive=True) 27 28EXCLUDE_DIRECTORIES = [ 29 '.git', 30 'build', 31 'contrib/cmsis/git', 32 "contrib/run-clang-format/git", 33 "contrib/cmock/git", 34 'product/rcar/src/CMSIS-FreeRTOS', 35 'unit_test/unity_mocks', 36] + UNIT_TEST_MOCKS 37 38# 39# Exclude patterns (applied to files only) 40# 41EXCLUDE = [ 42 "Makefile", 43 "*.mk", 44 "*.html", 45 "*.xml", 46 "*.css", 47 "*.gif", 48 "*.dat", 49 "*.swp", 50 "*.pyc", 51 ".gitmodules", 52 "*.svg", 53 "*.a", 54 "*.pdf", 55 "Makefile.*" 56] 57 58 59def convert(path): 60 print("\tConverting all tabs in %s into spaces..." % path) 61 try: 62 file, temp_file = tempfile.mkstemp(prefix='tabs_to_spaces_') 63 print("Using %s" % temp_file) 64 subprocess.check_call('expand -t4 %s > %s' % (path, temp_file), 65 shell=True) 66 shutil.copyfile(temp_file, path) 67 except Exception as e: 68 print("Error: Failed to convert file %s with %s" % (path, e)) 69 sys.exit(1) 70 finally: 71 if os.path.exists(temp_file): 72 os.remove(temp_file) 73 74 75def main(argv=[], prog_name=''): 76 parser = argparse.ArgumentParser(prog=prog_name) 77 parser.add_argument('-c', '--convert', 78 help='Convert tabs to 4 spaces.', 79 action='store_true', 80 default=False) 81 args = parser.parse_args(argv) 82 83 print('Checking the presence of tabs in the code...') 84 if args.convert: 85 print("Conversion mode is enabled.") 86 87 tabs_found_count = 0 88 89 # Get the files ignored by Git 90 # (This is better than 'git check-ignore' because it includes the files 91 # excluded by .git/info/exclude) 92 git_clean_output = subprocess.check_output("git clean -ndX".split()) 93 git_clean_output = git_clean_output.decode() 94 git_ignores = [line.split()[-1] for line in git_clean_output.splitlines()] 95 96 cwd = os.getcwd() 97 print("Executing from %s" % cwd) 98 99 for i, directory in enumerate(EXCLUDE_DIRECTORIES): 100 EXCLUDE_DIRECTORIES[i] = os.path.abspath(directory) 101 print("\tAdding to the exclude list: %s" % EXCLUDE_DIRECTORIES[i]) 102 103 for root, dirs, files in os.walk(cwd, topdown=True): 104 # 105 # Exclude directories based on the EXCLUDE_DIRECTORIES pattern list 106 # 107 dirs[:] = [d for d in dirs 108 if os.path.join(root, d) not in EXCLUDE_DIRECTORIES] 109 110 # 111 # Exclude files based on the EXCLUDE pattern list and the files 112 # Git ignores. 113 # 114 matches = list() 115 116 files = [f for f in files 117 if os.path.join(root, f) not in git_ignores] 118 119 for filename in files: 120 for file_pattern in (EXCLUDE + git_ignores): 121 if fnmatch.fnmatch(filename, file_pattern): 122 matches.append(filename) 123 break 124 for match in matches: 125 files.remove(match) 126 127 # 128 # Check files 129 # 130 for filename in files: 131 path = os.path.join(root, filename) 132 print("processing %s" % filename) 133 with open(path, encoding="utf-8") as file: 134 for line, string in enumerate(file): 135 if '\t' in string: 136 print('%d:%s has tab' % (line, path)) 137 tabs_found_count += 1 138 if args.convert: 139 convert(path) 140 break 141 142 if tabs_found_count == 0: 143 print("No tabs found") 144 return 0 145 else: 146 print('%d tab(s) found.' % tabs_found_count) 147 return 1 148 149 150if __name__ == '__main__': 151 sys.exit(main(sys.argv[1:], sys.argv[0])) 152