!B (3DEngine) Fix crash in dedicated server due to the unavailable renderer (Approved...
[CRYENGINE.git] / wscript
blob006d4708f43d6bf676e6d5cd924e081e02e86dca
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
12 import os
13 import shutil
14 import ConfigParser  
15 import traceback
16 import time
17 import subprocess
18 import sys
20 try:
21         import _winreg
22 except:
23         pass
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'
36         
37 ############################################################################### 
38 # List of subfolders to parse   
39 SUBFOLDERS =    [
40         'Code',
41         'Engine',
42         ]
44 ###############################################################################
45 ## Configure Options for WAF    
46 def options(opt):
47         opt.load('cry_utils' ,tooldir=CRY_WAF_TOOL_DIR) 
48         
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)
52         
53         ###########################################
54         # Load support for Uber Files
55         opt.load('generate_uber_files' ,tooldir=CRY_WAF_TOOL_DIR)       
56                 
57         ###########################################
58         # Load Project Generators based on host (use the custom cry versions)
59         host = Utils.unversioned_sys_platform()
60         
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)
64         
65         if host == 'darwin':
66                 opt.load('xcode' ,tooldir=CRY_WAF_TOOL_DIR)
67         if host == 'linux':
68                 opt.load('eclipse' ,tooldir=CRY_WAF_TOOL_DIR)
69                 
70         # Load tools to improve dependency checking (by using compiler features)
71         if host == 'win32':
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)
75         
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)
81                 
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)
87                         
88         ###########################################
89         # Add custom cryengine options  
90                         
91         opt.add_option('-p', '--project-spec', dest='project_spec', action='store', default='', help='Spec to use when building the project')
92         
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)') 
103         
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)')       
109         
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)
113         
114 ###############################################################################
115 ## Configure 'configure' step
116 def configure(conf):
117         ###########################################
118         # Load base configuration function              
119         conf.load('c_config')   
120         
121         sys_platform = Utils.unversioned_sys_platform()
122         if sys_platform == 'linux':
123                 host = 'linux_x64'
124         elif sys_platform== 'win32':
125                 host = 'win_x64'
126         elif sys_platform== 'darwin':
127                 host = 'darwin_x64'             
128         
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)                               
136                                 else:
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.' )
138                         else:
139                                 try:
140                                         subprocess.call(["python3","download_sdks.py"])
141                                 except:
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()
147         
148         ###########################################
149         # Load common windows settings if needed
150         conf.load('winres', tooldir=CRY_WAF_TOOL_DIR)
151         
152         # Load common platform scripts  
153         conf.load('compile_settings_cryengine', tooldir=CRY_WAF_TOOL_DIR)       
154         
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)
158         
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)
164         
165         optional_platforms = [
166         'durango',
167         'orbis'         
168         ]       
169         
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)
174                 else:
175                         try:
176                                 conf.get_supported_platforms().remove(platform)
177                         except:
178                                 pass
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)
183         
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('cs', tooldir=CRY_WAF_TOOL_DIR)
191                 
192         #Get user defined active specs
193         specs_platforms = set() 
194         if  conf.options.specs_to_include_in_project_generation: 
195                 spec_list = conf.options.specs_to_include_in_project_generation.replace(' ', '').split(',')             
196         else:  # Special case for Buildbot which sets no spec for specs_to_include_in_project_generation.  
197                 spec_list = conf.loaded_specs() 
198                                 
199         # Get the platform list of all active specs
200         tmp_specs_platforms = set()
201         for spec in spec_list:
202                 for platform in conf.get_supported_platforms():
203                         tmp_specs_platforms.update(conf.spec_valid_platforms(spec, platform))
204                                 
205         # Convert to complete platform names i.e. win -> win_x64 and win_x86
206         for valid_platform in tmp_specs_platforms:
207                 for value in conf.get_platform_permutation_map(valid_platform).values():
208                         specs_platforms.update(value)
209                                 
210         ###########################################
211         # Load settings for all platforms
212         platforms_done = ''
213         installed_platforms = []
214         for platform in conf.get_supported_platforms():
215         
216                 # Check if platform is required for active specs
217                 if platform not in specs_platforms: 
218                         continue
220                 Logs.info('[INFO] Configure "%s - [%s]"' % (platform, ', '.join(conf.get_supported_configurations())))
221                 
222                 # Load Platform Configuration (except cppcheck as it is not a *real* compiler)
223                 if platform != 'cppcheck':
224                         file_name = 'compile_rules_' + host + '_' + platform
225                         if os.path.isfile(os.path.join(conf.path.abspath(), CRY_WAF_TOOL_DIR, file_name + '.py')):
226                                 conf.load('compile_rules_' + host + '_' + platform, tooldir=CRY_WAF_TOOL_DIR)
227                                 
228                                 # Use the specialized function to load platform specifics
229                                 function_name = 'check_%s_%s_installed' % ( host, platform )
230                                 if not hasattr(conf, function_name):
231                                         conf.fatal('[ERROR] Required function \'%s\' not found' % function_name )
233                                 # Try to load the function                      
234                                 if not getattr(conf, function_name)():
235                                         Logs.warn('[WARNING] - Unable to locate platform "%s" on system. Disabling platform support for: %s !!!' % (platform, platform))
236                                         continue
237                         else:
238                                 Logs.warn('[WARNING] - Unable to locate compiler rules  "%s". Disabling platform support for: %s !!!. ' % (file_name, platform))
239                                 continue
240                 
241                 # platform installed
242                 installed_platforms.append(platform)
243                 
244                 for configuration in conf.get_supported_configurations():
245                         conf.setenv(platform + '_' + configuration) # change env to configuration env
246                         conf.init_compiler_settings()
247                         
248                         # Use the specialized function to load platform specifics
249                         function_name = 'load_%s_%s_%s_settings' % ( configuration, host, platform )
250                         if not hasattr(conf, function_name):
251                                 conf.fatal('[ERROR] Required function \'%s\' not found' % function_name )
253                         # Try to load the function                      
254                         getattr(conf, function_name)()
255                         
256                         conf.configure_qt()
258                         # Load swig and mono
259                         conf.configure_swig()
260                         conf.configure_mono()
261                         conf.env['build_relevant_platforms'] = installed_platforms
262                         conf.setenv('') # change env back to global env
264         conf.set_supported_platforms(installed_platforms)
265         conf.env['build_relevant_platforms'] = installed_platforms # need to store in global cache as well for  project_generators
267         ###########################################     
268         # Load support for c and cxx compiler
269         conf.load('c')
270         conf.load('cxx')
271                 
272         conf.load('protoc',tooldir=CRY_WAF_TOOL_DIR)
273         
274         ###########################################
275         # Load Platform specific helpers based on host
276         host = Utils.unversioned_sys_platform() 
277         if host == 'darwin':
278                 conf.load('c_osx')
279         
280         ###########################################
281         # Create VS-Projects automatically during configure when running on windows
282         if host == 'win32' and conf.is_option_true('generate_vs_projects_automatically'):
283                 # Workflow improvement: for all builds generate projects after the build
284                 # except when using the default build target 'utilities' then do it before
285                 if 'build' in Options.commands:
286                         build_cmd_idx = Options.commands.index('build')         
287                         Options.commands.insert(build_cmd_idx, 'msvs') 
288                 else:
289                         Options.commands.append('msvs')
290          
291         ###########################################
292         # Load remaining tools for correct auto configure
293         if host == 'win32':
294                 conf.load('recode', tooldir=CRY_WAF_TOOL_DIR)   
295                 conf.load('static_code_analyzer', tooldir=CRY_WAF_TOOL_DIR)
296         
297         ###########################################
298         # Recurse into subfolders for auto conf when any wscript changes
299         conf.recurse(dirs=SUBFOLDERS, mandatory=False)
300                 
301         # Load Game Specific parts      
302         for project in conf.game_projects():
303                 conf.game_project = project
304                 conf.recurse( conf.game_code_folder(project))
306         ###########################################
307         # Always update uber files during configuration
308         # But don't add the same command twice
309         if len(Options.commands) == 0:
310                 Options.commands.insert(0, 'generate_uber_files')
311         elif not Options.commands[0] == 'generate_uber_files':
312                 Options.commands.insert(0, 'generate_uber_files')
314         # Remove timestamp files to force builds of generate_uber_files and project gen even if
315         # some command after configure failes           
316         try:
317                 generate_uber_files_timestamp = conf.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')       
318                 os.stat(generate_uber_files_timestamp.abspath())
319         except OSError: 
320                 pass
321         else:
322                 generate_uber_files_timestamp.delete()
323         
324         try:
325                 project_gen_timestamp = conf.get_bintemp_folder_node().make_node('project_gen.timestamp')
326                 os.stat(project_gen_timestamp.abspath())
327         except OSError: 
328                 pass
329         else:
330                 project_gen_timestamp.delete()
332 def post_command_exec(bld):     
333         # [post project gen]
334         if bld.cmd == 'msvs':
335                 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
336                 project_gen_timestamp.write('')
337         # [post uberfile gen]
338         elif bld.cmd == 'generate_uber_files':
339                 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
340                 generate_uber_files_timestamp.write('')
341         # [post build]
342         elif bld.cmd.startswith('build'):
343                 for message in bld.post_build_msg_info:
344                         Logs.info(message)
345                         
346                 for message in bld.post_build_msg_warning:
347                         Logs.warn(message)
348                         
349                 for message in bld.post_build_msg_error:
350                         Logs.error(message)
351                         
352 stored_file_filter = ''
353 stored_output_file = ''
355 ###############################################################################
356 ## Run 'build' step     
357 def build(bld):
359         # Keep backward compatibility
360         if bld.options.project_spec == 'everything':    
361                 bld.options.project_spec = 'trybuilder'
362                 
363         bld.options.project_spec = bld.options.project_spec.strip() # remove spaces 
364         
365         # Create a post build message container
366         bld.post_build_msg_info = []
367         bld.post_build_msg_warning = []
368         bld.post_build_msg_error = []   
369         
370         # Add groups to ensure all task generators who create static lib tasks are executd first
371         bld.add_group('generate_static_lib')
372         bld.add_group('regular_group')
373         # Set back to our regular compile group
374         bld.set_group('regular_group')
375         
376         
377         ###########################################
378         # Load common windows settings if needed
379         bld.load('winres', tooldir=CRY_WAF_TOOL_DIR)
380                 
381         # Check if a valid spec is defined      
382         if bld.cmd != 'generate_uber_files' and bld.cmd != 'msvs' and bld.cmd != 'eclipse':
383                 # project spec is a mandatory parameter
384                 if bld.options.project_spec == '':
385                         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())
386                 
387                 # Ensure that the selected spec is supported on this platform
388                 if not bld.options.project_spec in bld.loaded_specs():
389                         bld.fatal('[ERROR] Invalid Project Spec (%s) defined, valid specs are %s' % (bld.options.project_spec, bld.loaded_specs()))
390         
391                 # Check for valid single file compilation flag pairs
392                 if bld.is_option_true('show_preprocessed_file') and bld.options.file_filter == "":  
393                         bld.fatal('--show-preprocessed-file can only be used in conjunction with --file-filter')
394                 elif bld.is_option_true('show_disassembly') and bld.options.file_filter == "":  
395                         bld.fatal('--show-disassembly can only be used in conjunction with --file-filter')    
396                                         
397         ###########################################
398         # Check timestamps for special commands         
399         if not 'generate_uber_files' in Options.commands and bld.cmd != 'generate_uber_files':
400                 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
401                 try:
402                         os.stat(generate_uber_files_timestamp.abspath())
403                 except OSError: 
404                         # No generate_uber file timestamp, restart command chain, prefixed with gen uber files cmd
405                         Options.commands = ['generate_uber_files'] + [bld.cmd] + Options.commands       
406                         return
407         
408         if not 'msvs' in Options.commands and bld.cmd != 'msvs':
409                 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
410                 try:
411                         os.stat(project_gen_timestamp.abspath())
412                 except OSError: 
413                         # No project_gen timestamp, append project_gen to commands
414                         if bld.is_option_true('generate_vs_projects_automatically') and  Utils.unversioned_sys_platform() == 'win32':
415                                 if not bld.is_option_true('internal_dont_check_recursive_execution'):
416                                         Options.commands = Options.commands + ['msvs']
417                                         
418         ###########################################
419         # Check for valid variant if we are not generating projects     
420         if bld.cmd == 'xcode' or bld.cmd == 'msvs' or bld.cmd == 'eclipse' or bld.cmd == 'generate_uber_files':
421                 bld.env['PLATFORM'] = 'project_generator'
422                 bld.env['CONFIGURATION'] = 'project_generator'
423         else:
424                 if not bld.variant:
425                         bld.fatal('please use a valid build configuration, try "waf --help"')                   
426                 
427                 (platform, configuration) = bld.get_platform_and_configuration()                        
428                 bld.env['PLATFORM']                     = platform
429                 bld.env['CONFIGURATION']        = configuration
430                 
431                 if  not platform in bld.get_supported_platforms():
432                         bld.fatal( '[ERROR] - Target platform "%s" not supported. [on host platform: %s]' % (platform, Utils.unversioned_sys_platform()) )
433                 
434                 # When building, only support platform that we are currently building for to reduce internal workload
435                 bld.set_supported_platforms([bld.env['PLATFORM']])
436                 
437                 # check if configuration is valid in the current spec scope
438                 bVariantValid = False
439                 for conf in bld.spec_valid_configurations():
440                         if conf == configuration:
441                                 bVariantValid = True
442                 if not bVariantValid:
443                         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())))
444                 
445                 # check if platform is valid in the current spec scope
446                 bVariantValid = False
447                 for p0 in bld.spec_valid_platforms():
448                         for p1 in bld.get_platform_list(bld.env['PLATFORM']):
449                                 if p0 == p1:
450                                         bVariantValid = True
451                 if not bVariantValid:
452                         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())))
454                 # Ensure that, if specified, target is supported in this spec
455                 if bld.options.targets:
456                         for target in bld.options.targets.split(','):
457                                 if not target in  bld.spec_modules():
458                                         bld.fatal('[ERROR] Module "%s" is not configurated to be build in spec "%s" in "%s|%s"' % (target, bld.options.project_spec, platform, configuration))
459                                         
460         ###########################################
461         # Setup command coordinator
462         bld.load('cmd_coordinator', tooldir=CRY_WAF_TOOL_DIR)
463         bld.setup_command_coordinator()
464          
465         # We might run multiple instances of WAF at the same time
466         # 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
467         # 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)
468         if not bld.instance_is_build_master():
469                 return
471         ###########################################
472         bld.add_post_fun(post_command_exec)
473         
474         ###########################################
475         # Setup Output Path for Project Geneators
476         bld.solution_name = bld.get_solution_name()
477         bld.projects_dir = bld.get_project_output_folder()      
478                                         
479         ###########################################
480         # Load configuration overwrites
481         bld.env['CONFIG_OVERWRITES'] = bld.get_solution_overrides()
483         ###########################################
484         # Disable optimizations if requested
485         # Must be done after computing overrides
486         if bld.is_option_true('force_deoptimized_builds'):
487                 bld.env['CFLAGS'] += bld.env['COMPILER_FLAGS_DisableOptimization']
488                 bld.env['CXXFLAGS'] += bld.env['COMPILER_FLAGS_DisableOptimization']
489                 
490         ###########################################
491         # Load Support for Recode if required
492         host = Utils.unversioned_sys_platform() 
493         if host == 'win32':
494                 bld.load('recode', tooldir=CRY_WAF_TOOL_DIR)                    
495                 if str(bld.options.support_recode) == str(True):
496                         bld.enable_recode = bld.check_global_recode_enabled()
497                         
498         ###########################################
499         # Add support for static code analyzer
500         bld.load('static_code_analyzer', tooldir=CRY_WAF_TOOL_DIR)              
501                 
502         # Clear <output folder>/Image/Loose on durango as a workaround for buggy deploy
503         if bld.env['PLATFORM'] == 'durango':
504                 for node in bld.get_output_folders(bld.env['PLATFORM'], bld.env['CONFIGURATION']):
505                         image_lost_dir = node.find_dir('Image/Loose')
506                         if image_lost_dir:
507                                 files = image_lost_dir.ant_glob('**/*')
508                                 for f in files:
509                                         f.chmod(493) # 0755
510                                         f.delete()
512         # Load Core Engine Parts (Engine, Tools, Core Shaders etc)
513         bld.game_project = None
514         bld.recurse(SUBFOLDERS, mandatory=False)        
515         
516         # Load Game Specific parts      
517         for project in bld.game_projects():
518                 bld.game_project = project
519                 bld.recurse( bld.game_code_folder(project))
520                 
521 ###############################################################################
522 @conf
523 def target_clean(self):
524         
525         tmp_targets = self.options.targets[:]
526         to_delete = []
527         # Sort of recursive algorithm, find all outputs of targets
528         # Repeat if new targets were added due to use directives
529         while len(tmp_targets) > 0:
530                 new_targets = []
531                 
532                 for tgen in self.get_all_task_gen():                            
533                         tgen.post()
534                         if not tgen.target in tmp_targets:
535                                 continue
536                 
537                         for t in tgen.tasks:
538                                 # Collect outputs
539                                 for n in t.outputs:
540                                         if n.is_child_of(self.bldnode):
541                                                 to_delete.append(n)
542                         # Check for use flag
543                         if hasattr(tgen, 'use'):                        
544                                 new_targets.append(tgen.use)
545                 # Copy new targets
546                 tmp_targets = new_targets[:]
547                 
548         # Final File list to delete
549         to_delete = list(set(to_delete))
550         for file in to_delete:
551                 file.delete()
552         
553                 
554 ###############################################################################
555 # Copy pasted from MSVS..
556 def convert_vs_configuration_to_waf_configuration(configuration):
557         if 'Debug' in configuration:
558                 return 'debug'
559         if 'Profile' in configuration:
560                 return 'profile'
561         if 'Release' in configuration:
562                 return 'release'
563         if 'Performance' in configuration:
564                 return 'performance'
565                         
566         return ''
567                 
568 ###############################################################################         
569 @conf
570 def utilities(bld):             
571         if not bld.is_option_true('ask_for_user_input'):        
572                 return
573                 
574         # Function to handle creating of new libraries
575         def install_waf_addin():
576                 enviroment = os.environ.copy()          
577                 if not 'APPDATA' in enviroment:
578                         print 'APPDATA enviroment variable not found, WAF cannot figure out where to install the addin'
579                         return
580                 
581                 install_path = enviroment['APPDATA'] + '\\Microsoft\\MSEnvShared\\Addins'
582                 source_path = bld.path.abspath() + '\\Code\\Tools\\waf_addin\\Bin_Release'
583                 
584                 Logs.info('WAF Addin will be installed into:\n%s' % install_path)               
585                 bld.start_msg('Create Install directory')
586                 try:
587                         if not os.path.exists(install_path):
588                                 os.makedirs(install_path)
589                 except:
590                         bld.end_msg('failed (%s)' % sys.exc_info()[1], color='RED')
591                 else:
592                         bld.end_msg('ok')
593                         
594                 for file in ['WAF_Addin.AddIn', 'WAF_Addin.dll', 'WAF_Addin.xml']:
595                         bld.start_msg('Copy %s to install directory' % file)
596                         # Make output writeable
597                         try:
598                                 os.chmod(install_path + '\\' + file, 493) # 0755
599                         except:
600                                 pass
601                                 
602                         try:
603                                 shutil.copy2(source_path + '\\' + file, install_path + '\\' + file)
604                         except:
605                                 bld.end_msg('failed (%s)' % sys.exc_info()[1], color='RED')
606                         else:
607                                 bld.end_msg('ok')
608                 Options.commands.insert(0, 'utilities')
609                 
610         def reconfigure_waf():
611                 Options.commands.insert(0, 'configure')
612                 
613         def bootstrap_update():
614                 bld.ExecuteBootstrap('update')
615                 Options.commands.insert(0, 'utilities')
616         
617         def regenerate_vs_solution():
618                 Options.commands.insert(0, 'utilities')
619                 Options.commands.insert(0, 'msvs')
620         
621         def regenerate_uber_files():
622                 Options.commands.insert(0, 'utilities')
623                 Options.commands.insert(0, 'generate_uber_files')
624                                 
625         def regenerate_config_file():
626                 print type(bld)
627                 # Remove current config file and remove some state
628                 config_file = bld.get_user_settings_node()
629                 config_file.delete()
631                 # Load user settings            
632                 bld.load_user_settings()
633                 
634                 # Afterwards return to utilies dialog
635                 Options.commands.insert(0, 'utilities')
636                 
637         def apply_new_crycommon_layout_to_custom_folder():
638                  folder = raw_input('Please enter absolute folder path: ')
639                  if os.path.isdir(folder):
640                         bld.convert_files_in_folder_to_cry_common([bld.root.make_node(folder)])
641                  else:
642                         Logs.error("Folder could not be found.", folder)                        
643                  Options.commands.insert(0, 'utilities')                
644                                                 
645         def waf_options():                              
646                 Options.commands.insert(0, 'utilities')
647                 Options.commands.insert(0, 'show_option_dialog')
648                 
649         # Function to handle exit requests
650         def exit():
651                 pass
652                                 
653         if not bld.is_option_true('console_mode'):
654                 conversion_file = ['Apply new CryCommon layout', [ 
655                         ('Custom folder and subfolders', apply_new_crycommon_layout_to_custom_folder) 
656                 ]]              
657                 
658                 menu_file = ['', [              
659                         ('Exit', exit) 
660                 ]]
661                 
662                 menu_regenerate = ['Generate', [ 
663                         ("Visual Studio Solution", regenerate_vs_solution),
664                         ("Uber Files", regenerate_uber_files)
665                  ]]
666                 
667                 menu_tools = ['Tools', [
668                         ("Options", waf_options),
669                         ("Configure", reconfigure_waf)
670                 ]]
671                 
672                 if bld.is_bootstrap_available():
673                         menu_tools[1] += [("Run Bootstrap", bootstrap_update)]
674                         
675                 #menu_tools[1] += [("Install WAF Addin", install_waf_addin)] # disabled as unmaintained         
676                 fn_result = bld.gui_get_waf_menu_choice([menu_tools, menu_regenerate,  conversion_file, menu_file])
677                 
678                 
679                 if fn_result:
680                         fn_result()      # Execute result
681                 else:
682                         Options.commands.insert(0, 'utilities')
683                         
684         else:                   
685                 choices = [ 
686                     (exit, 'Exit'),
687                                 #(install_waf_addin,Install WAF Addin), # disabled as unmaintained
688                                 (regenerate_vs_solution, 'Regenerate Visual Studio Solution'),                          
689                                 (regenerate_uber_files, 'Regenerate Uber Files'),
690                                 (regenerate_config_file, 'Regenerate Config File')
691                         ]
692                         
693                 if bld.is_bootstrap_available():
694                         choices += [(bootstrap_update, 'Run Bootstrap Update')]
695                         
696                 print(' === Crytek WAF Build System === ')
697                 print(' Please use waf --help to see all valid build targets and build options')
698                 
699                 for idx, option in enumerate(choices):
700                         print('%s: %s' % (idx, option[1]))
701                 
702                 user_input = raw_input('Please select an option: ')
703                 print('')
704                 # Sanity check for valid user inputs
705                 try:                                    
706                         if int(user_input) < int(0) or int(user_input) > len(choices):
707                                 raise                   
708                 except:
709                         print('Invalid Input, please choose a value between 1 and %s', len(choices))    
710                         Options.commands.insert(0, 'utilities')
711                         return
712                 
713                 # Input is fine, exectue selection
714                 if int(user_input) == 1:
715                         return
716                 
717                 print type(bld)
718                 choices[int(user_input)][0]()
720 ##############################################################################
721 class execute_utilities(BuildContext):
722         ''' Util class to execute waf utilities dialog  '''
723         cmd = 'utilities'
724         fun = 'utilities'
725         
726 ###############################################################################
727 # Create Build Context Commands for multiple platforms/configurations
728 for platform in PLATFORMS[Utils.unversioned_sys_platform()]:
729         for configuration in CONFIGURATIONS:
730                 # Create new class to execute build with variant
731                 name = CleanContext.__name__.replace('Context','').lower()
732                 class tmp_clean(CleanContext):
733                         cmd = name + '_' + platform + '_' + configuration
734                         variant = platform + '_' + configuration
735                         
736                         def __init__(self, **kw):
737                                 super(CleanContext, self).__init__(**kw)
738                                 self.top_dir = kw.get('top_dir', Context.top_dir)
739                                 
740                         def execute(self):
741                                 if Configure.autoconfig:
742                                         env = ConfigSet.ConfigSet()
743                                         
744                                         do_config = False
745                                         try:
746                                                 env.load(os.path.join(Context.lock_dir, Options.lockfile))
747                                         except Exception:
748                                                 Logs.warn('Configuring the project')
749                                                 do_config = True
750                                         else:
751                                                 if env.run_dir != Context.run_dir:
752                                                         do_config = True
753                                                 else:
754                                                         h = 0
755                                                         for f in env['files']:
756                                                                 try:                                            
757                                                                         h = hash((h, Utils.readf(f, 'rb')))
758                                                                 except (IOError, EOFError):
759                                                                         pass # ignore missing files (will cause a rerun cause of the changed hash)                                                                                                                      
760                                                         do_config = h != env.hash
762                                         if do_config:
763                                                 Options.commands.insert(0, self.cmd)
764                                                 Options.commands.insert(0, 'configure')
765                                                 return
767                                 # Execute custom clear command
768                                 self.restore()
769                                 if not self.all_envs:
770                                         self.load_envs()
771                                 self.recurse([self.run_dir])
772         
773                                 if self.options.targets:
774                                         self.target_clean()
775                                 else:
776                                         try:
777                                                 self.clean()
778                                         finally:
779                                                 self.store()
780                                 
781                         
782                 # Create new class to execute clean with variant
783                 name = BuildContext.__name__.replace('Context','').lower()
784                 class tmp_build(BuildContext):
785                         cmd = name + '_' + platform + '_' + configuration
786                         variant = platform + '_' + configuration                                
787         
788 ###############################################################################
789 # Install Default variant                               
790 for y in (BuildContext, CleanContext):
791         class tmp(y):
792                 variant = 'utilities'
793                 fun = 'utilities'
794                 
795 ###############################################################################
796 # Create Commands for backwards compatibility
797 if Utils.unversioned_sys_platform() == 'win32': 
798                 for configuration in CONFIGURATIONS:
799                         for context in (BuildContext, CleanContext):
800                                 name = context.__name__.replace('Context','').lower()
801                                 class tmp(context):
802                                         cmd = name + '_win32_' + configuration
803                                         variant = 'win_x86_' + configuration
804                 for configuration in CONFIGURATIONS:
805                         for context in (BuildContext, CleanContext):
806                                 name = context.__name__.replace('Context','').lower()
807                                 class tmp(context):
808                                         cmd = name + '_win64_' + configuration
809                                         variant = 'win_x64_' + configuration
812 @taskgen_method
813 def copy_files(self, source_file, output_folders = None):
815         if not output_folders:
816                 output_folders = self.bld.get_output_folders(self.bld.env['PLATFORM'], self.bld.env['CONFIGURATION'])
818         for node in output_folders:
819                 output_node = node
820                 if hasattr(self, 'output_sub_folder'):
821                         _output_sub_folder = str(self.output_sub_folder)
822                         if os.path.isabs(_output_sub_folder):
823                                 output_node = self.bld.root.make_node(_output_sub_folder)
824                         else:
825                                 output_node = output_node.make_node(_output_sub_folder)
826                 output_node = output_node.make_node( os.path.basename(source_file.abspath()) )
828                 path = os.path.dirname( output_node.abspath() )
829                 if not os.path.exists( path ):
830                         os.makedirs( path )
832                 self.create_task('copy_outputs', source_file, output_node) 
833         
834 ###############################################################################
835 # Class to handle copying of the final outputs into the Bin folder
836 class copy_outputs(Task):
837         color = 'YELLOW'
838         
839         def run(self):
840                 src = self.inputs[0].abspath()
841                 tgt = self.outputs[0].abspath()
842                 
843                 # Create output folder
844                 if not os.path.exists( os.path.dirname( tgt ) ):
845                         try:
846                                 os.makedirs( os.path.dirname( tgt ) )
847                         except:
848                                 pass # Some other thread must have created the folder in the meantime
849                         
850                 def _copy_file(src, tgt, recursion_count = 0):
851                         # Make output writeable
852                         try:
853                                 os.chmod(tgt, 493) # 0755
854                         except:
855                                 pass
856                                 
857                         try:
858                                 shutil.copy2(src, tgt)
859                         except (IOError, os.error) as why:
860                         
861                                 # Try again
862                                 if recursion_count < 2:
863                                         time.sleep(1)
864                                         recursion_count += 1
865                                         return _copy_file(src, tgt, recursion_count)
866                                         
867                                 self.err_msg = "[Error] %s\n[Error] Could not perform copy %s -> %s" % (str(why), src, tgt)
868                                 return -1
869                         except:
870                                 # Try again
871                                 if recursion_count < 2:
872                                         time.sleep(1)
873                                         recursion_count += 1
874                                         return _copy_file(src, tgt, recursion_count)                                    
875                                 self.err_msg = "[Error] Could not perform copy %s -> %s" % (src, tgt)
876                                 return -1
877                                 
878                         return 0
880                 # Copy file
881                 return _copy_file(src, tgt)
882                 
883         def runnable_status(self):
884                 if super(copy_outputs, self).runnable_status() == -1:
885                         return -1
886                 
887                 src = self.inputs[0].abspath()
888                 tgt = self.outputs[0].abspath()
890                 # If there any target file is missing, we have to copy
891                 try:
892                         stat_tgt = os.stat(tgt)
893                 except OSError: 
894                         return RUN_ME
895                 
896                 # Now compare both file stats
897                 try:
898                         stat_src = os.stat(src)                         
899                 except OSError:
900                         pass
901                 else:
902                         # same size and identical timestamps -> make no copy
903                         if stat_src.st_mtime >= stat_tgt.st_mtime + 2 or stat_src.st_size != stat_tgt.st_size:                  
904                                 return RUN_ME
905                                 
906                 # Everything fine, we can skip this task                
907                 return -2 # SKIP_ME
908                 
909         
910 ###############################################################################
911 # Function to generate the copy tasks for build outputs 
912 @after_method('set_pdb_flags')
913 @after_method('apply_incpaths')
914 @after_method('apply_map_file')
915 @feature('c', 'cxx')
916 def add_install_copy(self, output_folders = None):
917         if self.bld.cmd == "msvs":
918                 return
920         if not getattr(self, 'link_task', None):
921                 return
923         if self._type == 'stlib': # Do not copy static libs
924                 return
925                 
926         for src in self.link_task.outputs:              
927                 self.copy_files(src, output_folders)
929         
930 ###############################################################################
931 # Function to generate the EXE_VERSION_INFO defines
932 @after_method('apply_incpaths')
933 @feature('c', 'cxx')
934 def apply_version_info(self):
935         version = str( self.bld.get_product_version() )
937         version_parts = version.split('.')
938         if len(version_parts) < 3:
939                 Logs.warn('Invalid build version (%s), falling back to (1.0.0.1)' % version )                                           
940                 version_parts = ['1', '0', '0', '1']
942         version_parts[0] = version_parts[0].strip()
943         version_parts[1] = version_parts[1].strip()
944         version_parts[2] = version_parts[2].strip()
945         version_parts[3] = version_parts[3].strip()
947         for t in getattr(self, 'compiled_tasks', []):
948                 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_0=' + version_parts[0])
949                 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_1=' + version_parts[1])
950                 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_2=' + version_parts[2])
951                 t.env.append_value('DEFINES', 'EXE_VERSION_INFO_3=' + version_parts[3])
953 ############################################################################### 
954 def wrap_execute(execute_method):
955         """
956         Decorator used to set the commands that can be configured automatically
957         """
958         def execute(self):              
959                 # Make sure to create all needed temp folders
960                 bin_temp = self.get_bintemp_folder_node()
961                 bin_temp.mkdir()
962                 tmp_files_folder = bin_temp.make_node('TempFiles')
963                 tmp_files_folder.mkdir()
964                 
965                 # Before executing any build or configure commands, check for config file
966                 # and for bootstrap updates
967                 self.load_user_settings()
968                 check_for_bootstrap(self)
969         
970                 return execute_method(self)
971                         
972         return execute
973         
974 BuildContext.execute = wrap_execute(BuildContext.execute)
975 ConfigurationContext.execute = wrap_execute(ConfigurationContext.execute)
977 ############################################################################### 
978 def check_for_bootstrap(bld):
979         global g_bootstrap_was_run
980         if g_bootstrap_was_run:
981                 return
982         g_bootstrap_was_run = True      
983         
984         if not (bld.is_bootstrap_available() and bld.is_option_true('auto_run_bootstrap')):
985                 return # Skip auto bootstrapping
986                 
987         bootstrap_dat = bld.path.make_node('bootstrap.dat')
988         bootstrap_timestamp = bld.get_bintemp_folder_node().make_node('bootstrap.timestamp')
989         
990         # Check for bootstrap.dat
991         try:
992                 stat_bootstrap_dat = os.stat(bootstrap_dat.abspath())
993         except OSError: 
994                 bld.fatal('bootstrap.dat file not found')
995         
996         # Check for bootstrap.timestamp, run bootstrap is it doesn't exist
997         try:
998                 stat_bootstrap_timestamp = os.stat(bootstrap_timestamp.abspath())
999                 if stat_bootstrap_dat.st_mtime < stat_bootstrap_timestamp.st_mtime + 2:
1000                         return # No need to bootstrap
1001         except OSError:                         
1002                 pass
1003                 
1004         bld.ExecuteBootstrap('update')
1005         
1006         # Create TimeStamp File
1007         bootstrap_timestamp.write('')
1009 ############################################################################### 
1010 @conf
1011 def get_output_folders(self, platform, configuration, target_spec = None, game_project = None):
1012         """
1013         Specifies the final binary output folder depending on the current setup
1014         <root>/<out_folder_xxx><output_folder_post_fix><output_extension_xxx>/<output_sub_folder>
1015         """
1016         path = ""
1017                 
1018         # Add <out_folder_xxx> part
1019         if platform == 'win_x86':
1020                 path += self.options.out_folder_win32           
1021         elif platform == 'win_x64':
1022                 path += self.options.out_folder_win64           
1023         elif platform == 'durango':             
1024                 path += self.options.out_folder_durango
1025         elif platform == 'orbis':
1026                 path += self.options.out_folder_orbis   
1027         elif platform == 'linux_x86_gcc':
1028                 path += self.options.out_folder_linux32_gcc
1029         elif platform == 'linux_x86_clang':
1030                 path += self.options.out_folder_linux32_clang   
1031         elif platform == 'linux_x64_gcc':
1032                 path += self.options.out_folder_linux64_gcc
1033         elif platform == 'linux_x64_clang':
1034                 path += self.options.out_folder_linux64_clang
1035         elif platform == 'darwin_x86':
1036                 path += self.options.out_folder_darwin32
1037         elif platform == 'darwin_x64':
1038                 path += self.options.out_folder_darwin64
1039         elif platform == 'android_arm_gcc':
1040                 path += self.options.out_folder_android         
1041         elif platform == 'android_arm_clang':
1042                 path += self.options.out_folder_android         
1043         else:
1044                 path += 'bin/platform_unknown'
1045                                 
1046         # Add <output_folder_post_fix> part
1047         if not target_spec: # Fall back to command line specified spec if none was given
1048                 target_spec = self.options.project_spec
1049                 
1050         if target_spec:
1051                 post_fix = self.spec_output_folder_post_fix(target_spec, platform, configuration)
1052                 if post_fix:
1053                         path += post_fix
1054                         
1055         # Add <output_extension_xxx> part
1056         if not configuration and self.env['CONFIGURATION']:
1057                 _configuration = self.env['CONFIGURATION']
1058         else:
1059                 _configuration = configuration
1061         if _configuration == 'debug':        
1062                 path += self.options.output_extension_debug             
1063         elif _configuration == 'profile':
1064                 path += self.options.output_extension_profile           
1065         elif _configuration == 'performance':
1066                 path += self.options.output_extension_performance
1067         elif _configuration == 'release':
1068                 path += self.options.output_extension_release
1069         else:
1070                 path += "_config_unknown"
1071         
1072         # Add <output_sub_folder> part
1073         if self.env['output_sub_folder']:               
1074                 path += os.sep + os.sep.join(self.env['output_sub_folder'])                     
1076         # Correct handling for absolute paths
1077         if os.path.isabs(path):
1078                 output_folder_nodes = [ self.root.make_node(path) ]
1079         else: # For relative path, prefix binary output folder with game project folder
1080                 output_folder_nodes = []
1081                 if target_spec == None:
1082                         target_spec = self.options.project_spec # Fall back to global spec
1083                 output_folder_nodes += [ self.path.make_node(path)]     
1084                 # Code below is to support multiple output folder
1085                 #for project in self.active_projects(target_spec):
1086                 #       project_folder = self.project_output_folder(project)
1087                 #       output_folder_nodes += [ self.path.make_node(project_folder).make_node(path) ]
1088                 #       output_folder_nodes += [ self.path.make_node(path)      ]
1090         return output_folder_nodes
1092         
1093 ###############################################################################
1094 @conf
1095 def is_bootstrap_available(bld):
1096         bootstrap_path = bld.path.abspath() + '/Tools/branch_bootstrap/dist/bootstrap.exe'
1097         return os.path.isfile(bootstrap_path)
1098         
1099 ############################################################################### 
1100 @conf
1101 def ExecuteBootstrap(bld, command):
1103         if not is_bootstrap_available(bld):
1104                 return
1105                 
1106         host = Utils.unversioned_sys_platform()
1107         executable = None
1108         if host == 'win32':
1109                 executable = [bld.path.abspath() + '/Tools/branch_bootstrap/dist/bootstrap.exe']
1110         elif host == 'darwin':
1111                 executable = ['python3', bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.py']
1112         elif host == 'linux':
1113                 executable = ['python3', bld.path.abspath() + '/Tools/branch_bootstrap/bootstrap.py']
1114         if executable is None:
1115                 bld.fatal('unknown host, could not determine bootstrap executable')
1116         
1117         bootstrap_dat   = bld.path.abspath() + '/bootstrap.dat'
1118         
1119         bintmp = bld.get_bintemp_folder_node()
1120         bootstrap_digest = bintmp.make_node('.bootstrap.digest.pickle')
1121                         
1122         Logs.info('Beginning Branch Bootstrap Operation (this might take a while)')
1123         
1124         ret = subprocess.call(
1125                 executable + [
1126                         '-d' + bootstrap_dat,
1127                         '-m' + bootstrap_digest.abspath(), command])
1128         if ret == 0:
1129                 bld.msg('branch bootstrap', 'done')
1130         else:
1131                 bld.msg('branch bootstrap', 'failed', color='RED')
1132                 sys.exit(1)     
1133                 
1134 from waflib.Configure import conf
1135         
1136 @conf
1137 def read_file_list(bld, file):
1138         file_node = bld.path.make_node(file)    
1140         return bld.parse_json_file(file_node)
1141         
1142 @conf
1143 def get_platform_and_configuration(bld):
1144         # Assume that the configuration begins after the last '_'
1145         platform =  "_".join( bld.variant.split("_")[:-1] )
1146         configuration = bld.variant.split("_")[-1]
1147         
1148         # Keep Backward compatibility with old names
1149         if platform == '_win32':
1150                 platform = 'win_x86'
1151         if platform == '_win64':
1152                 platform = 'win_x64'
1154         return (platform, configuration)
1156 @feature('link_to_output_folder')
1157 @after_method('process_source')
1158 def link_to_output_folder(self):
1159         """
1160         Task Generator for tasks which generate symbolic links from the source to the dest folder
1161         """
1162         return # Disabled for now
1163         
1164         if self.bld.env['PLATFORM'] == 'project_generator':
1165                 return # Dont create links during project generation
1167         if sys.getwindowsversion()[0] < 6:
1168                 self.bld.fatal('not supported')
1169                 
1170         # Compute base relative path (from <Root> to taskgen wscript
1171         relative_base_path = self.path.path_from(self.bld.path)
1172         
1173         # TODO: Need to handle absolute path here correctly
1174         spec_name = self.bld.options.project_spec                       
1175         for project in self.bld.active_projects(spec_name):
1176                 project_folder = self.bld.project_output_folder(project)
1177                 for file in self.source:
1178                         # Build output folder
1179                         relativ_file_path = file.path_from(self.path)
1180                         
1181                         output_node = self.bld.path.make_node(project_folder)
1182                         output_node = output_node.make_node(relative_base_path)
1183                         output_node = output_node.make_node(relativ_file_path)
1184                         
1185                         path = os.path.dirname( output_node.abspath() )
1186                         if not os.path.exists( path ):
1187                                 os.makedirs( path )
1188                         
1189                         self.create_task('create_symbol_link', file, output_node) 
1190         
1191 import ctypes
1192 ###############################################################################
1193 # Class to handle copying of the final outputs into the Bin folder
1194 class create_symbol_link(Task):
1195         color = 'YELLOW'
1196         
1197         def run(self):  
1198                 src = self.inputs[0].abspath()
1199                 tgt = self.outputs[0].abspath()
1200                         
1201                 # Make output writeable
1202                 try:
1203                         os.chmod(tgt, 493) # 0755
1204                 except:
1205                         pass
1206                         
1207                 try:
1208                         kdll = ctypes.windll.LoadLibrary("kernel32.dll")
1209                         res = kdll.CreateSymbolicLinkA(tgt, src, 0)
1210                 except:
1211                         self.generator.bld.fatal("File Link Error (%s -> %s( (%s)" % (src, tgt, sys.exc_info()[0]))
1212                 
1213                 return 0
1214                 
1215         def runnable_status(self):      
1216                 if super(create_symbol_link, self).runnable_status() == -1:
1217                         return -1
1218                 
1219                 src = self.inputs[0].abspath()
1220                 tgt = self.outputs[0].abspath()
1222                 # If there any target file is missing, we have to copy
1223                 try:
1224                         stat_tgt = os.stat(tgt)
1225                 except OSError:                 
1226                         return RUN_ME
1227                 
1228                 # Now compare both file stats
1229                 try:
1230                         stat_src = os.stat(src)                         
1231                 except OSError:
1232                         pass
1233                 else:
1234                         # same size and identical timestamps -> make no copy
1235                         if stat_src.st_mtime >= stat_tgt.st_mtime + 2:                  
1236                                 return RUN_ME
1237                                 
1238                 # Everything fine, we can skip this task                
1239                 return -2 # SKIP_ME     
1242 @feature('cxx', 'c', 'cprogram', 'cxxprogram', 'cshlib', 'cxxshlib', 'cstlib', 'cxxstlib')
1243 @after_method('apply_link')
1244 def add_compiler_dependency(self):
1245         """ Helper function to ensure each compile task depends on the compiler """
1246         if self.env['PLATFORM'] == 'project_generator':
1247                 return
1248                 
1249         # Create nodes for compiler and linker
1250         c_compiler_node = self.bld.root.make_node( self.env['CC'] )
1251         cxx_compiler_node = self.bld.root.make_node( self.env['CXX'] )
1252         linker_node = self.bld.root.make_node( self.env['LINK'] )
1254         # Let all compile tasks depend on the compiler
1255         for t in getattr(self, 'compiled_tasks', []):
1256                 if os.path.isabs( self.env['CC'] ):
1257                         t.dep_nodes.append(c_compiler_node)
1258                 if os.path.isabs( self.env['CXX'] ):
1259                         t.dep_nodes.append(cxx_compiler_node)
1260                 
1261         # Let all link tasks depend on the linker
1262         if getattr(self, 'link_task', None):
1263                 if os.path.isabs(  self.env['LINK'] ):
1264                         self.link_task.dep_nodes.append(linker_node)
1266 ###############################################################################
1267 def show_option_dialog(ctx):
1268         ctx.gui_modify_user_options()
1269                 
1270 ###############################################################################
1271 class execute_waf_options_dialog(BuildContext):
1272         ''' Util class to execute waf options dialog  '''
1273         cmd = 'show_option_dialog'
1274         fun = 'show_option_dialog'
1275         
1277 #avoid log message
1278 # "feature 'java' does not exist - bind at least one method to it"
1279 # the java feature is added if waf detects a single 'java' file.. which is used in android
1281 @feature('java')
1282 def dummy_func(ctx):
1283         pass