From 6be0543fc16b9c5c019aac9bb256cd8ea7772f67 Mon Sep 17 00:00:00 2001 From: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:02:26 -0800 Subject: [PATCH] Adds script to brute-force detect unused files Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> --- scripts/cleanup/unusued_compilation.py | 102 +++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 scripts/cleanup/unusued_compilation.py diff --git a/scripts/cleanup/unusued_compilation.py b/scripts/cleanup/unusued_compilation.py new file mode 100644 index 0000000000..25fa4ca3ce --- /dev/null +++ b/scripts/cleanup/unusued_compilation.py @@ -0,0 +1,102 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +import argparse +import os +import shutil +import sys +sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../build')) +import ci_build + +EXTENSIONS_OF_INTEREST = ('.c', '.cc', '.cpp', '.cxx', '.h', '.hpp', '.hxx', '.inl') +EXCLUSIONS = ('AZ_DECLARE_MODULE_CLASS') + +def create_filelist(path): + filelist = set() + for input_file in path: + if os.path.isdir(input_file): + for dp, dn, filenames in os.walk(input_file): + if 'build\\windows_vs2019' in dp: + continue + for f in filenames: + extension = os.path.splitext(f)[1] + extension_lower = extension.lower() + if extension_lower in EXTENSIONS_OF_INTEREST: + filelist.add(os.path.join(dp, f)) + else: + extension = os.path.splitext(input_file)[1] + extension_lower = extension.lower() + if extension_lower in EXTENSIONS_OF_INTEREST: + filelist.add(os.path.join(os.getcwd(), input_file)) + else: + print(f'Error, file {input_file} does not have an extension of interest') + sys.exit(1) + return filelist + +def is_excluded(file): + normalized_file = os.path.normpath(file) + if '\\Platform\\' in normalized_file and not '\\Windows\\' in normalized_file: + return True + return False + +def filter_from_processed(filelist, filter_file_path): + if not os.path.exists(filter_file_path): + return # nothing to filter + + with open(filter_file_path, 'r') as filter_file: + try: + processed_files = [s.strip() for s in filter_file.readlines()] + except UnicodeDecodeError as err: + print('Error reading file {}, err: {}'.format(filter_file_path, err)) + sys.exit(1) + filelist -= set(processed_files) + filelist = [f for f in filelist if not is_excluded(f)] + +def cleanup_unused_compilation(path): + # 1. Create a list of all h/cpp files (consider multiple extensions) + filelist = create_filelist(path) + # 2. Remove files from "processed" list. This is needed because the process is a bruteforce approach and + # can take a while. If something is found in the middle, we want to be able to continue instead of + # starting over. Removing the "unusued_compilation_processed.txt" will start over. + filter_file_path = os.path.join(os.getcwd(), 'unusued_compilation_processed.txt') + filter_from_processed(filelist, filter_file_path) + # 3. For each file + total_files = len(filelist) + current_files = 1 + for file in filelist: + print(f"[{current_files}/{total_files}] Trying {file}") + # b. create backup + shutil.copy(file, file + '.bak') + # c. set the file contents as empty + with open(file, 'w') as source_file: + source_file.write('') + # d. build + ret = ci_build.build('build_config.json', 'Windows', 'profile_vs2019') + # e.1 if build succeeds, leave the file empty (leave backup) + # e.2 if build fails, restore backup + if ret != 0: + shutil.copy(file + '.bak', file) + print(f"\t[FAILED] restoring") + else: + print(f"\t[SUCCEED]") + # f. delete backup + os.remove(file + '.bak') + # add file to processed-list + with open(filter_file_path, 'a') as filter_file: + filter_file.write(file) + filter_file.write('\n') + current_files += 1 + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='This script finds C++ files that do not affect compilation (and therefore can be potentially removed)', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('file_or_dir', type=str, nargs='+', default=os.getcwd(), + help='list of files or directories to search within for files to consider') + args = parser.parse_args() + ret = cleanup_unused_compilation(args.file_or_dir) + sys.exit(ret)