11ba12b75SBruce Richardson#!/usr/bin/env python3 21ba12b75SBruce Richardson# SPDX-License-Identifier: BSD-3-Clause 31ba12b75SBruce Richardson# Copyright(c) 2021 Intel Corporation 41ba12b75SBruce Richardson 51ba12b75SBruce Richardson''' 61ba12b75SBruce RichardsonA Python script to run some checks on meson.build files in DPDK 71ba12b75SBruce Richardson''' 81ba12b75SBruce Richardson 91ba12b75SBruce Richardsonimport sys 101ba12b75SBruce Richardsonimport os 11*f88b0b89SDavid Marchandimport re 121ba12b75SBruce Richardsonfrom os.path import relpath, join 131ba12b75SBruce Richardsonfrom argparse import ArgumentParser 141ba12b75SBruce Richardson 151ba12b75SBruce RichardsonVERBOSE = False 161ba12b75SBruce Richardson 171ba12b75SBruce Richardson 181ba12b75SBruce Richardsondef scan_dir(path): 191ba12b75SBruce Richardson '''return meson.build files found in path''' 201ba12b75SBruce Richardson for root, dirs, files in os.walk(path): 211ba12b75SBruce Richardson if 'meson.build' in files: 221ba12b75SBruce Richardson yield(relpath(join(root, 'meson.build'))) 231ba12b75SBruce Richardson 241ba12b75SBruce Richardson 251ba12b75SBruce Richardsondef split_code_comments(line): 261ba12b75SBruce Richardson 'splits a line into a code part and a comment part, returns (code, comment) tuple' 271ba12b75SBruce Richardson if line.lstrip().startswith('#'): 281ba12b75SBruce Richardson return ('', line) 291ba12b75SBruce Richardson elif '#' in line and '#include' not in line: # catch 99% of cases, not 100% 301ba12b75SBruce Richardson idx = line.index('#') 311ba12b75SBruce Richardson while (line[idx - 1].isspace()): 321ba12b75SBruce Richardson idx -= 1 331ba12b75SBruce Richardson return line[:idx], line[idx:] 341ba12b75SBruce Richardson else: 351ba12b75SBruce Richardson return (line, '') 361ba12b75SBruce Richardson 371ba12b75SBruce Richardson 381ba12b75SBruce Richardsondef setline(contents, index, value): 391ba12b75SBruce Richardson 'sets the contents[index] to value. Returns the line, along with code and comments part' 401ba12b75SBruce Richardson line = contents[index] = value 411ba12b75SBruce Richardson code, comments = split_code_comments(line) 421ba12b75SBruce Richardson return line, code, comments 431ba12b75SBruce Richardson 441ba12b75SBruce Richardson 451ba12b75SBruce Richardsondef check_indentation(filename, contents): 461ba12b75SBruce Richardson '''check that a list or files() is correctly indented''' 471ba12b75SBruce Richardson infiles = False 481ba12b75SBruce Richardson inlist = False 491ba12b75SBruce Richardson edit_count = 0 501ba12b75SBruce Richardson for lineno, line in enumerate(contents): 511ba12b75SBruce Richardson code, comments = split_code_comments(line) 521ba12b75SBruce Richardson if not code.strip(): 531ba12b75SBruce Richardson continue 54*f88b0b89SDavid Marchand if re.match('^ *\t', code): 55*f88b0b89SDavid Marchand print(f'Error parsing {filename}:{lineno}, got some tabulation') 561ba12b75SBruce Richardson if code.endswith('files('): 571ba12b75SBruce Richardson if infiles: 581ba12b75SBruce Richardson raise(f'Error parsing {filename}:{lineno}, got "files(" when already parsing files list') 591ba12b75SBruce Richardson if inlist: 601ba12b75SBruce Richardson print(f'Error parsing {filename}:{lineno}, got "files(" when already parsing array list') 611ba12b75SBruce Richardson infiles = True 621ba12b75SBruce Richardson indent_count = len(code) - len(code.lstrip(' ')) 631ba12b75SBruce Richardson indent = ' ' * (indent_count + 8) # double indent required 641ba12b75SBruce Richardson elif code.endswith('= ['): 651ba12b75SBruce Richardson if infiles: 661ba12b75SBruce Richardson raise(f'Error parsing {filename}:{lineno}, got start of array when already parsing files list') 671ba12b75SBruce Richardson if inlist: 681ba12b75SBruce Richardson print(f'Error parsing {filename}:{lineno}, got start of array when already parsing array list') 691ba12b75SBruce Richardson inlist = True 701ba12b75SBruce Richardson indent_count = len(code) - len(code.lstrip(' ')) 711ba12b75SBruce Richardson indent = ' ' * (indent_count + 8) # double indent required 721ba12b75SBruce Richardson elif infiles and (code.endswith(')') or code.strip().startswith(')')): 731ba12b75SBruce Richardson infiles = False 741ba12b75SBruce Richardson continue 751ba12b75SBruce Richardson elif inlist and (code.endswith(']') or code.strip().startswith(']')): 761ba12b75SBruce Richardson inlist = False 771ba12b75SBruce Richardson continue 781ba12b75SBruce Richardson elif inlist or infiles: 791ba12b75SBruce Richardson # skip further subarrays or lists 801ba12b75SBruce Richardson if '[' in code or ']' in code: 811ba12b75SBruce Richardson continue 821ba12b75SBruce Richardson if not code.startswith(indent) or code[len(indent)] == ' ': 831ba12b75SBruce Richardson print(f'Error: Incorrect indent at {filename}:{lineno + 1}') 841ba12b75SBruce Richardson line, code, comments = setline(contents, lineno, indent + line.strip()) 851ba12b75SBruce Richardson edit_count += 1 861ba12b75SBruce Richardson if not code.endswith(','): 871ba12b75SBruce Richardson print(f'Error: Missing trailing "," in list at {filename}:{lineno + 1}') 881ba12b75SBruce Richardson line, code, comments = setline(contents, lineno, code + ',' + comments) 891ba12b75SBruce Richardson edit_count += 1 901ba12b75SBruce Richardson if len(code.split(',')) > 2: # only one comma per line 911ba12b75SBruce Richardson print(f'Error: multiple entries per line in list at {filename}:{lineno +1}') 921ba12b75SBruce Richardson entries = [e.strip() for e in code.split(',') if e.strip()] 931ba12b75SBruce Richardson line, code, comments = setline(contents, lineno, 941ba12b75SBruce Richardson indent + (',\n' + indent).join(entries) + 951ba12b75SBruce Richardson ',' + comments) 961ba12b75SBruce Richardson edit_count += 1 971ba12b75SBruce Richardson return edit_count 981ba12b75SBruce Richardson 991ba12b75SBruce Richardson 1001ba12b75SBruce Richardsondef process_file(filename, fix): 1011ba12b75SBruce Richardson '''run checks on file "filename"''' 1021ba12b75SBruce Richardson if VERBOSE: 1031ba12b75SBruce Richardson print(f'Processing {filename}') 1041ba12b75SBruce Richardson with open(filename) as f: 1051ba12b75SBruce Richardson contents = [ln.rstrip() for ln in f.readlines()] 1061ba12b75SBruce Richardson 1071ba12b75SBruce Richardson if check_indentation(filename, contents) > 0 and fix: 1081ba12b75SBruce Richardson print(f"Fixing {filename}") 1091ba12b75SBruce Richardson with open(filename, 'w') as f: 1101ba12b75SBruce Richardson f.writelines([f'{ln}\n' for ln in contents]) 1111ba12b75SBruce Richardson 1121ba12b75SBruce Richardson 1131ba12b75SBruce Richardsondef main(): 1141ba12b75SBruce Richardson '''parse arguments and then call other functions to do work''' 1151ba12b75SBruce Richardson global VERBOSE 1161ba12b75SBruce Richardson parser = ArgumentParser(description='Run syntax checks on DPDK meson.build files') 1171ba12b75SBruce Richardson parser.add_argument('-d', metavar='directory', default='.', help='Directory to process') 1181ba12b75SBruce Richardson parser.add_argument('--fix', action='store_true', help='Attempt to fix errors') 1191ba12b75SBruce Richardson parser.add_argument('-v', action='store_true', help='Verbose output') 1201ba12b75SBruce Richardson args = parser.parse_args() 1211ba12b75SBruce Richardson 1221ba12b75SBruce Richardson VERBOSE = args.v 1231ba12b75SBruce Richardson for f in scan_dir(args.d): 1241ba12b75SBruce Richardson process_file(f, args.fix) 1251ba12b75SBruce Richardson 1261ba12b75SBruce Richardson 1271ba12b75SBruce Richardsonif __name__ == "__main__": 1281ba12b75SBruce Richardson main() 129