xref: /dpdk/devtools/check-meson.py (revision f88b0b892204d071a46a629b00424a8d47055ba4)
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