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