You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.9 KiB
Python
136 lines
4.9 KiB
Python
#
|
|
# 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 fnmatch
|
|
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(',
|
|
'REGISTER_QT_CLASS_DESC(',
|
|
'TEST(',
|
|
'TEST_F(',
|
|
'TEST_P(',
|
|
'INSTANTIATE_TEST_CASE_P(',
|
|
'INSTANTIATE_TYPED_TEST_CASE_P(',
|
|
'AZ_UNIT_TEST_HOOK(',
|
|
'IMPLEMENT_TEST_EXECUTABLE_MAIN(',
|
|
'BENCHMARK_REGISTER_F(',
|
|
'DllMain(',
|
|
'wWinMain(',
|
|
'AZ_DLL_EXPORT',
|
|
'CreatePluginInstance('
|
|
)
|
|
PATH_EXCLUSIONS = (
|
|
'*\\Platform\\Android\\*',
|
|
'*\\Platform\\Common\\*',
|
|
'*\\Platform\\iOS\\*',
|
|
'*\\Platform\\Linux\\*',
|
|
'*\\Platform\\Mac\\*',
|
|
'Templates\\*',
|
|
'python\\*',
|
|
'build\\*',
|
|
'install\\*',
|
|
'Gems\\ImGui\\External\\ImGui\\*',
|
|
'*_Traits_*.h',
|
|
)
|
|
|
|
|
|
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' in dp:
|
|
continue
|
|
for f in filenames:
|
|
extension = os.path.splitext(f)[1]
|
|
extension_lower = extension.lower()
|
|
if extension_lower in EXTENSIONS_OF_INTEREST:
|
|
normalized_file = os.path.normpath(os.path.join(dp, f))
|
|
filelist.add(normalized_file)
|
|
else:
|
|
extension = os.path.splitext(input_file)[1]
|
|
extension_lower = extension.lower()
|
|
if extension_lower in EXTENSIONS_OF_INTEREST:
|
|
normalized_file = os.path.normpath(os.path.join(os.getcwd(), input_file))
|
|
filelist.add(normalized_file)
|
|
else:
|
|
print(f'Error, file {input_file} does not have an extension of interest')
|
|
sys.exit(1)
|
|
return filelist
|
|
|
|
def is_excluded(file):
|
|
for path_exclusion in PATH_EXCLUSIONS:
|
|
if fnmatch.fnmatch(file, path_exclusion):
|
|
return True
|
|
with open(file, 'r') as file:
|
|
contents = file.read()
|
|
for exclusion_term in EXCLUSIONS:
|
|
if exclusion_term in contents:
|
|
return True
|
|
return False
|
|
|
|
def filter_from_processed(filelist, filter_file_path):
|
|
filelist = set([f for f in filelist if not is_excluded(f)])
|
|
if os.path.exists(filter_file_path):
|
|
with open(filter_file_path, 'r') as filter_file:
|
|
processed_files = [s.strip() for s in filter_file.readlines()]
|
|
filelist -= set(processed_files)
|
|
return filelist
|
|
|
|
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')
|
|
filelist = filter_from_processed(filelist, filter_file_path)
|
|
sorted_filelist = sorted(filelist)
|
|
# 3. For each file
|
|
total_files = len(sorted_filelist)
|
|
current_files = 1
|
|
for file in sorted_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')
|
|
# 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)
|