xref: /dpdk/devtools/process-iwyu.py (revision 0988482fb36299663d28dac5bcb008c586fd008d)
1*0988482fSSean Morrissey#!/usr/bin/env python3
2*0988482fSSean Morrissey# SPDX-License-Identifier: BSD-3-Clause
3*0988482fSSean Morrissey# Copyright(c) 2021 Intel Corporation
4*0988482fSSean Morrissey#
5*0988482fSSean Morrissey
6*0988482fSSean Morrisseyimport argparse
7*0988482fSSean Morrisseyimport fileinput
8*0988482fSSean Morrisseyimport sys
9*0988482fSSean Morrisseyfrom os.path import abspath, relpath, join
10*0988482fSSean Morrisseyfrom pathlib import Path
11*0988482fSSean Morrisseyfrom mesonbuild import mesonmain
12*0988482fSSean Morrissey
13*0988482fSSean Morrissey
14*0988482fSSean Morrisseydef args_parse():
15*0988482fSSean Morrissey    "parse arguments and return the argument object back to main"
16*0988482fSSean Morrissey    parser = argparse.ArgumentParser(description="This script can be used to remove includes which are not in use\n")
17*0988482fSSean Morrissey    parser.add_argument('-b', '--build_dir', type=str, default='build',
18*0988482fSSean Morrissey                        help="The path to the build directory in which the IWYU tool was used in.")
19*0988482fSSean Morrissey    parser.add_argument('-d', '--sub_dir', type=str, default='',
20*0988482fSSean Morrissey                        help="The sub-directory to remove headers from.")
21*0988482fSSean Morrissey    parser.add_argument('file', type=Path,
22*0988482fSSean Morrissey                        help="The path to the IWYU log file or output from stdin.")
23*0988482fSSean Morrissey
24*0988482fSSean Morrissey    return parser.parse_args()
25*0988482fSSean Morrissey
26*0988482fSSean Morrissey
27*0988482fSSean Morrisseydef run_meson(args):
28*0988482fSSean Morrissey    "Runs a meson command logging output to process-iwyu.log"
29*0988482fSSean Morrissey    with open('process-iwyu.log', 'a') as sys.stdout:
30*0988482fSSean Morrissey        ret = mesonmain.run(args, abspath('meson'))
31*0988482fSSean Morrissey    sys.stdout = sys.__stdout__
32*0988482fSSean Morrissey    return ret
33*0988482fSSean Morrissey
34*0988482fSSean Morrissey
35*0988482fSSean Morrisseydef remove_includes(filepath, include, build_dir):
36*0988482fSSean Morrissey    "Attempts to remove include, if it fails then revert to original state"
37*0988482fSSean Morrissey    with open(filepath) as f:
38*0988482fSSean Morrissey        lines = f.readlines()  # Read lines when file is opened
39*0988482fSSean Morrissey
40*0988482fSSean Morrissey    with open(filepath, 'w') as f:
41*0988482fSSean Morrissey        for ln in lines:  # Removes the include passed in
42*0988482fSSean Morrissey            if not ln.startswith(include):
43*0988482fSSean Morrissey                f.write(ln)
44*0988482fSSean Morrissey
45*0988482fSSean Morrissey    # run test build -> call meson on the build folder, meson compile -C build
46*0988482fSSean Morrissey    ret = run_meson(['compile', '-C', build_dir])
47*0988482fSSean Morrissey    if (ret == 0):  # Include is not needed -> build is successful
48*0988482fSSean Morrissey        print('SUCCESS')
49*0988482fSSean Morrissey    else:
50*0988482fSSean Morrissey        # failed, catch the error
51*0988482fSSean Morrissey        # return file to original state
52*0988482fSSean Morrissey        with open(filepath, 'w') as f:
53*0988482fSSean Morrissey            f.writelines(lines)
54*0988482fSSean Morrissey        print('FAILED')
55*0988482fSSean Morrissey
56*0988482fSSean Morrissey
57*0988482fSSean Morrisseydef get_build_config(builddir, condition):
58*0988482fSSean Morrissey    "returns contents of rte_build_config.h"
59*0988482fSSean Morrissey    with open(join(builddir, 'rte_build_config.h')) as f:
60*0988482fSSean Morrissey        return [ln for ln in f.readlines() if condition(ln)]
61*0988482fSSean Morrissey
62*0988482fSSean Morrissey
63*0988482fSSean Morrisseydef uses_libbsd(builddir):
64*0988482fSSean Morrissey    "return whether the build uses libbsd or not"
65*0988482fSSean Morrissey    return bool(get_build_config(builddir, lambda ln: 'RTE_USE_LIBBSD' in ln))
66*0988482fSSean Morrissey
67*0988482fSSean Morrissey
68*0988482fSSean Morrisseydef process(args):
69*0988482fSSean Morrissey    "process the iwyu output on a set of files"
70*0988482fSSean Morrissey    filepath = None
71*0988482fSSean Morrissey    build_dir = abspath(args.build_dir)
72*0988482fSSean Morrissey    directory = args.sub_dir
73*0988482fSSean Morrissey
74*0988482fSSean Morrissey    print("Warning: The results of this script may include false positives which are required for different systems",
75*0988482fSSean Morrissey          file=sys.stderr)
76*0988482fSSean Morrissey
77*0988482fSSean Morrissey    keep_str_fns = uses_libbsd(build_dir)  # check for libbsd
78*0988482fSSean Morrissey    if keep_str_fns:
79*0988482fSSean Morrissey        print("Warning: libbsd is present, build will fail to detect incorrect removal of rte_string_fns.h",
80*0988482fSSean Morrissey              file=sys.stderr)
81*0988482fSSean Morrissey    # turn on werror
82*0988482fSSean Morrissey    run_meson(['configure', build_dir, '-Dwerror=true'])
83*0988482fSSean Morrissey    # Use stdin if no iwyu_tool out file given
84*0988482fSSean Morrissey    for line in fileinput.input(args.file):
85*0988482fSSean Morrissey        if 'should remove' in line:
86*0988482fSSean Morrissey            # If the file path in the iwyu_tool output is an absolute path it
87*0988482fSSean Morrissey            # means the file is outside of the dpdk directory, therefore ignore it.
88*0988482fSSean Morrissey            # Also check to see if the file is within the specified sub directory.
89*0988482fSSean Morrissey            filename = line.split()[0]
90*0988482fSSean Morrissey            if (filename != abspath(filename) and
91*0988482fSSean Morrissey                    directory in filename):
92*0988482fSSean Morrissey                filepath = relpath(join(build_dir, filename))
93*0988482fSSean Morrissey        elif line.startswith('-') and filepath:
94*0988482fSSean Morrissey            include = '#include ' + line.split()[2]
95*0988482fSSean Morrissey            print(f"Remove {include} from {filepath} ... ", end='', flush=True)
96*0988482fSSean Morrissey            if keep_str_fns and '<rte_string_fns.h>' in include:
97*0988482fSSean Morrissey                print('skipped')
98*0988482fSSean Morrissey                continue
99*0988482fSSean Morrissey            remove_includes(filepath, include, build_dir)
100*0988482fSSean Morrissey        else:
101*0988482fSSean Morrissey            filepath = None
102*0988482fSSean Morrissey
103*0988482fSSean Morrissey
104*0988482fSSean Morrisseydef main():
105*0988482fSSean Morrissey    process(args_parse())
106*0988482fSSean Morrissey
107*0988482fSSean Morrissey
108*0988482fSSean Morrisseyif __name__ == '__main__':
109*0988482fSSean Morrissey    main()
110