######################################################################################## # All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or # its licensors. # # For complete copyright and license terms please see the LICENSE at the root of this # distribution (the "License"). All use of this software is governed by the License, # or, if provided, by the license below or the license accompanying this file. Do not # remove or modify any license notices. This file is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # # # Original file Copyright Crytek GMBH or its affiliates, used under license. # ######################################################################################## import sys # you can uncomment this to make debugging build command lines easier. # sys.stdout.write('[WAF] Command line: ' + str(sys.argv)) import os import time from waflib import Configure, Logs, Utils, Options, ConfigSet from waflib.Build import BuildContext, CleanContext, Context # Attempt to import the aztest module try: import aztest AZ_TEST_SUPPORTED=True except ImportError: AZ_TEST_SUPPORTED=False pass BUILD_METRICS_SUPPORTED=False METRICS_NAMESPACE=None try: if '--enable-build-metrics' in sys.argv: # Inject the 'build_metrics' subpath into the sys.path so that the imported modules from build_metrics can do a relative import within itself build_metrics_folder_absolute_path = os.path.normpath(os.path.join(os.path.dirname(sys.argv[0]), 'build_metrics')) sys.path.insert(0, build_metrics_folder_absolute_path) from build_metrics import build_metrics_reporter, build_metrics_overrides if not build_metrics_reporter.metric_reporter.parse_command_line(sys.argv): Logs.error('--metrics-namespace must be set when metrics is enabled. Metrics will not be generated for this build.') else: BUILD_METRICS_SUPPORTED=True METRICS_NAMESPACE=build_metrics_reporter.metric_reporter.metrics_namespace build_metrics_reporter.start_metric_reporter() sys.path.remove(build_metrics_folder_absolute_path) except Exception: import traceback print(traceback.print_exc()) # Load globals from branch spec file if we must from waf_branch_spec import ADDITIONAL_SEARCH_PATHS, LUMBERYARD_ENGINE_PATH ############################################################################### Configure.autoconfig = True ############################################################################### LMBR_WAF_TOOL_DIR = os.path.normpath('{}/Tools/build/waf-1.7.13/lmbrwaflib'.format(LUMBERYARD_ENGINE_PATH)) ############################################################################### CURRENT_WAF_EXECUTABLE = '"{}" {}/Tools/build/waf-1.7.13/lmbr_waf'.format(sys.executable, LUMBERYARD_ENGINE_PATH) ############################################################################### if LUMBERYARD_ENGINE_PATH != '.': # If the lumberyard engine path external to the current path '.', update the context waf_dir Context.waf_dir = os.path.normpath('{}/Tools/build/waf-1.7.13'.format(LUMBERYARD_ENGINE_PATH)) ENGINE_FOLDERS = [ '@ENGINE@/Code', '@ENGINE@/Engine' ] def recurse_subfolders(ctx): recurse_paths = [ctx.Path(path) for path in ENGINE_FOLDERS + ADDITIONAL_SEARCH_PATHS] ctx.recurse(dirs=recurse_paths) # Configure Options for WAF def options(opt): # Load the lmbr waf modules opt.load('lumberyard', tooldir=[LMBR_WAF_TOOL_DIR]) opt.load_lmbr_general_modules() # Add additional options opt.add_lmbr_waf_options(AZ_TEST_SUPPORTED) # Lastly, load data driven settings opt.load_lmbr_data_driven_modules() def configure(conf): if BUILD_METRICS_SUPPORTED: build_metrics_reporter.set_build_command(conf.cmd) # Read the required lmbr configuration conf.calculate_engine_path() conf.mark_3rd_party_config_for_autoconf() conf.load_user_settings() conf.initialize_third_party_settings() if conf.is_option_true('update_user_settings'): conf.update_settings_options_file() # Run the WAF configure workflow conf.run_bootstrap() conf.load_lmbr_data_driven_modules() try: import restricted restricted.run(conf) except ImportError: pass conf.add_game_projects_to_specs() conf.add_gems_to_specs() conf.load_compile_rules_for_enabled_platforms() configure_timer = Utils.Timer() Logs.info("[WAF] 'Configure Projects' starting...") recurse_subfolders(conf) conf.configure_game_projects() conf.process_additional_code_folders() conf.process_gems() Logs.info("[WAF] 'Configure Projects' successful ({})".format(str(configure_timer))) conf.clear_waf_timestamp_files() def post_command_exec(bld): # [post project gen] if bld.cmd == 'msvs': project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp') project_gen_timestamp.write('') # [post uberfile gen] elif bld.cmd == 'generate_uber_files': generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp') generate_uber_files_timestamp.write('') # [post build] elif bld.cmd.startswith('build'): for message in bld.post_build_msg_info: Logs.info(message) for message in bld.post_build_msg_warning: Logs.warn(message) for message in bld.post_build_msg_error: Logs.error(message) stored_file_filter = '' stored_output_file = '' ############################################################################### # Run 'build' step def build(bld): if not isinstance(bld, BuildContext): bld.fatal("[Error] Invalid build command: '{}'. Type in '{} --help' for more information" .format(bld.cmd if hasattr(bld, 'cmd') else str(bld), CURRENT_WAF_EXECUTABLE)) if BUILD_METRICS_SUPPORTED: build_metrics_reporter.add_output_directory(bld.get_variant_dir()) build_metrics_reporter.set_build_command(bld.cmd) # Read the required lmbr configuration bld.calculate_engine_path() bld.mark_3rd_party_config_for_autoconf() bld.validate_build_command() bld.load_user_settings() # Validate if the platform is enabled since it could be disabled after the build command was instantiated build_platform = bld.env['PLATFORM'] if build_platform and build_platform != 'project_generator': if not bld.env['BUILD_ENABLED']: bld.fatal("[Error] Target platform '{}' not available for builds.".format(build_platform)) bld.initialize_third_party_settings() bld.load('artifacts_cache', tooldir=[LMBR_WAF_TOOL_DIR]) bld.options.project_spec = bld.options.project_spec.strip() # remove spaces bld.load_lmbr_data_driven_modules() # Create a post build message container bld.post_build_msg_info = [] bld.post_build_msg_warning = [] bld.post_build_msg_error = [] bld.add_group('az_code_gen_group') bld.add_group('regular_group') bld.set_group('regular_group') bld.prepare_build_environment() bld.add_gems_to_specs() bld.add_game_projects_to_specs() if not bld.check_special_command_timestamps(): return # Check conditionally loaded modules bld.check_module_load_options() ########################################### # Check if we need to start ourself recursively for IB if bld.invoke_waf_recursively(BUILD_METRICS_SUPPORTED, METRICS_NAMESPACE): if BUILD_METRICS_SUPPORTED: # we don't need anymore metrics from this build, so stop the reporter build_metrics_reporter.stop_metric_reporter() return ########################################### bld.add_post_fun(post_command_exec) ########################################### # Load configuration overwrites bld.env['CONFIG_OVERWRITES'] = bld.get_solution_overrides() # Load Core Engine Parts (Engine, Tools, Core Shaders etc) bld.game_project = None recurse_subfolders(bld) bld.process_gems() if bld.env['CONFIGURATION'] != 'project_generator': bld.qtlib_bootstrap(bld.env['PLATFORM'], bld.env['CONFIGURATION']) bld.setup_game_projects() bld.process_additional_code_folders() bld.validate_monolithic_specified_targets() # Save current build environment so that later commands in a chain can make use of the information (ex. run_tests) if 'build' in bld.cmd: bld.env.store(os.path.join(bld.cache_dir, 'last_build_cache.py')) def package(pkg): pkg.options.project_spec = pkg.options.project_spec.strip() # remove spaces pkg.calculate_engine_path() pkg.load_user_settings() pkg.initialize_third_party_settings() pkg.prepare_build_environment() # Perform a pre-validation on the platforms availability pkg.update_platform_availability_from_options() pkg.add_gems_to_specs() pkg.add_game_projects_to_specs() pkg.game_project = pkg.project pkg.check_module_load_options() pkg.process_gems() pkg.setup_game_projects() recurse_subfolders(pkg) def deploy(ctx): ctx.calculate_engine_path() ctx.load_user_settings() ctx.initialize_third_party_settings() ctx.prepare_build_environment() # Perform a pre-validation on the platforms availability ctx.update_platform_availability_from_options() ctx.add_gems_to_specs() ctx.add_game_projects_to_specs() ctx.game_project = ctx.project ctx.check_module_load_options() ctx.process_gems() ctx.setup_game_projects() recurse_subfolders(ctx) def run_unit_test(ctx): ctx.calculate_engine_path() ctx.load_user_settings() ctx.initialize_third_party_settings() ctx.prepare_build_environment() # Perform a pre-validation on the platforms availability ctx.update_platform_availability_from_options() ctx.add_gems_to_specs() ctx.add_game_projects_to_specs() ctx.game_project = ctx.project ctx.check_module_load_options() ctx.process_gems() ctx.setup_game_projects() recurse_subfolders(ctx) ############################################################################### def show_option_dialog(ctx): ctx.gui_modify_user_options() ############################################################################### class execute_waf_options_dialog(BuildContext): ''' Util class to execute waf options dialog ''' cmd = 'show_option_dialog' fun = 'show_option_dialog' ############################################################################### if AZ_TEST_SUPPORTED: def run_tests(ctx): """ Runs tests using the AzTestScanner """ # Start by grabbing the full args from command line args = ['scan'] if ctx.options.test_params: args += ctx.options.test_params.split() # Convenience function to check for flags in args def has_params(args, *params): return any(x in args for x in params) # If we are chaining, we want the cached info from the last build command # We do not override parameters that are passed in by the user (ex. the dir to test) ctx.env.load(os.path.join(ctx.cache_dir, 'last_build_cache.py')) if not has_params(args, '-d', '--dir'): args += ['--dir', ctx.get_output_folders(ctx.env['PLATFORM'], ctx.env['CONFIGURATION'])[0].abspath()] if not has_params(args, 'o', '--only') and ctx.options.targets: args += ['--only', ctx.options.targets] aztest.execute(args) class RunTestsContext(BuildContext): cmd = 'run_tests' fun = 'run_tests' def clean_stale_cached_artifacts(ctx): def should_delete(folder_path, days_to_keep): if not os.path.isdir(folder_path): return False last_access_timestamp = os.stat(folder_path).st_mtime current_timestamp = time.time() days_since_last_access = (current_timestamp - last_access_timestamp) / 60 / 60 / 24 if days_since_last_access > days_to_keep: return True return False whitelist = ['wafpickle'] artifacts_cache = ctx.options.artifacts_cache artifacts_cache_days_to_keep = int(ctx.options.artifacts_cache_days_to_keep) if ctx.is_option_true('artifacts_cache_wipeout'): artifacts_cache_days_to_keep = -1 whitelist = [] success = True for dir in os.listdir(artifacts_cache): folder_path = os.path.join(artifacts_cache, dir) if dir not in whitelist and should_delete(folder_path, artifacts_cache_days_to_keep): Logs.info('Deleting stale artifacts {}'.format(folder_path)) try: if Utils.is_win32: os.system('rmdir /S /Q {}'.format(folder_path)) else: os.system('rm -fr {}'.format(folder_path)) except Exception as e: Logs.warn('Failed to delete stale artifacts {}. {}'.format(dir, str(e))) success = False if not success: Logs.warn('Failed to delete all stale artifacts, make sure you have the correct permission to clean the cache and the cache is not in use.') class CleanStaleCachedArtifacts(BuildContext): cmd = 'clean_stale_cached_artifacts' fun = 'clean_stale_cached_artifacts'