!I integrate from //ce/main...
[CRYENGINE.git] / wscript
blob1302245e8b7329c643c6aab51059d85214539efc
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_x64', tooldir=CRY_WAF_TOOL_DIR)
162         conf.load('compile_settings_darwin', tooldir=CRY_WAF_TOOL_DIR)
163         
164         optional_platforms = [
165         'durango',
166         'orbis'         
167         ]       
168         
169         for platform in optional_platforms:     
170                 file_name = 'compile_settings_' + platform
171                 if os.path.isfile(os.path.join(conf.path.abspath(), CRY_WAF_TOOL_DIR, file_name + '.py')):
172                         conf.load(file_name, tooldir=CRY_WAF_TOOL_DIR)
173                 else:
174                         try:
175                                 conf.get_supported_platforms().remove(platform)
176                         except:
177                                 pass
178                         Logs.warn('[WARNING] - Compiler settings not found. (%s) ' % file_name )
180         # Load CppCheck since it is a 'global' platform
181         conf.load('cppcheck', tooldir=CRY_WAF_TOOL_DIR)
182         
183         # Load QT last as we need to hook the runnable status at the lowest level
184         conf.load('qt' ,tooldir=CRY_WAF_TOOL_DIR)
186         ###########################################
187         # Load support for c# and swig
188         conf.load('swig', tooldir=CRY_WAF_TOOL_DIR)
189         conf.load('protoc', 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()                     
257                         conf.configure_protoc()
259                         # Load swig and mono
260                         conf.configure_swig()
261                         conf.configure_mono()
262                         conf.env['build_relevant_platforms'] = installed_platforms
263                         conf.setenv('') # change env back to global env
265         conf.set_supported_platforms(installed_platforms)
266         conf.env['build_relevant_platforms'] = installed_platforms # need to store in global cache as well for  project_generators
268         ###########################################     
269         # Load support for c and cxx compiler
270         conf.load('c')
271         conf.load('cxx')
272                 
273         conf.load('protoc',tooldir=CRY_WAF_TOOL_DIR)
274         
275         ###########################################
276         # Load Platform specific helpers based on host
277         host = Utils.unversioned_sys_platform() 
278         if host == 'darwin':
279                 conf.load('c_osx')
280         
281         ###########################################
282         # Create VS-Projects automatically during configure when running on windows
283         if host == 'win32' and conf.is_option_true('generate_vs_projects_automatically'):
284                 # Workflow improvement: for all builds generate projects after the build
285                 # except when using the default build target 'utilities' then do it before
286                 if 'build' in Options.commands:
287                         build_cmd_idx = Options.commands.index('build')         
288                         Options.commands.insert(build_cmd_idx, 'msvs') 
289                 else:
290                         Options.commands.append('msvs')
291          
292         ###########################################
293         # Load remaining tools for correct auto configure
294         if host == 'win32':
295                 conf.load('recode', tooldir=CRY_WAF_TOOL_DIR)   
296                 conf.load('static_code_analyzer', tooldir=CRY_WAF_TOOL_DIR)
297         
298         ###########################################
299         # Recurse into subfolders for auto conf when any wscript changes
300         conf.recurse(dirs=SUBFOLDERS, mandatory=False)
301                 
302         # Load Game Specific parts      
303         for project in conf.game_projects():
304                 conf.game_project = project
305                 conf.recurse( conf.game_code_folder(project))
307         ###########################################
308         # Always update uber files during configuration
309         # But don't add the same command twice
310         if len(Options.commands) == 0:
311                 Options.commands.insert(0, 'generate_uber_files')
312         elif not Options.commands[0] == 'generate_uber_files':
313                 Options.commands.insert(0, 'generate_uber_files')
315         # Remove timestamp files to force builds of generate_uber_files and project gen even if
316         # some command after configure failes           
317         try:
318                 generate_uber_files_timestamp = conf.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')       
319                 os.stat(generate_uber_files_timestamp.abspath())
320         except OSError: 
321                 pass
322         else:
323                 generate_uber_files_timestamp.delete()
324         
325         try:
326                 project_gen_timestamp = conf.get_bintemp_folder_node().make_node('project_gen.timestamp')
327                 os.stat(project_gen_timestamp.abspath())
328         except OSError: 
329                 pass
330         else:
331                 project_gen_timestamp.delete()
333 def post_command_exec(bld):     
334         # [post project gen]
335         if bld.cmd == 'msvs':
336                 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
337                 project_gen_timestamp.write('')
338         # [post uberfile gen]
339         elif bld.cmd == 'generate_uber_files':
340                 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
341                 generate_uber_files_timestamp.write('')
342         # [post build]
343         elif bld.cmd.startswith('build'):
344                 for message in bld.post_build_msg_info:
345                         Logs.info(message)
346                         
347                 for message in bld.post_build_msg_warning:
348                         Logs.warn(message)
349                         
350                 for message in bld.post_build_msg_error:
351                         Logs.error(message)
352                         
353 stored_file_filter = ''
354 stored_output_file = ''
356 ###############################################################################
357 ## Run 'build' step     
358 def build(bld):
360         ###########################################
361         # Setup command coordinator
362         bld.load('cmd_coordinator', tooldir=CRY_WAF_TOOL_DIR)
363         bld.setup_command_coordinator()
364         
365         # We might run multiple instances of WAF at the same time
366         # 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
367         # 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)
368         if not bld.instance_is_build_master():
369                 return
371         # Keep backward compatibility
372         if bld.options.project_spec == 'everything':    
373                 bld.options.project_spec = 'trybuilder'
374                 
375         bld.options.project_spec = bld.options.project_spec.strip() # remove spaces 
376         
377         # Create a post build message container
378         bld.post_build_msg_info = []
379         bld.post_build_msg_warning = []
380         bld.post_build_msg_error = []   
381         
382         # Add groups to ensure all task generators who create static lib tasks are executd first
383         bld.add_group('generate_static_lib')
384         bld.add_group('regular_group')
385         # Set back to our regular compile group
386         bld.set_group('regular_group')
387         
388         
389         ###########################################
390         # Load common windows settings if needed
391         bld.load('winres', tooldir=CRY_WAF_TOOL_DIR)
392                 
393         # Check if a valid spec is defined      
394         if bld.cmd != 'generate_uber_files' and bld.cmd != 'msvs' and bld.cmd != 'eclipse':
395                 # project spec is a mandatory parameter
396                 if bld.options.project_spec == '':
397                         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())
398                 
399                 # Ensure that the selected spec is supported on this platform
400                 if not bld.options.project_spec in bld.loaded_specs():
401                         bld.fatal('[ERROR] Invalid Project Spec (%s) defined, valid specs are %s' % (bld.options.project_spec, bld.loaded_specs()))
402         
403                 # Check for valid single file compilation flag pairs
404                 if bld.is_option_true('show_preprocessed_file') and bld.options.file_filter == "":  
405                         bld.fatal('--show-preprocessed-file can only be used in conjunction with --file-filter')
406                 elif bld.is_option_true('show_disassembly') and bld.options.file_filter == "":  
407                         bld.fatal('--show-disassembly can only be used in conjunction with --file-filter')    
408                                         
409         ###########################################
410         # Check timestamps for special commands         
411         if not 'generate_uber_files' in Options.commands and bld.cmd != 'generate_uber_files':
412                 generate_uber_files_timestamp = bld.get_bintemp_folder_node().make_node('generate_uber_files.timestamp')
413                 try:
414                         os.stat(generate_uber_files_timestamp.abspath())
415                 except OSError: 
416                         # No generate_uber file timestamp, restart command chain, prefixed with gen uber files cmd
417                         Options.commands = ['generate_uber_files'] + [bld.cmd] + Options.commands       
418                         return
419         
420         if not 'msvs' in Options.commands and bld.cmd != 'msvs':
421                 project_gen_timestamp = bld.get_bintemp_folder_node().make_node('project_gen.timestamp')
422                 try:
423                         os.stat(project_gen_timestamp.abspath())
424                 except OSError: 
425                         # No project_gen timestamp, append project_gen to commands
426                         if bld.is_option_true('generate_vs_projects_automatically') and  Utils.unversioned_sys_platform() == 'win32':
427                                 Options.commands = Options.commands + ['msvs']
428                                         
429         ###########################################
430         # Check for valid variant if we are not generating projects     
431         if bld.cmd == 'xcode' or bld.cmd == 'msvs' or bld.cmd == 'eclipse' or bld.cmd == 'generate_uber_files':
432                 bld.env['PLATFORM'] = 'project_generator'
433                 bld.env['CONFIGURATION'] = 'project_generator'
434         else:
435                 if not bld.variant:
436                         bld.fatal('please use a valid build configuration, try "waf --help"')                   
437                 
438                 (platform, configuration) = bld.get_platform_and_configuration()                        
439                 bld.env['PLATFORM']                     = platform
440                 bld.env['CONFIGURATION']        = configuration
441                 
442                 if  not platform in bld.get_supported_platforms():
443                         bld.fatal( '[ERROR] - Target platform "%s" not supported. [on host platform: %s]' % (platform, Utils.unversioned_sys_platform()) )
444                 
445                 # When building, only support platform that we are currently building for to reduce internal workload
446                 bld.set_supported_platforms([bld.env['PLATFORM']])
447                 
448                 # check if configuration is valid in the current spec scope
449                 bVariantValid = False
450                 for conf in bld.spec_valid_configurations():
451                         if conf == configuration:
452                                 bVariantValid = True
453                 if not bVariantValid:
454                         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())))
455                 
456                 # check if platform is valid in the current spec scope
457                 bVariantValid = False
458                 for p0 in bld.spec_valid_platforms():
459                         for p1 in bld.get_platform_list(bld.env['PLATFORM']):
460                                 if p0 == p1:
461                                         bVariantValid = True
462                 if not bVariantValid:
463                         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())))
465                 # Ensure that, if specified, target is supported in this spec
466                 if bld.options.targets:
467                         for target in bld.options.targets.split(','):
468                                 if not target in  bld.spec_modules():
469                                         bld.fatal('[ERROR] Module "%s" is not configurated to be build in spec "%s" in "%s|%s"' % (target, bld.options.project_spec, platform, configuration))
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()
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" or 'android' in self.bld.env['PLATFORM']:
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()
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':
1040                 path += self.options.out_folder_android
1041         elif platform == 'android_arm64':
1042                 path += self.options.out_folder_android64
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/bootstrap.exe'
1097         return os.path.isfile(bootstrap_path)
1098         
1099 ############################################################################### 
1100 @conf
1101 def ExecuteBootstrap(bld):
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/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(),
1128                                                 '--addrelevance=buildmachine'])
1129         if ret == 0:
1130                 bld.msg('branch bootstrap', 'done')
1131         else:
1132                 bld.msg('branch bootstrap', 'failed', color='RED')
1133                 sys.exit(1)     
1134                 
1135 from waflib.Configure import conf
1136         
1137 @conf
1138 def read_file_list(bld, file):
1139         file_node = bld.path.make_node(file)    
1141         return bld.parse_json_file(file_node)
1142         
1143 @conf
1144 def get_platform_and_configuration(bld):
1145         # Assume that the configuration begins after the last '_'
1146         platform =  "_".join( bld.variant.split("_")[:-1] )
1147         configuration = bld.variant.split("_")[-1]
1148         
1149         # Keep Backward compatibility with old names
1150         if platform == '_win32':
1151                 platform = 'win_x86'
1152         if platform == '_win64':
1153                 platform = 'win_x64'
1155         return (platform, configuration)
1157 @feature('link_to_output_folder')
1158 @after_method('process_source')
1159 def link_to_output_folder(self):
1160         """
1161         Task Generator for tasks which generate symbolic links from the source to the dest folder
1162         """
1163         return # Disabled for now
1164         
1165         if self.bld.env['PLATFORM'] == 'project_generator':
1166                 return # Dont create links during project generation
1168         if sys.getwindowsversion()[0] < 6:
1169                 self.bld.fatal('not supported')
1170                 
1171         # Compute base relative path (from <Root> to taskgen wscript
1172         relative_base_path = self.path.path_from(self.bld.path)
1173         
1174         # TODO: Need to handle absolute path here correctly
1175         spec_name = self.bld.options.project_spec                       
1176         for project in self.bld.active_projects(spec_name):
1177                 project_folder = self.bld.project_output_folder(project)
1178                 for file in self.source:
1179                         # Build output folder
1180                         relativ_file_path = file.path_from(self.path)
1181                         
1182                         output_node = self.bld.path.make_node(project_folder)
1183                         output_node = output_node.make_node(relative_base_path)
1184                         output_node = output_node.make_node(relativ_file_path)
1185                         
1186                         path = os.path.dirname( output_node.abspath() )
1187                         if not os.path.exists( path ):
1188                                 os.makedirs( path )
1189                         
1190                         self.create_task('create_symbol_link', file, output_node) 
1191         
1192 import ctypes
1193 ###############################################################################
1194 # Class to handle copying of the final outputs into the Bin folder
1195 class create_symbol_link(Task):
1196         color = 'YELLOW'
1197         
1198         def run(self):  
1199                 src = self.inputs[0].abspath()
1200                 tgt = self.outputs[0].abspath()
1201                         
1202                 # Make output writeable
1203                 try:
1204                         os.chmod(tgt, 493) # 0755
1205                 except:
1206                         pass
1207                         
1208                 try:
1209                         kdll = ctypes.windll.LoadLibrary("kernel32.dll")
1210                         res = kdll.CreateSymbolicLinkA(tgt, src, 0)
1211                 except:
1212                         self.generator.bld.fatal("File Link Error (%s -> %s( (%s)" % (src, tgt, sys.exc_info()[0]))
1213                 
1214                 return 0
1215                 
1216         def runnable_status(self):      
1217                 if super(create_symbol_link, self).runnable_status() == -1:
1218                         return -1
1219                 
1220                 src = self.inputs[0].abspath()
1221                 tgt = self.outputs[0].abspath()
1223                 # If there any target file is missing, we have to copy
1224                 try:
1225                         stat_tgt = os.stat(tgt)
1226                 except OSError:                 
1227                         return RUN_ME
1228                 
1229                 # Now compare both file stats
1230                 try:
1231                         stat_src = os.stat(src)                         
1232                 except OSError:
1233                         pass
1234                 else:
1235                         # same size and identical timestamps -> make no copy
1236                         if stat_src.st_mtime >= stat_tgt.st_mtime + 2:                  
1237                                 return RUN_ME
1238                                 
1239                 # Everything fine, we can skip this task                
1240                 return -2 # SKIP_ME     
1243 @feature('cxx', 'c', 'cprogram', 'cxxprogram', 'cshlib', 'cxxshlib', 'cstlib', 'cxxstlib')
1244 @after_method('apply_link')
1245 def add_compiler_dependency(self):
1246         """ Helper function to ensure each compile task depends on the compiler """
1247         if self.env['PLATFORM'] == 'project_generator':
1248                 return
1249                 
1250         # Create nodes for compiler and linker
1251         c_compiler_node = self.bld.root.make_node( self.env['CC'] )
1252         cxx_compiler_node = self.bld.root.make_node( self.env['CXX'] )
1253         linker_node = self.bld.root.make_node( self.env['LINK'] )
1255         # Let all compile tasks depend on the compiler
1256         for t in getattr(self, 'compiled_tasks', []):
1257                 if os.path.isabs( self.env['CC'] ):
1258                         t.dep_nodes.append(c_compiler_node)
1259                 if os.path.isabs( self.env['CXX'] ):
1260                         t.dep_nodes.append(cxx_compiler_node)
1261                 
1262         # Let all link tasks depend on the linker
1263         if getattr(self, 'link_task', None):
1264                 if os.path.isabs(  self.env['LINK'] ):
1265                         self.link_task.dep_nodes.append(linker_node)
1267 ###############################################################################
1268 def show_option_dialog(ctx):
1269         ctx.gui_modify_user_options()
1270                 
1271 ###############################################################################
1272 class execute_waf_options_dialog(BuildContext):
1273         ''' Util class to execute waf options dialog  '''
1274         cmd = 'show_option_dialog'
1275         fun = 'show_option_dialog'
1276         
1278 #avoid log message
1279 # "feature 'java' does not exist - bind at least one method to it"
1280 # the java feature is added if waf detects a single 'java' file.. which is used in android
1282 @feature('java')
1283 def dummy_func(ctx):
1284         pass