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