net/cnxk: support marking and VLAN tagging
[dpdk.git] / devtools / check-meson.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: BSD-3-Clause
3 # Copyright(c) 2021 Intel Corporation
4
5 '''
6 A Python script to run some checks on meson.build files in DPDK
7 '''
8
9 import sys
10 import os
11 from os.path import relpath, join
12 from argparse import ArgumentParser
13
14 VERBOSE = False
15
16
17 def 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
24 def 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
37 def 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
44 def 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
97 def 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
110 def 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
124 if __name__ == "__main__":
125     main()