1#!/usr/bin/env python3 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright(c) 2021 Intel Corporation 4 5''' 6A Python script to run some checks on meson.build files in DPDK 7''' 8 9import sys 10import os 11import re 12from os.path import relpath, join 13from argparse import ArgumentParser 14 15VERBOSE = False 16 17 18def scan_dir(path): 19 '''return meson.build files found in path''' 20 for root, dirs, files in os.walk(path): 21 if 'meson.build' in files: 22 yield(relpath(join(root, 'meson.build'))) 23 24 25def split_code_comments(line): 26 'splits a line into a code part and a comment part, returns (code, comment) tuple' 27 if line.lstrip().startswith('#'): 28 return ('', line) 29 elif '#' in line and '#include' not in line: # catch 99% of cases, not 100% 30 idx = line.index('#') 31 while (line[idx - 1].isspace()): 32 idx -= 1 33 return line[:idx], line[idx:] 34 else: 35 return (line, '') 36 37 38def setline(contents, index, value): 39 'sets the contents[index] to value. Returns the line, along with code and comments part' 40 line = contents[index] = value 41 code, comments = split_code_comments(line) 42 return line, code, comments 43 44 45def check_indentation(filename, contents): 46 '''check that a list or files() is correctly indented''' 47 infiles = False 48 inlist = False 49 edit_count = 0 50 for lineno, line in enumerate(contents): 51 code, comments = split_code_comments(line) 52 if not code.strip(): 53 continue 54 if re.match('^ *\t', code): 55 print(f'Error parsing {filename}:{lineno}, got some tabulation') 56 if code.endswith('files('): 57 if infiles: 58 raise(f'Error parsing {filename}:{lineno}, got "files(" when already parsing files list') 59 if inlist: 60 print(f'Error parsing {filename}:{lineno}, got "files(" when already parsing array list') 61 infiles = True 62 indent_count = len(code) - len(code.lstrip(' ')) 63 indent = ' ' * (indent_count + 8) # double indent required 64 elif code.endswith('= ['): 65 if infiles: 66 raise(f'Error parsing {filename}:{lineno}, got start of array when already parsing files list') 67 if inlist: 68 print(f'Error parsing {filename}:{lineno}, got start of array when already parsing array list') 69 inlist = True 70 indent_count = len(code) - len(code.lstrip(' ')) 71 indent = ' ' * (indent_count + 8) # double indent required 72 elif infiles and (code.endswith(')') or code.strip().startswith(')')): 73 infiles = False 74 continue 75 elif inlist and (code.endswith(']') or code.strip().startswith(']')): 76 inlist = False 77 continue 78 elif inlist or infiles: 79 # skip further subarrays or lists 80 if '[' in code or ']' in code: 81 continue 82 if not code.startswith(indent) or code[len(indent)] == ' ': 83 print(f'Error: Incorrect indent at {filename}:{lineno + 1}') 84 line, code, comments = setline(contents, lineno, indent + line.strip()) 85 edit_count += 1 86 if not code.endswith(','): 87 print(f'Error: Missing trailing "," in list at {filename}:{lineno + 1}') 88 line, code, comments = setline(contents, lineno, code + ',' + comments) 89 edit_count += 1 90 if len(code.split(',')) > 2: # only one comma per line 91 print(f'Error: multiple entries per line in list at {filename}:{lineno +1}') 92 entries = [e.strip() for e in code.split(',') if e.strip()] 93 line, code, comments = setline(contents, lineno, 94 indent + (',\n' + indent).join(entries) + 95 ',' + comments) 96 edit_count += 1 97 return edit_count 98 99 100def process_file(filename, fix): 101 '''run checks on file "filename"''' 102 if VERBOSE: 103 print(f'Processing {filename}') 104 with open(filename) as f: 105 contents = [ln.rstrip() for ln in f.readlines()] 106 107 if check_indentation(filename, contents) > 0 and fix: 108 print(f"Fixing {filename}") 109 with open(filename, 'w') as f: 110 f.writelines([f'{ln}\n' for ln in contents]) 111 112 113def main(): 114 '''parse arguments and then call other functions to do work''' 115 global VERBOSE 116 parser = ArgumentParser(description='Run syntax checks on DPDK meson.build files') 117 parser.add_argument('-d', metavar='directory', default='.', help='Directory to process') 118 parser.add_argument('--fix', action='store_true', help='Attempt to fix errors') 119 parser.add_argument('-v', action='store_true', help='Verbose output') 120 args = parser.parse_args() 121 122 VERBOSE = args.v 123 for f in scan_dir(args.d): 124 process_file(f, args.fix) 125 126 127if __name__ == "__main__": 128 main() 129