2021-04-26 10:54:02 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# Copyright(c) 2021 Intel Corporation
|
|
|
|
|
|
|
|
'''
|
|
|
|
A Python script to run some checks on meson.build files in DPDK
|
|
|
|
'''
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import os
|
2021-10-22 20:55:31 +00:00
|
|
|
import re
|
2021-04-26 10:54:02 +00:00
|
|
|
from os.path import relpath, join
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
|
|
|
|
VERBOSE = False
|
|
|
|
|
|
|
|
|
|
|
|
def scan_dir(path):
|
|
|
|
'''return meson.build files found in path'''
|
|
|
|
for root, dirs, files in os.walk(path):
|
|
|
|
if 'meson.build' in files:
|
|
|
|
yield(relpath(join(root, 'meson.build')))
|
|
|
|
|
|
|
|
|
|
|
|
def split_code_comments(line):
|
|
|
|
'splits a line into a code part and a comment part, returns (code, comment) tuple'
|
|
|
|
if line.lstrip().startswith('#'):
|
|
|
|
return ('', line)
|
|
|
|
elif '#' in line and '#include' not in line: # catch 99% of cases, not 100%
|
|
|
|
idx = line.index('#')
|
|
|
|
while (line[idx - 1].isspace()):
|
|
|
|
idx -= 1
|
|
|
|
return line[:idx], line[idx:]
|
|
|
|
else:
|
|
|
|
return (line, '')
|
|
|
|
|
|
|
|
|
|
|
|
def setline(contents, index, value):
|
|
|
|
'sets the contents[index] to value. Returns the line, along with code and comments part'
|
|
|
|
line = contents[index] = value
|
|
|
|
code, comments = split_code_comments(line)
|
|
|
|
return line, code, comments
|
|
|
|
|
|
|
|
|
|
|
|
def check_indentation(filename, contents):
|
|
|
|
'''check that a list or files() is correctly indented'''
|
|
|
|
infiles = False
|
|
|
|
inlist = False
|
|
|
|
edit_count = 0
|
|
|
|
for lineno, line in enumerate(contents):
|
|
|
|
code, comments = split_code_comments(line)
|
|
|
|
if not code.strip():
|
|
|
|
continue
|
2021-10-22 20:55:31 +00:00
|
|
|
if re.match('^ *\t', code):
|
|
|
|
print(f'Error parsing {filename}:{lineno}, got some tabulation')
|
2021-04-26 10:54:02 +00:00
|
|
|
if code.endswith('files('):
|
|
|
|
if infiles:
|
|
|
|
raise(f'Error parsing {filename}:{lineno}, got "files(" when already parsing files list')
|
|
|
|
if inlist:
|
|
|
|
print(f'Error parsing {filename}:{lineno}, got "files(" when already parsing array list')
|
|
|
|
infiles = True
|
|
|
|
indent_count = len(code) - len(code.lstrip(' '))
|
|
|
|
indent = ' ' * (indent_count + 8) # double indent required
|
|
|
|
elif code.endswith('= ['):
|
|
|
|
if infiles:
|
|
|
|
raise(f'Error parsing {filename}:{lineno}, got start of array when already parsing files list')
|
|
|
|
if inlist:
|
|
|
|
print(f'Error parsing {filename}:{lineno}, got start of array when already parsing array list')
|
|
|
|
inlist = True
|
|
|
|
indent_count = len(code) - len(code.lstrip(' '))
|
|
|
|
indent = ' ' * (indent_count + 8) # double indent required
|
|
|
|
elif infiles and (code.endswith(')') or code.strip().startswith(')')):
|
|
|
|
infiles = False
|
|
|
|
continue
|
|
|
|
elif inlist and (code.endswith(']') or code.strip().startswith(']')):
|
|
|
|
inlist = False
|
|
|
|
continue
|
|
|
|
elif inlist or infiles:
|
|
|
|
# skip further subarrays or lists
|
|
|
|
if '[' in code or ']' in code:
|
|
|
|
continue
|
|
|
|
if not code.startswith(indent) or code[len(indent)] == ' ':
|
|
|
|
print(f'Error: Incorrect indent at {filename}:{lineno + 1}')
|
|
|
|
line, code, comments = setline(contents, lineno, indent + line.strip())
|
|
|
|
edit_count += 1
|
|
|
|
if not code.endswith(','):
|
|
|
|
print(f'Error: Missing trailing "," in list at {filename}:{lineno + 1}')
|
|
|
|
line, code, comments = setline(contents, lineno, code + ',' + comments)
|
|
|
|
edit_count += 1
|
|
|
|
if len(code.split(',')) > 2: # only one comma per line
|
|
|
|
print(f'Error: multiple entries per line in list at {filename}:{lineno +1}')
|
|
|
|
entries = [e.strip() for e in code.split(',') if e.strip()]
|
|
|
|
line, code, comments = setline(contents, lineno,
|
|
|
|
indent + (',\n' + indent).join(entries) +
|
|
|
|
',' + comments)
|
|
|
|
edit_count += 1
|
|
|
|
return edit_count
|
|
|
|
|
|
|
|
|
|
|
|
def process_file(filename, fix):
|
|
|
|
'''run checks on file "filename"'''
|
|
|
|
if VERBOSE:
|
|
|
|
print(f'Processing {filename}')
|
|
|
|
with open(filename) as f:
|
|
|
|
contents = [ln.rstrip() for ln in f.readlines()]
|
|
|
|
|
|
|
|
if check_indentation(filename, contents) > 0 and fix:
|
|
|
|
print(f"Fixing {filename}")
|
|
|
|
with open(filename, 'w') as f:
|
|
|
|
f.writelines([f'{ln}\n' for ln in contents])
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
'''parse arguments and then call other functions to do work'''
|
|
|
|
global VERBOSE
|
|
|
|
parser = ArgumentParser(description='Run syntax checks on DPDK meson.build files')
|
|
|
|
parser.add_argument('-d', metavar='directory', default='.', help='Directory to process')
|
|
|
|
parser.add_argument('--fix', action='store_true', help='Attempt to fix errors')
|
|
|
|
parser.add_argument('-v', action='store_true', help='Verbose output')
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
VERBOSE = args.v
|
|
|
|
for f in scan_dir(args.d):
|
|
|
|
process_file(f, args.fix)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|