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