1 # Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
3 from waflib import Configure, Logs, Utils, Node, TaskGen, Options, ConfigSet
4 from waflib.Build import BuildContext, CleanContext, Context
5 from waflib.Tools import c_aliases, c
6 from waflib.Task import Task,RUN_ME
7 from waflib.Configure import conf, ConfigurationContext
8 from waflib.TaskGen import after_method, before_method, feature, extension
9 from waflib.Errors import BuildError, WafError
10 from waflib.TaskGen import taskgen_method
25 # Load globals from branch spec file if we must
26 from waf_branch_spec import PLATFORMS
27 from waf_branch_spec import CONFIGURATIONS
29 g_bootstrap_was_run = False
31 ###############################################################################
32 Configure.autoconfig = True
34 ###############################################################################
35 CRY_WAF_TOOL_DIR='Code/Tools/waf-1.7.13/crywaflib'
37 ###############################################################################
38 # List of subfolders to parse
44 ###############################################################################
45 ## Configure Options for WAF
47 opt.load('cry_utils' ,tooldir=CRY_WAF_TOOL_DIR)
49 opt.load('project_settings', tooldir=CRY_WAF_TOOL_DIR)
50 opt.load('branch_spec', tooldir=CRY_WAF_TOOL_DIR)
51 opt.load('gui_tasks' , tooldir=CRY_WAF_TOOL_DIR)
53 ###########################################
54 # Load support for Uber Files
55 opt.load('generate_uber_files' ,tooldir=CRY_WAF_TOOL_DIR)
57 ###########################################
58 # Load Project Generators based on host (use the custom cry versions)
59 host = Utils.unversioned_sys_platform()
61 opt.load('msvs', tooldir=CRY_WAF_TOOL_DIR)
62 opt.load('msvs_override_handling', tooldir=CRY_WAF_TOOL_DIR)
63 opt.load('mscv_helper' ,tooldir=CRY_WAF_TOOL_DIR)
66 opt.load('xcode' ,tooldir=CRY_WAF_TOOL_DIR)
68 opt.load('eclipse' ,tooldir=CRY_WAF_TOOL_DIR)
70 # Load tools to improve dependency checking (by using compiler features)
72 opt.load('mscv_helper', tooldir=CRY_WAF_TOOL_DIR)
73 opt.load('msvcdeps', tooldir=CRY_WAF_TOOL_DIR)
74 opt.load('gccdeps', tooldir=CRY_WAF_TOOL_DIR)
77 # Load internal module extensions
78 module_extension_toolsdir = CRY_WAF_TOOL_DIR + '/module_extensions'
79 for file_name in [each for each in os.listdir(module_extension_toolsdir) if each.endswith('.py')]:
80 opt.load(file_name[:-3], tooldir=module_extension_toolsdir)
82 # Load custom user module extensions
83 custom_module_extensions = Context.launch_dir + '/_WAF_/custom_module_extensions'
84 if os.path.exists(custom_module_extensions):
85 for file_name in [each for each in os.listdir(custom_module_extensions) if each.endswith('.py')]:
86 opt.load(file_name[:-3], tooldir=custom_module_extensions)
88 ###########################################
89 # Add custom cryengine options
91 opt.add_option('-p', '--project-spec', dest='project_spec', action='store', default='', help='Spec to use when building the project')
93 # Add special command line option to prevent recursive execution of WAF
94 opt.add_option('--internal-dont-check-recursive-execution', dest='internal_dont_check_recursive_execution', action='store', default='False', help='!INTERNAL ONLY! DONT USE')
96 # Add options primarily used by the Visual Studio WAF Addin
97 waf_addin_group = opt.add_option_group('Visual Studio WAF Addin Options')
98 waf_addin_group.add_option('-a', '--ask-for-user-input', dest='ask_for_user_input', action='store', default='True', help='Disables all user promps in WAF')
99 waf_addin_group.add_option( '--file-filter', dest='file_filter', action='store', default="", help='Only compile files matching this filter')
100 waf_addin_group.add_option( '--show-includes', dest='show_includes', action='store', default='False', help='Show all files included (requires a file_filter)')
101 waf_addin_group.add_option( '--show-preprocessed-file', dest='show_preprocessed_file', action='store', default='False', help='Generate only Preprocessor Output (requires a file_filter)')
102 waf_addin_group.add_option( '--show-disassembly', dest='show_disassembly', action='store', default='False', help='Generate only Assembler Output (requires a file_filter)')
104 # DEPRECATED OPTIONS, only used to keep backwards compatibility
105 waf_addin_group.add_option( '--output-file', dest='output_file', action='store', default="", help='*DEPRECATED* Specify Output File for Disassemly or Preprocess option (requires a file_filter)')
106 waf_addin_group.add_option( '--use-overwrite-file', dest='use_overwrite_file', action='store', default="False", help='*DEPRECATED* Use special BinTemp/cry_waf.configuration_overwrites to specify per target configurations')
107 waf_addin_group.add_option( '--console-mode', dest='console_mode', action='store', default="False", help='No Gui. Display console only')
108 waf_addin_group.add_option( '--generate-product', dest='generate_product', action='store', default="False", help='Generate CryEngine product version (internal use)')
110 # Lastly, load data driven settings
111 opt.load('default_settings', tooldir=CRY_WAF_TOOL_DIR)
112 opt.load('cryengine_modules', tooldir=CRY_WAF_TOOL_DIR)
114 ###############################################################################
115 ## Configure 'configure' step
117 ###########################################
118 # Load base configuration function
119 conf.load('c_config')
121 sys_platform = Utils.unversioned_sys_platform()
122 if sys_platform == 'linux':
124 elif sys_platform== 'win32':
126 elif sys_platform== 'darwin':
129 #Download SDKs for first time git users
130 if not conf.is_bootstrap_available():
131 if not os.path.isdir(os.path.join(conf.path.abspath(), 'Code/SDKs')):
132 download_sdk_exe = os.path.join(conf.path.abspath(), 'download_sdks.exe')
133 if host == 'win_x64':
134 if os.path.isfile(download_sdk_exe):
135 subprocess.call(download_sdk_exe)
137 conf.fatal('[ERROR] Missing 3rd party SDK folder "<root>/Code/SDKs". \nAuto download failed: "<root>/download_sdks.exe" could not be located.\nPlease follow the ReadMe instructions on GitHub to download the SDKs manually.' )
140 subprocess.call(["python3","download_sdks.py"])
142 conf.fatal('[ERROR] Missing 3rd party SDK folder "<root>/Code/SDKs". \nAuto download failed: "<root>/download_sdks.py" could not be located or Python3 is not available.\nPlease follow the ReadMe instructions on GitHub to download the SDKs manually.' )
144 ###########################################
145 # Check for changes in user settings file
146 conf.check_user_settings_files()
148 ###########################################
149 # Load common windows settings if needed
150 conf.load('winres', tooldir=CRY_WAF_TOOL_DIR)
152 # Load common platform scripts
153 conf.load('compile_settings_cryengine', tooldir=CRY_WAF_TOOL_DIR)
155 conf.load('compile_settings_msvc', tooldir=CRY_WAF_TOOL_DIR)
156 conf.load('compile_settings_gcc', tooldir=CRY_WAF_TOOL_DIR)
157 conf.load('compile_settings_clang', tooldir=CRY_WAF_TOOL_DIR)
159 conf.load('compile_settings_windows', tooldir=CRY_WAF_TOOL_DIR)
160 conf.load('compile_settings_linux', tooldir=CRY_WAF_TOOL_DIR)
161 conf.load('compile_settings_linux_x86', tooldir=CRY_WAF_TOOL_DIR)
162 conf.load('compile_settings_linux_x64', tooldir=CRY_WAF_TOOL_DIR)
163 conf.load('compile_settings_darwin', tooldir=CRY_WAF_TOOL_DIR)
165 optional_platforms = [
170 for platform in optional_platforms:
171 file_name = 'compile_settings_' + platform
172 if os.path.isfile(os.path.join(conf.path.abspath(), CRY_WAF_TOOL_DIR, file_name + '.py')):
173 conf.load(file_name, tooldir=CRY_WAF_TOOL_DIR)
176 conf.get_supported_platforms().remove(platform)
179 Logs.warn('[WARNING] - Compiler settings not found. (%s) ' % file_name )
181 # Load CppCheck since it is a 'global' platform
182 conf.load('cppcheck', tooldir=CRY_WAF_TOOL_DIR)
184 # Load QT last as we need to hook the runnable status at the lowest level
185 conf.load('qt' ,tooldir=CRY_WAF_TOOL_DIR)
187 ###########################################
188 # Load support for c# and swig
189 conf.load('swig', tooldir=CRY_WAF_TOOL_DIR)
190 conf.load('protoc', tooldir=CRY_WAF_TOOL_DIR)
191 conf.load('cs', tooldir=CRY_WAF_TOOL_DIR)
193 #Get user defined active specs
194 specs_platforms = set()
195 if conf.options.specs_to_include_in_project_generation:
196 spec_list = conf.options.specs_to_include_in_project_generation.replace(' ', '').split(',')
197 else: # Special case for Buildbot which sets no spec for specs_to_include_in_project_generation.
198 spec_list = conf.loaded_specs()
200 # Get the platform list of all active specs
201 tmp_specs_platforms = set()
202 for spec in spec_list:
203 for platform in conf.get_supported_platforms():
204 tmp_specs_platforms.update(conf.spec_valid_platforms(spec, platform))
206 # Convert to complete platform names i.e. win -> win_x64 and win_x86
207 for valid_platform in tmp_specs_platforms:
208 for value in conf.get_platform_permutation_map(valid_platform).values():
209 specs_platforms.update(value)
211 ###########################################
212 # Load settings for all platforms
214 installed_platforms = []
215 for platform in conf.get_supported_platforms():
217 # Check if platform is required for active specs
218 if platform not in specs_platforms:
221 Logs.info('[INFO] Configure "%s - [%s]"' % (platform, ', '.join(conf.get_supported_configurations())))
223 # Load Platform Configuration (except cppcheck as it is not a *real* compiler)
224 if platform != 'cppcheck':
225 file_name = 'compile_rules_' + host + '_' + platform
226 if os.path.isfile(os.path.join(conf.path.abspath(), CRY_WAF_TOOL_DIR, file_name + '.py')):
227 conf.load('compile_rules_' + host + '_' + platform, tooldir=CRY_WAF_TOOL_DIR)
229 # Use the specialized function to load platform specifics
230 function_name = 'check_%s_%s_installed' % ( host, platform )
231 if not hasattr(conf, function_name):
232 conf.fatal('[ERROR] Required function \'%s\' not found' % function_name )
234 # Try to load the function
235 if not getattr(conf, function_name)():
236 Logs.warn('[WARNING] - Unable to locate platform "%s" on system. Disabling platform support for: %s !!!' % (platform, platform))
239 Logs.warn('[WARNING] - Unable to locate compiler rules "%s". Disabling platform support for: %s !!!. ' % (file_name, platform))
243 installed_platforms.append(platform)
245 for configuration in conf.get_supported_configurations():
246 conf.setenv(platform + '_' + configuration) # change env to configuration env
247 conf.init_compiler_settings()
249 # Use the specialized function to load platform specifics
250 function_name = 'load_%s_%s_%s_settings' % ( configuration, host, platform )
251 if not hasattr(conf, function_name):
252 conf.fatal('[ERROR] Required function \'%s\' not found' % function_name )
254 # Try to load the function
255 getattr(conf, function_name)()
258 conf.configure_protoc()
261 conf.configure_swig()
262 conf.configure_mono()
263 conf.env['build_relevant_platforms'] = installed_platforms
264 conf.setenv('') # change env back to global env
266 conf.set_supported_platforms(installed_platforms)
267 conf.env['build_relevant_platforms'] = installed_platforms # need to store in global cache as well for project_generators
269 ###########################################
270 # Load support for c and cxx compiler
274 conf.load('protoc',tooldir=CRY_WAF_TOOL_DIR)
276 ###########################################
277 # Load Platform specific helpers based on host
278 host = Utils.unversioned_sys_platform()
282 ###########################################
283 # Create VS-Projects automatically during configure when running on windows
284 if host == 'win32' and conf.is_option_true('generate_vs_projects_automatically'):
285 # Workflow improvement: for all builds generate projects after the build
286 # except when using the default build target 'utilities' then do it before
287 if 'build' in Options.commands:
288 build_cmd_idx = Options.commands.index('build')
289 Options.commands.insert(build_cmd_idx, 'msvs')
291 Options.commands.append('msvs')
293 ###########################################
294 # Load remaining tools for correct auto configure
296 conf.load('recode', tooldir=CRY_WAF_TOOL_DIR)
297 conf.load('static_code_analyzer', tooldir=CRY_WAF_TOOL_DIR)
299 ###########################################
300 # Recurse into subfolders for auto conf when any wscript changes
301 conf.recurse(dirs=SUBFOLDERS, mandatory=False)
303 # Load Game Specific parts
304 for project in conf.game_projects():
305 conf.game_project = project
306 conf.recurse( conf.game_code_folder(project))
308 ###########################################
309 # Always update uber files during configuration
310 # But don't add the same command twice
311 if len(Options.commands) == 0:
312 Options.commands.insert(0, 'generate_uber_files')
313 elif not Options.commands[0] == 'generate_uber_files':
314 Options.commands.insert(0, 'generate_uber_files')
316 # Remove timestamp files to force builds of generate_uber_files and project gen even if
317 # some command after configure failes
319 generate_uber_files_timestamp = conf.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
320 os.stat(generate_uber_files_timestamp.abspath())
324 generate_uber_files_timestamp.delete()
327 project_gen_timestamp = conf.get_bintemp_folder_node().make_node('project_gen.timestamp')
328 os.stat(project_gen_timestamp.abspath())
332 project_gen_timestamp.delete()
334 def post_command_exec(bld):
336 if bld.cmd == 'msvs':
337 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
338 project_gen_timestamp.write('')
339 # [post uberfile gen]
340 elif bld.cmd == 'generate_uber_files':
341 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
342 generate_uber_files_timestamp.write('')
344 elif bld.cmd.startswith('build'):
345 for message in bld.post_build_msg_info:
348 for message in bld.post_build_msg_warning:
351 for message in bld.post_build_msg_error:
354 stored_file_filter = ''
355 stored_output_file = ''
357 ###############################################################################
361 # Keep backward compatibility
362 if bld.options.project_spec == 'everything':
363 bld.options.project_spec = 'trybuilder'
365 bld.options.project_spec = bld.options.project_spec.strip() # remove spaces
367 # Create a post build message container
368 bld.post_build_msg_info = []
369 bld.post_build_msg_warning = []
370 bld.post_build_msg_error = []
372 # Add groups to ensure all task generators who create static lib tasks are executd first
373 bld.add_group('generate_static_lib')
374 bld.add_group('regular_group')
375 # Set back to our regular compile group
376 bld.set_group('regular_group')
379 ###########################################
380 # Load common windows settings if needed
381 bld.load('winres', tooldir=CRY_WAF_TOOL_DIR)
383 # Check if a valid spec is defined
384 if bld.cmd != 'generate_uber_files' and bld.cmd != 'msvs' and bld.cmd != 'eclipse':
385 # project spec is a mandatory parameter
386 if bld.options.project_spec == '':
387 bld.fatal('[ERROR] No Project Spec defined. Use "-p <spec_name>" command line option to specify project spec. Valid specs are "%s". ' % bld.loaded_specs())
389 # Ensure that the selected spec is supported on this platform
390 if not bld.options.project_spec in bld.loaded_specs():
391 bld.fatal('[ERROR] Invalid Project Spec (%s) defined, valid specs are %s' % (bld.options.project_spec, bld.loaded_specs()))
393 # Check for valid single file compilation flag pairs
394 if bld.is_option_true('show_preprocessed_file') and bld.options.file_filter == "":
395 bld.fatal('--show-preprocessed-file can only be used in conjunction with --file-filter')
396 elif bld.is_option_true('show_disassembly') and bld.options.file_filter == "":
397 bld.fatal('--show-disassembly can only be used in conjunction with --file-filter')
399 ###########################################
400 # Check timestamps for special commands
401 if not 'generate_uber_files' in Options.commands and bld.cmd != 'generate_uber_files':
402 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
404 os.stat(generate_uber_files_timestamp.abspath())
406 # No generate_uber file timestamp, restart command chain, prefixed with gen uber files cmd
407 Options.commands = ['generate_uber_files'] + [bld.cmd] + Options.commands
410 if not 'msvs' in Options.commands and bld.cmd != 'msvs':
411 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
413 os.stat(project_gen_timestamp.abspath())
415 # No project_gen timestamp, append project_gen to commands
416 if bld.is_option_true('generate_vs_projects_automatically') and Utils.unversioned_sys_platform() == 'win32':
417 if not bld.is_option_true('internal_dont_check_recursive_execution'):
418 Options.commands = Options.commands + ['msvs']
420 ###########################################
421 # Check for valid variant if we are not generating projects
422 if bld.cmd == 'xcode' or bld.cmd == 'msvs' or bld.cmd == 'eclipse' or bld.cmd == 'generate_uber_files':
423 bld.env['PLATFORM'] = 'project_generator'
424 bld.env['CONFIGURATION'] = 'project_generator'
427 bld.fatal('please use a valid build configuration, try "waf --help"')
429 (platform, configuration) = bld.get_platform_and_configuration()
430 bld.env['PLATFORM'] = platform
431 bld.env['CONFIGURATION'] = configuration
433 if not platform in bld.get_supported_platforms():
434 bld.fatal( '[ERROR] - Target platform "%s" not supported. [on host platform: %s]' % (platform, Utils.unversioned_sys_platform()) )
436 # When building, only support platform that we are currently building for to reduce internal workload
437 bld.set_supported_platforms([bld.env['PLATFORM']])
439 # check if configuration is valid in the current spec scope
440 bVariantValid = False
441 for conf in bld.spec_valid_configurations():
442 if conf == configuration:
444 if not bVariantValid:
445 bld.fatal('[ERROR] Building Spec "%s" for "%s|%s" is not supported. Valid Configurations are: "%s".' % (bld.options.project_spec, platform, configuration, ', '.join(bld.spec_valid_configurations())))
447 # check if platform is valid in the current spec scope
448 bVariantValid = False
449 for p0 in bld.spec_valid_platforms():
450 for p1 in bld.get_platform_list(bld.env['PLATFORM']):
453 if not bVariantValid:
454 bld.fatal('[ERROR] Building Spec "%s" for "%s|%s" is not supported.. Valid Platforms are: "%s".' % (bld.options.project_spec, platform, configuration, ', '.join(bld.spec_valid_platforms())))
456 # Ensure that, if specified, target is supported in this spec
457 if bld.options.targets:
458 for target in bld.options.targets.split(','):
459 if not target in bld.spec_modules():
460 bld.fatal('[ERROR] Module "%s" is not configurated to be build in spec "%s" in "%s|%s"' % (target, bld.options.project_spec, platform, configuration))
462 ###########################################
463 # Setup command coordinator
464 bld.load('cmd_coordinator', tooldir=CRY_WAF_TOOL_DIR)
465 bld.setup_command_coordinator()
467 # We might run multiple instances of WAF at the same time
468 # 1) IB as master ... The WAF IB instance (master) will handle task creation and will intercept each call to a .exe file making it the "Build Master" ... the original WAF instance should just exit here
469 # 2) IB as service ... The original WAF instance(master) will handle the task creation and "may" forward the .exe calls to the IB instance (slave)
470 if not bld.instance_is_build_master():
473 ###########################################
474 bld.add_post_fun(post_command_exec)
476 ###########################################
477 # Setup Output Path for Project Geneators
478 bld.solution_name = bld.get_solution_name()
479 bld.projects_dir = bld.get_project_output_folder()
481 ###########################################
482 # Load configuration overwrites
483 bld.env['CONFIG_OVERWRITES'] = bld.get_solution_overrides()
485 ###########################################
486 # Disable optimizations if requested
487 # Must be done after computing overrides
488 if bld.is_option_true('force_deoptimized_builds'):
489 bld.env['CFLAGS'] += bld.env['COMPILER_FLAGS_DisableOptimization']
490 bld.env['CXXFLAGS'] += bld.env['COMPILER_FLAGS_DisableOptimization']
492 ###########################################
493 # Load Support for Recode if required
494 host = Utils.unversioned_sys_platform()
496 bld.load('recode', tooldir=CRY_WAF_TOOL_DIR)
497 if str(bld.options.support_recode) == str(True):
498 bld.enable_recode = bld.check_global_recode_enabled()
500 ###########################################
501 # Add support for static code analyzer
502 bld.load('static_code_analyzer', tooldir=CRY_WAF_TOOL_DIR)
504 # Clear <output folder>/Image/Loose on durango as a workaround for buggy deploy
505 if bld.env['PLATFORM'] == 'durango':
506 for node in bld.get_output_folders(bld.env['PLATFORM'], bld.env['CONFIGURATION']):
507 image_lost_dir = node.find_dir('Image/Loose')
509 files = image_lost_dir.ant_glob('**/*')
514 # Load Core Engine Parts (Engine, Tools, Core Shaders etc)
515 bld.game_project = None
516 bld.recurse(SUBFOLDERS, mandatory=False)
518 # Load Game Specific parts
519 for project in bld.game_projects():
520 bld.game_project = project
521 bld.recurse( bld.game_code_folder(project))
523 ###############################################################################
525 def target_clean(self):
527 tmp_targets = self.options.targets[:]
529 # Sort of recursive algorithm, find all outputs of targets
530 # Repeat if new targets were added due to use directives
531 while len(tmp_targets) > 0:
534 for tgen in self.get_all_task_gen():
536 if not tgen.target in tmp_targets:
542 if n.is_child_of(self.bldnode):
545 if hasattr(tgen, 'use'):
546 new_targets.append(tgen.use)
548 tmp_targets = new_targets[:]
550 # Final File list to delete
551 to_delete = list(set(to_delete))
552 for file in to_delete:
556 ###############################################################################
557 # Copy pasted from MSVS..
558 def convert_vs_configuration_to_waf_configuration(configuration):
559 if 'Debug' in configuration:
561 if 'Profile' in configuration:
563 if 'Release' in configuration:
565 if 'Performance' in configuration:
570 ###############################################################################
573 if not bld.is_option_true('ask_for_user_input'):
576 # Function to handle creating of new libraries
577 def install_waf_addin():
578 enviroment = os.environ.copy()
579 if not 'APPDATA' in enviroment:
580 print 'APPDATA enviroment variable not found, WAF cannot figure out where to install the addin'
583 install_path = enviroment['APPDATA'] + '\\Microsoft\\MSEnvShared\\Addins'
584 source_path = bld.path.abspath() + '\\Code\\Tools\\waf_addin\\Bin_Release'
586 Logs.info('WAF Addin will be installed into:\n%s' % install_path)
587 bld.start_msg('Create Install directory')
589 if not os.path.exists(install_path):
590 os.makedirs(install_path)
592 bld.end_msg('failed (%s)' % sys.exc_info()[1], color='RED')
596 for file in ['WAF_Addin.AddIn', 'WAF_Addin.dll', 'WAF_Addin.xml']:
597 bld.start_msg('Copy %s to install directory' % file)
598 # Make output writeable
600 os.chmod(install_path + '\\' + file, 493) # 0755
605 shutil.copy2(source_path + '\\' + file, install_path + '\\' + file)
607 bld.end_msg('failed (%s)' % sys.exc_info()[1], color='RED')
610 Options.commands.insert(0, 'utilities')
612 def reconfigure_waf():
613 Options.commands.insert(0, 'configure')
615 def bootstrap_update():
616 bld.ExecuteBootstrap()
617 Options.commands.insert(0, 'utilities')
619 def regenerate_vs_solution():
620 Options.commands.insert(0, 'utilities')
621 Options.commands.insert(0, 'msvs')
623 def regenerate_uber_files():
624 Options.commands.insert(0, 'utilities')
625 Options.commands.insert(0, 'generate_uber_files')
627 def regenerate_config_file():
629 # Remove current config file and remove some state
630 config_file = bld.get_user_settings_node()
634 bld.load_user_settings()
636 # Afterwards return to utilies dialog
637 Options.commands.insert(0, 'utilities')
639 def apply_new_crycommon_layout_to_custom_folder():
640 folder = raw_input('Please enter absolute folder path: ')
641 if os.path.isdir(folder):
642 bld.convert_files_in_folder_to_cry_common([bld.root.make_node(folder)])
644 Logs.error("Folder could not be found.", folder)
645 Options.commands.insert(0, 'utilities')
648 Options.commands.insert(0, 'utilities')
649 Options.commands.insert(0, 'show_option_dialog')
651 # Function to handle exit requests
655 if not bld.is_option_true('console_mode'):
656 conversion_file = ['Apply new CryCommon layout', [
657 ('Custom folder and subfolders', apply_new_crycommon_layout_to_custom_folder)
664 menu_regenerate = ['Generate', [
665 ("Visual Studio Solution", regenerate_vs_solution),
666 ("Uber Files", regenerate_uber_files)
669 menu_tools = ['Tools', [
670 ("Options", waf_options),
671 ("Configure", reconfigure_waf)
674 if bld.is_bootstrap_available():
675 menu_tools[1] += [("Run Bootstrap", bootstrap_update)]
677 #menu_tools[1] += [("Install WAF Addin", install_waf_addin)] # disabled as unmaintained
678 fn_result = bld.gui_get_waf_menu_choice([menu_tools, menu_regenerate, conversion_file, menu_file])
682 fn_result() # Execute result
684 Options.commands.insert(0, 'utilities')
689 #(install_waf_addin,Install WAF Addin), # disabled as unmaintained
690 (regenerate_vs_solution, 'Regenerate Visual Studio Solution'),
691 (regenerate_uber_files, 'Regenerate Uber Files'),
692 (regenerate_config_file, 'Regenerate Config File')
695 if bld.is_bootstrap_available():
696 choices += [(bootstrap_update, 'Run Bootstrap Update')]
698 print(' === Crytek WAF Build System === ')
699 print(' Please use waf --help to see all valid build targets and build options')
701 for idx, option in enumerate(choices):
702 print('%s: %s' % (idx, option[1]))
704 user_input = raw_input('Please select an option: ')
706 # Sanity check for valid user inputs
708 if int(user_input) < int(0) or int(user_input) > len(choices):
711 print('Invalid Input, please choose a value between 1 and %s', len(choices))
712 Options.commands.insert(0, 'utilities')
715 # Input is fine, exectue selection
716 if int(user_input) == 1:
720 choices[int(user_input)][0]()
722 ##############################################################################
723 class execute_utilities(BuildContext):
724 ''' Util class to execute waf utilities dialog '''
728 ###############################################################################
729 # Create Build Context Commands for multiple platforms/configurations
730 for platform in PLATFORMS[Utils.unversioned_sys_platform()]:
731 for configuration in CONFIGURATIONS:
732 # Create new class to execute build with variant
733 name = CleanContext.__name__.replace('Context','').lower()
734 class tmp_clean(CleanContext):
735 cmd = name + '_' + platform + '_' + configuration
736 variant = platform + '_' + configuration
738 def __init__(self, **kw):
739 super(CleanContext, self).__init__(**kw)
740 self.top_dir = kw.get('top_dir', Context.top_dir)
743 if Configure.autoconfig:
744 env = ConfigSet.ConfigSet()
748 env.load(os.path.join(Context.lock_dir, Options.lockfile))
750 Logs.warn('Configuring the project')
753 if env.run_dir != Context.run_dir:
757 for f in env['files']:
759 h = hash((h, Utils.readf(f, 'rb')))
760 except (IOError, EOFError):
761 pass # ignore missing files (will cause a rerun cause of the changed hash)
762 do_config = h != env.hash
765 Options.commands.insert(0, self.cmd)
766 Options.commands.insert(0, 'configure')
769 # Execute custom clear command
771 if not self.all_envs:
773 self.recurse([self.run_dir])
775 if self.options.targets:
784 # Create new class to execute clean with variant
785 name = BuildContext.__name__.replace('Context','').lower()
786 class tmp_build(BuildContext):
787 cmd = name + '_' + platform + '_' + configuration
788 variant = platform + '_' + configuration
790 ###############################################################################
791 # Install Default variant
792 for y in (BuildContext, CleanContext):
794 variant = 'utilities'
797 ###############################################################################
798 # Create Commands for backwards compatibility
799 if Utils.unversioned_sys_platform() == 'win32':
800 for configuration in CONFIGURATIONS:
801 for context in (BuildContext, CleanContext):
802 name = context.__name__.replace('Context','').lower()
804 cmd = name + '_win32_' + configuration
805 variant = 'win_x86_' + configuration
806 for configuration in CONFIGURATIONS:
807 for context in (BuildContext, CleanContext):
808 name = context.__name__.replace('Context','').lower()
810 cmd = name + '_win64_' + configuration
811 variant = 'win_x64_' + configuration
815 def copy_files(self, source_file, output_folders = None):
817 if not output_folders:
818 output_folders = self.bld.get_output_folders(self.bld.env['PLATFORM'], self.bld.env['CONFIGURATION'])
820 for node in output_folders:
822 if hasattr(self, 'output_sub_folder'):
823 _output_sub_folder = str(self.output_sub_folder)
824 if os.path.isabs(_output_sub_folder):
825 output_node = self.bld.root.make_node(_output_sub_folder)
827 output_node = output_node.make_node(_output_sub_folder)
828 output_node = output_node.make_node( os.path.basename(source_file.abspath()) )
830 path = os.path.dirname( output_node.abspath() )
831 if not os.path.exists( path ):
834 self.create_task('copy_outputs', source_file, output_node)
836 ###############################################################################
837 # Class to handle copying of the final outputs into the Bin folder
838 class copy_outputs(Task):
842 src = self.inputs[0].abspath()
843 tgt = self.outputs[0].abspath()
845 # Create output folder
846 if not os.path.exists( os.path.dirname( tgt ) ):
848 os.makedirs( os.path.dirname( tgt ) )
850 pass # Some other thread must have created the folder in the meantime
852 def _copy_file(src, tgt, recursion_count = 0):
853 # Make output writeable
855 os.chmod(tgt, 493) # 0755
860 shutil.copy2(src, tgt)
861 except (IOError, os.error) as why:
864 if recursion_count < 2:
867 return _copy_file(src, tgt, recursion_count)
869 self.err_msg = "[Error] %s\n[Error] Could not perform copy %s -> %s" % (str(why), src, tgt)
873 if recursion_count < 2:
876 return _copy_file(src, tgt, recursion_count)
877 self.err_msg = "[Error] Could not perform copy %s -> %s" % (src, tgt)
883 return _copy_file(src, tgt)
885 def runnable_status(self):
886 if super(copy_outputs, self).runnable_status() == -1:
889 src = self.inputs[0].abspath()
890 tgt = self.outputs[0].abspath()
892 # If there any target file is missing, we have to copy
894 stat_tgt = os.stat(tgt)
898 # Now compare both file stats
900 stat_src = os.stat(src)
904 # same size and identical timestamps -> make no copy
905 if stat_src.st_mtime >= stat_tgt.st_mtime + 2 or stat_src.st_size != stat_tgt.st_size:
908 # Everything fine, we can skip this task
912 ###############################################################################
913 # Function to generate the copy tasks for build outputs
914 @after_method('set_pdb_flags')
915 @after_method('apply_incpaths')
916 @after_method('apply_map_file')
918 def add_install_copy(self, output_folders = None):
919 if self.bld.cmd == "msvs" or 'android' in self.bld.env['PLATFORM']:
922 if not getattr(self, 'link_task', None):
925 if self._type == 'stlib': # Do not copy static libs
928 for src in self.link_task.outputs:
929 self.copy_files(src, output_folders)
932 ###############################################################################
933 # Function to generate the EXE_VERSION_INFO defines
934 @after_method('apply_incpaths')
936 def apply_version_info(self):
937 version = str( self.bld.get_product_version() )
939 version_parts = version.split('.')
940 if len(version_parts) < 3:
941 Logs.warn('Invalid build version (%s), falling back to (1.0.0.1)' % version )
942 version_parts = ['1', '0', '0', '1']
944 version_parts[0] = version_parts[0].strip()
945 version_parts[1] = version_parts[1].strip()
946 version_parts[2] = version_parts[2].strip()
947 version_parts[3] = version_parts[3].strip()
949 for t in getattr(self, 'compiled_tasks', []):
950 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_0=' + version_parts[0])
951 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_1=' + version_parts[1])
952 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_2=' + version_parts[2])
953 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_3=' + version_parts[3])
955 ###############################################################################
956 def wrap_execute(execute_method):
958 Decorator used to set the commands that can be configured automatically
961 # Make sure to create all needed temp folders
962 bin_temp = self.get_bintemp_folder_node()
964 tmp_files_folder = bin_temp.make_node('TempFiles')
965 tmp_files_folder.mkdir()
967 # Before executing any build or configure commands, check for config file
968 # and for bootstrap updates
969 self.load_user_settings()
970 check_for_bootstrap(self)
972 return execute_method(self)
976 BuildContext.execute = wrap_execute(BuildContext.execute)
977 ConfigurationContext.execute = wrap_execute(ConfigurationContext.execute)
979 ###############################################################################
980 def check_for_bootstrap(bld):
981 global g_bootstrap_was_run
982 if g_bootstrap_was_run:
984 g_bootstrap_was_run = True
986 if not (bld.is_bootstrap_available() and bld.is_option_true('auto_run_bootstrap')):
987 return # Skip auto bootstrapping
989 bootstrap_dat = bld.path.make_node('bootstrap.dat')
990 bootstrap_timestamp = bld.get_bintemp_folder_node().make_node('bootstrap.timestamp')
992 # Check for bootstrap.dat
994 stat_bootstrap_dat = os.stat(bootstrap_dat.abspath())
996 bld.fatal('bootstrap.dat file not found')
998 # Check for bootstrap.timestamp, run bootstrap is it doesn't exist
1000 stat_bootstrap_timestamp = os.stat(bootstrap_timestamp.abspath())
1001 if stat_bootstrap_dat.st_mtime < stat_bootstrap_timestamp.st_mtime + 2:
1002 return # No need to bootstrap
1006 bld.ExecuteBootstrap()
1008 # Create TimeStamp File
1009 bootstrap_timestamp.write('')
1011 ###############################################################################
1013 def get_output_folders(self, platform, configuration, target_spec = None, game_project = None):
1015 Specifies the final binary output folder depending on the current setup
1016 <root>/<out_folder_xxx><output_folder_post_fix><output_extension_xxx>/<output_sub_folder>
1020 # Add <out_folder_xxx> part
1021 if platform == 'win_x86':
1022 path += self.options.out_folder_win32
1023 elif platform == 'win_x64':
1024 path += self.options.out_folder_win64
1025 elif platform == 'durango':
1026 path += self.options.out_folder_durango
1027 elif platform == 'orbis':
1028 path += self.options.out_folder_orbis
1029 elif platform == 'linux_x86_gcc':
1030 path += self.options.out_folder_linux32_gcc
1031 elif platform == 'linux_x86_clang':
1032 path += self.options.out_folder_linux32_clang
1033 elif platform == 'linux_x64_gcc':
1034 path += self.options.out_folder_linux64_gcc
1035 elif platform == 'linux_x64_clang':
1036 path += self.options.out_folder_linux64_clang
1037 elif platform == 'darwin_x86':
1038 path += self.options.out_folder_darwin32
1039 elif platform == 'darwin_x64':
1040 path += self.options.out_folder_darwin64
1041 elif platform == 'android_arm':
1042 path += self.options.out_folder_android
1043 elif platform == 'android_arm64':
1044 path += self.options.out_folder_android64
1046 path += 'bin/platform_unknown'
1048 # Add <output_folder_post_fix> part
1049 if not target_spec: # Fall back to command line specified spec if none was given
1050 target_spec = self.options.project_spec
1053 post_fix = self.spec_output_folder_post_fix(target_spec, platform, configuration)
1057 # Add <output_extension_xxx> part
1058 if not configuration and self.env['CONFIGURATION']:
1059 _configuration = self.env['CONFIGURATION']
1061 _configuration = configuration
1063 if _configuration == 'debug':
1064 path += self.options.output_extension_debug
1065 elif _configuration == 'profile':
1066 path += self.options.output_extension_profile
1067 elif _configuration == 'performance':
1068 path += self.options.output_extension_performance
1069 elif _configuration == 'release':
1070 path += self.options.output_extension_release
1072 path += "_config_unknown"
1074 # Add <output_sub_folder> part
1075 if self.env['output_sub_folder']:
1076 path += os.sep + os.sep.join(self.env['output_sub_folder'])
1078 # Correct handling for absolute paths
1079 if os.path.isabs(path):
1080 output_folder_nodes = [ self.root.make_node(path) ]
1081 else: # For relative path, prefix binary output folder with game project folder
1082 output_folder_nodes = []
1083 if target_spec == None:
1084 target_spec = self.options.project_spec # Fall back to global spec
1085 output_folder_nodes += [ self.path.make_node(path)]
1086 # Code below is to support multiple output folder
1087 #for project in self.active_projects(target_spec):
1088 # project_folder = self.project_output_folder(project)
1089 # output_folder_nodes += [ self.path.make_node(project_folder).make_node(path) ]
1090 # output_folder_nodes += [ self.path.make_node(path) ]
1092 return output_folder_nodes
1095 ###############################################################################
1097 def is_bootstrap_available(bld):
1098 bootstrap_path = bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.exe'
1099 return os.path.isfile(bootstrap_path)
1101 ###############################################################################
1103 def ExecuteBootstrap(bld):
1105 if not is_bootstrap_available(bld):
1108 host = Utils.unversioned_sys_platform()
1111 executable = [bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.exe']
1112 elif host == 'darwin':
1113 executable = ['python3', bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.py']
1114 elif host == 'linux':
1115 executable = ['python3', bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.py']
1116 if executable is None:
1117 bld.fatal('unknown host, could not determine bootstrap executable')
1119 bootstrap_dat = bld.path.abspath() + '/bootstrap.dat'
1121 bintmp = bld.get_bintemp_folder_node()
1122 bootstrap_digest = bintmp.make_node('.bootstrap.digest.pickle')
1124 Logs.info('Beginning Branch Bootstrap Operation (this might take a while)')
1126 ret = subprocess.call(
1128 '-d' + bootstrap_dat,
1129 '-m' + bootstrap_digest.abspath(),
1130 '--addrelevance=buildmachine'])
1132 bld.msg('branch bootstrap', 'done')
1134 bld.msg('branch bootstrap', 'failed', color='RED')
1137 from waflib.Configure import conf
1140 def read_file_list(bld, file):
1141 file_node = bld.path.make_node(file)
1143 return bld.parse_json_file(file_node)
1146 def get_platform_and_configuration(bld):
1147 # Assume that the configuration begins after the last '_'
1148 platform = "_".join( bld.variant.split("_")[:-1] )
1149 configuration = bld.variant.split("_")[-1]
1151 # Keep Backward compatibility with old names
1152 if platform == '_win32':
1153 platform = 'win_x86'
1154 if platform == '_win64':
1155 platform = 'win_x64'
1157 return (platform, configuration)
1159 @feature('link_to_output_folder')
1160 @after_method('process_source')
1161 def link_to_output_folder(self):
1163 Task Generator for tasks which generate symbolic links from the source to the dest folder
1165 return # Disabled for now
1167 if self.bld.env['PLATFORM'] == 'project_generator':
1168 return # Dont create links during project generation
1170 if sys.getwindowsversion()[0] < 6:
1171 self.bld.fatal('not supported')
1173 # Compute base relative path (from <Root> to taskgen wscript
1174 relative_base_path = self.path.path_from(self.bld.path)
1176 # TODO: Need to handle absolute path here correctly
1177 spec_name = self.bld.options.project_spec
1178 for project in self.bld.active_projects(spec_name):
1179 project_folder = self.bld.project_output_folder(project)
1180 for file in self.source:
1181 # Build output folder
1182 relativ_file_path = file.path_from(self.path)
1184 output_node = self.bld.path.make_node(project_folder)
1185 output_node = output_node.make_node(relative_base_path)
1186 output_node = output_node.make_node(relativ_file_path)
1188 path = os.path.dirname( output_node.abspath() )
1189 if not os.path.exists( path ):
1192 self.create_task('create_symbol_link', file, output_node)
1195 ###############################################################################
1196 # Class to handle copying of the final outputs into the Bin folder
1197 class create_symbol_link(Task):
1201 src = self.inputs[0].abspath()
1202 tgt = self.outputs[0].abspath()
1204 # Make output writeable
1206 os.chmod(tgt, 493) # 0755
1211 kdll = ctypes.windll.LoadLibrary("kernel32.dll")
1212 res = kdll.CreateSymbolicLinkA(tgt, src, 0)
1214 self.generator.bld.fatal("File Link Error (%s -> %s( (%s)" % (src, tgt, sys.exc_info()[0]))
1218 def runnable_status(self):
1219 if super(create_symbol_link, self).runnable_status() == -1:
1222 src = self.inputs[0].abspath()
1223 tgt = self.outputs[0].abspath()
1225 # If there any target file is missing, we have to copy
1227 stat_tgt = os.stat(tgt)
1231 # Now compare both file stats
1233 stat_src = os.stat(src)
1237 # same size and identical timestamps -> make no copy
1238 if stat_src.st_mtime >= stat_tgt.st_mtime + 2:
1241 # Everything fine, we can skip this task
1245 @feature('cxx', 'c', 'cprogram', 'cxxprogram', 'cshlib', 'cxxshlib', 'cstlib', 'cxxstlib')
1246 @after_method('apply_link')
1247 def add_compiler_dependency(self):
1248 """ Helper function to ensure each compile task depends on the compiler """
1249 if self.env['PLATFORM'] == 'project_generator':
1252 # Create nodes for compiler and linker
1253 c_compiler_node = self.bld.root.make_node( self.env['CC'] )
1254 cxx_compiler_node = self.bld.root.make_node( self.env['CXX'] )
1255 linker_node = self.bld.root.make_node( self.env['LINK'] )
1257 # Let all compile tasks depend on the compiler
1258 for t in getattr(self, 'compiled_tasks', []):
1259 if os.path.isabs( self.env['CC'] ):
1260 t.dep_nodes.append(c_compiler_node)
1261 if os.path.isabs( self.env['CXX'] ):
1262 t.dep_nodes.append(cxx_compiler_node)
1264 # Let all link tasks depend on the linker
1265 if getattr(self, 'link_task', None):
1266 if os.path.isabs( self.env['LINK'] ):
1267 self.link_task.dep_nodes.append(linker_node)
1269 ###############################################################################
1270 def show_option_dialog(ctx):
1271 ctx.gui_modify_user_options()
1273 ###############################################################################
1274 class execute_waf_options_dialog(BuildContext):
1275 ''' Util class to execute waf options dialog '''
1276 cmd = 'show_option_dialog'
1277 fun = 'show_option_dialog'
1281 # "feature 'java' does not exist - bind at least one method to it"
1282 # the java feature is added if waf detects a single 'java' file.. which is used in android
1285 def dummy_func(ctx):