Merged changes in the trunk up to revision 42116.
[blender.git] / build_files / scons / tools / Blender.py
1 #!/usr/bin/env python
2
3 """
4 tools.BlenderEnvironment
5
6 This environment builds on SCons.Script.SConscript.SConsEnvironment
7
8 * library repository
9 * custom printout
10 * wrapper functions
11
12 TODO: clean up and sanitise code - crosscheck with btools and SConstruct
13 to kill any code duplication
14
15 """
16
17 import os
18 import os.path
19 import string
20 import glob
21 import time
22 import sys
23 import tarfile
24 import shutil
25 import cStringIO
26 import platform
27
28 from SCons.Script.SConscript import SConsEnvironment
29 import SCons.Action
30 import SCons.Util
31 import SCons.Builder
32 import SCons.Tool
33 import bcolors
34 bc = bcolors.bcolors()
35 import btools
36 VERSION = btools.VERSION
37 VERSION_RELEASE_CYCLE = btools.VERSION_RELEASE_CYCLE
38
39 Split = SCons.Util.Split
40 Action = SCons.Action.Action
41 Builder = SCons.Builder.Builder
42 GetBuildPath = SConsEnvironment.GetBuildPath
43
44 # a few globals
45 root_build_dir = ''
46 doc_build_dir = ''
47 quickie = None # Anything else than None if BF_QUICK has been passed
48 quicklist = [] # The list of libraries/programs to compile during a quickie
49 program_list = [] # A list holding Nodes to final binaries, used to create installs
50 arguments = None
51 targets = None
52 resources = []
53
54 #some internals
55 blenderdeps = [] # don't manipulate this one outside this module!
56
57 ##### LIB STUFF ##########
58
59 possible_types = ['core'] # can be set in ie. SConstruct
60 libs = {}
61 vcp = []
62
63 def getresources():
64     return resources
65
66 def init_lib_dict():
67     for pt in possible_types:
68         libs[pt] = {}
69
70 # helper func for add_lib_to_dict
71 def internal_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
72     if not libname in dict[libtype]:
73         done = None
74         while not done:
75             if dict[libtype].has_key(priority):
76                 priority = priority + 1
77             else:
78                 done = True
79         dict[libtype][priority] = libname
80
81 # libtype and priority can both be lists, for defining lib in multiple places
82 def add_lib_to_dict(env, dict = None, libtype = None, libname = None, priority = 100):
83     if not dict or not libtype or not libname:
84         print "Passed wrong arg"
85         env.Exit()
86
87     if type(libtype) is str and type(priority) is int:
88         internal_lib_to_dict(dict, libtype, libname, priority)
89     elif type(libtype) is list and type(priority) is list:
90         if len(libtype)==len(priority):
91             for lt, p in zip(libtype, priority):
92                 internal_lib_to_dict(dict, lt, libname, p)
93         else:
94             print "libtype and priority lists are unequal in length"
95             env.Exit()
96     else:
97         print "Wrong type combinations for libtype and priority. Only str and int or list and list"
98         env.Exit()
99
100 def create_blender_liblist(lenv = None, libtype = None):
101     if not lenv or not libtype:
102         print "missing arg"
103
104     lst = []
105     if libtype in possible_types:
106         curlib = libs[libtype]
107         sortlist = curlib.keys()
108         sortlist.sort()
109         for sk in sortlist:
110             v = curlib[sk]
111             if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
112                 target = os.path.abspath(os.getcwd() + os.sep + root_build_dir + 'lib' + os.sep +lenv['LIBPREFIX'] + v + lenv['LIBSUFFIX'])
113             else:
114                 target = os.path.abspath(root_build_dir + 'lib' + os.sep +lenv['LIBPREFIX'] + v + lenv['LIBSUFFIX'])
115             lst.append(target)
116
117     return lst
118
119 ## TODO: static linking
120 def setup_staticlibs(lenv):
121     statlibs = [
122         #here libs for static linking
123     ]
124
125     libincs = []
126
127     if lenv['WITH_BF_FFMPEG']:
128         libincs += Split(lenv['BF_FFMPEG_LIBPATH'])
129
130     libincs.extend([
131         lenv['BF_OPENGL_LIBPATH'],
132         lenv['BF_JPEG_LIBPATH'],
133         lenv['BF_ZLIB_LIBPATH'],
134         lenv['BF_PNG_LIBPATH'],
135         lenv['BF_ICONV_LIBPATH']
136         ])
137
138     libincs += Split(lenv['BF_FREETYPE_LIBPATH'])
139     if lenv['WITH_BF_PYTHON']:
140         libincs += Split(lenv['BF_PYTHON_LIBPATH'])
141     if lenv['WITH_BF_SDL']:
142         libincs += Split(lenv['BF_SDL_LIBPATH'])
143     if lenv['WITH_BF_JACK']:
144         libincs += Split(lenv['BF_JACK_LIBPATH'])
145     if lenv['WITH_BF_SNDFILE']:
146         libincs += Split(lenv['BF_SNDFILE_LIBPATH'])
147     if lenv['WITH_BF_OPENEXR']:
148         libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
149         if lenv['WITH_BF_STATICOPENEXR']:
150             statlibs += Split(lenv['BF_OPENEXR_LIB_STATIC'])
151     if lenv['WITH_BF_TIFF']:
152         libincs += Split(lenv['BF_TIFF_LIBPATH'])
153         if lenv['WITH_BF_STATICTIFF']:
154             statlibs += Split(lenv['BF_TIFF_LIB_STATIC'])
155     if lenv['WITH_BF_ZLIB'] and lenv['WITH_BF_STATICZLIB']:
156         statlibs += Split(lenv['BF_ZLIB_LIB_STATIC'])
157     if lenv['WITH_BF_FFTW3']:
158         libincs += Split(lenv['BF_FFTW3_LIBPATH'])
159         if lenv['WITH_BF_STATICFFTW3']:
160             statlibs += Split(lenv['BF_FFTW3_LIB_STATIC'])
161     if lenv['WITH_BF_FFMPEG'] and lenv['WITH_BF_STATICFFMPEG']:
162         statlibs += Split(lenv['BF_FFMPEG_LIB_STATIC'])
163     if lenv['WITH_BF_INTERNATIONAL']:
164         libincs += Split(lenv['BF_GETTEXT_LIBPATH'])
165         if lenv['WITH_BF_GETTEXT_STATIC']:
166             statlibs += Split(lenv['BF_GETTEXT_LIB_STATIC'])
167         if lenv['WITH_BF_FREETYPE_STATIC']:
168             statlibs += Split(lenv['BF_FREETYPE_LIB_STATIC'])
169     if lenv['WITH_BF_OPENAL']:
170         libincs += Split(lenv['BF_OPENAL_LIBPATH'])
171         if lenv['WITH_BF_STATICOPENAL']:
172             statlibs += Split(lenv['BF_OPENAL_LIB_STATIC'])
173     if lenv['WITH_BF_STATICOPENGL']:
174         statlibs += Split(lenv['BF_OPENGL_LIB_STATIC'])
175     if lenv['WITH_BF_STATICCXX']:
176         statlibs += Split(lenv['BF_CXX_LIB_STATIC'])
177
178     if lenv['WITH_BF_PYTHON'] and lenv['WITH_BF_STATICPYTHON']:
179         statlibs += Split(lenv['BF_PYTHON_LIB_STATIC'])
180
181     if lenv['WITH_BF_SNDFILE'] and lenv['WITH_BF_STATICSNDFILE']:
182         statlibs += Split(lenv['BF_SNDFILE_LIB_STATIC'])
183
184     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
185         libincs += Split(lenv['BF_PTHREADS_LIBPATH'])
186
187     if lenv['WITH_BF_COLLADA']:
188         libincs += Split(lenv['BF_OPENCOLLADA_LIBPATH'])
189         if lenv['OURPLATFORM'] not in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
190             libincs += Split(lenv['BF_PCRE_LIBPATH'])
191             libincs += Split(lenv['BF_EXPAT_LIBPATH'])
192
193     if lenv['WITH_BF_OPENMP']:
194         if lenv['OURPLATFORM'] == 'linuxcross':
195             libincs += Split(lenv['BF_OPENMP_LIBPATH'])
196             
197     if lenv['WITH_BF_OIIO']:
198         libincs += Split(lenv['BF_OIIO_LIBPATH'])
199         if lenv['WITH_BF_STATICOIIO']:
200             statlibs += Split(lenv['BF_OIIO_LIB_STATIC'])
201
202     if lenv['WITH_BF_BOOST']:
203         libincs += Split(lenv['BF_BOOST_LIBPATH'])
204         if lenv['WITH_BF_STATICBOOST']:
205             statlibs += Split(lenv['BF_BOOST_LIB_STATIC'])
206
207     # setting this last so any overriding of manually libs could be handled
208     if lenv['OURPLATFORM'] not in ('win32-vc', 'win32-mingw', 'win64-vc', 'linuxcross'):
209         libincs.append('/usr/lib')
210
211     if lenv['WITH_BF_JEMALLOC']:
212         libincs += Split(lenv['BF_JEMALLOC_LIBPATH'])
213         if lenv['WITH_BF_STATICJEMALLOC']:
214             statlibs += Split(lenv['BF_JEMALLOC_LIB_STATIC'])
215
216     if lenv['OURPLATFORM']=='linux':
217         if lenv['WITH_BF_3DMOUSE']:
218             libincs += Split(lenv['BF_3DMOUSE_LIBPATH'])
219             if lenv['WITH_BF_STATIC3DMOUSE']:
220                 statlibs += Split(lenv['BF_3DMOUSE_LIB_STATIC'])
221
222     return statlibs, libincs
223
224 def setup_syslibs(lenv):
225     syslibs = []
226
227     if not lenv['WITH_BF_FREETYPE_STATIC']:
228         syslibs += Split(lenv['BF_FREETYPE_LIB'])
229     if lenv['WITH_BF_PYTHON'] and not lenv['WITH_BF_STATICPYTHON']:
230         if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc', 'win32-mingw'):
231             syslibs.append(lenv['BF_PYTHON_LIB']+'_d')
232         else:
233             syslibs.append(lenv['BF_PYTHON_LIB'])
234     if lenv['WITH_BF_INTERNATIONAL'] and not lenv['WITH_BF_GETTEXT_STATIC']:
235         syslibs += Split(lenv['BF_GETTEXT_LIB'])
236     if lenv['WITH_BF_OPENAL']:
237         if not lenv['WITH_BF_STATICOPENAL']:
238             syslibs += Split(lenv['BF_OPENAL_LIB'])
239     if lenv['WITH_BF_OPENMP'] and lenv['CC'] != 'icc':
240         if lenv['CC'] == 'cl.exe':
241             syslibs += ['vcomp']
242         else:
243             syslibs += ['gomp']
244     if lenv['WITH_BF_ICONV']:
245         syslibs += Split(lenv['BF_ICONV_LIB'])
246     if lenv['WITH_BF_OIIO']:
247         if not lenv['WITH_BF_STATICOIIO']:
248             syslibs += Split(lenv['BF_OIIO_LIB'])
249
250     if lenv['WITH_BF_OPENEXR'] and not lenv['WITH_BF_STATICOPENEXR']:
251         syslibs += Split(lenv['BF_OPENEXR_LIB'])
252     if lenv['WITH_BF_TIFF'] and not lenv['WITH_BF_STATICTIFF']:
253         syslibs += Split(lenv['BF_TIFF_LIB'])
254     if lenv['WITH_BF_ZLIB'] and not lenv['WITH_BF_STATICZLIB']:
255         syslibs += Split(lenv['BF_ZLIB_LIB'])
256     if lenv['WITH_BF_FFMPEG'] and not lenv['WITH_BF_STATICFFMPEG']:
257         syslibs += Split(lenv['BF_FFMPEG_LIB'])
258         if lenv['WITH_BF_OGG']:
259             syslibs += Split(lenv['BF_OGG_LIB'])
260     if lenv['WITH_BF_JACK']:
261         syslibs += Split(lenv['BF_JACK_LIB'])
262     if lenv['WITH_BF_SNDFILE'] and not lenv['WITH_BF_STATICSNDFILE']:
263         syslibs += Split(lenv['BF_SNDFILE_LIB'])
264     if lenv['WITH_BF_FFTW3'] and not lenv['WITH_BF_STATICFFTW3']:
265         syslibs += Split(lenv['BF_FFTW3_LIB'])
266     if lenv['WITH_BF_SDL']:
267         syslibs += Split(lenv['BF_SDL_LIB'])
268     if not lenv['WITH_BF_STATICOPENGL']:
269         syslibs += Split(lenv['BF_OPENGL_LIB'])
270     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw','linuxcross', 'win64-vc'):
271         syslibs += Split(lenv['BF_PTHREADS_LIB'])
272     if lenv['WITH_BF_COLLADA']:
273         syslibs.append(lenv['BF_PCRE_LIB'])
274         if lenv['BF_DEBUG']:
275             syslibs += [colladalib+'_d' for colladalib in Split(lenv['BF_OPENCOLLADA_LIB'])]
276         else:
277             syslibs += Split(lenv['BF_OPENCOLLADA_LIB'])
278         syslibs.append(lenv['BF_EXPAT_LIB'])
279
280     if lenv['WITH_BF_JEMALLOC']:
281         if not lenv['WITH_BF_STATICJEMALLOC']:
282             syslibs += Split(lenv['BF_JEMALLOC_LIB'])
283
284     if lenv['OURPLATFORM']=='linux':
285         if lenv['WITH_BF_3DMOUSE']:
286             if not lenv['WITH_BF_STATIC3DMOUSE']:
287                 syslibs += Split(lenv['BF_3DMOUSE_LIB'])
288                 
289     if lenv['WITH_BF_BOOST'] and not lenv['WITH_BF_STATICBOOST']:
290         syslibs += Split(lenv['BF_BOOST_LIB'])
291
292     syslibs += Split(lenv['BF_JPEG_LIB'])
293     syslibs += Split(lenv['BF_PNG_LIB'])
294         
295     syslibs += lenv['LLIBS']
296
297     return syslibs
298
299 def propose_priorities():
300     print bc.OKBLUE+"Priorities:"+bc.ENDC
301     for t in possible_types:
302         print bc.OKGREEN+"\t"+t+bc.ENDC
303         new_priority = 0
304         curlib = libs[t]
305         sortlist = curlib.keys()
306         sortlist.sort()
307
308         for sk in sortlist:
309             v = curlib[sk]
310             #for p,v in sorted(libs[t].iteritems()):
311             print "\t\t",new_priority, v
312             new_priority += 5
313
314 # emits the necessary file objects for creator.c, to be used in creating
315 # the final blender executable
316 def creator(env):
317     sources = ['creator.c']# + Blender.buildinfo(env, "dynamic") + Blender.resources
318
319     incs = ['#/intern/guardedalloc', '#/source/blender/blenlib', '#/source/blender/blenkernel', '#/source/blender/editors/include', '#/source/blender/blenloader', '#/source/blender/imbuf', '#/source/blender/renderconverter', '#/source/blender/render/extern/include', '#/source/blender/windowmanager', '#/source/blender/makesdna', '#/source/blender/makesrna', '#/source/gameengine/BlenderRoutines', '#/extern/glew/include', '#/source/blender/gpu', '#/source/blender/freestyle', env['BF_OPENGL_INC']]
320
321     defs = []
322
323     if env['WITH_BF_BINRELOC']:
324         incs.append('#/extern/binreloc/include')
325         defs.append('WITH_BINRELOC')
326
327     if env['WITH_BF_SDL']:
328         defs.append('WITH_SDL')
329
330     if env['WITH_BF_LIBMV']:
331         incs.append('#/extern/libmv')
332         defs.append('WITH_LIBMV')
333
334     if env['WITH_BF_PYTHON']:
335         incs.append('#/source/blender/python')
336         defs.append('WITH_PYTHON')
337         if env['BF_DEBUG']:
338             defs.append('_DEBUG')
339
340     if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
341         incs.append(env['BF_PTHREADS_INC'])
342
343     env.Append(CPPDEFINES=defs)
344     env.Append(CPPPATH=incs)
345     obj = [env.Object(root_build_dir+'source/creator/creator/creator', ['#source/creator/creator.c'])]
346
347     return obj
348
349 ## TODO: see if this can be made in an emitter
350 def buildinfo(lenv, build_type):
351     """
352     Generate a buildinfo object
353     """
354     build_date = time.strftime ("%Y-%m-%d")
355     build_time = time.strftime ("%H:%M:%S")
356     build_rev = os.popen('svnversion').read()[:-1] # remove \n
357     if build_rev == '': 
358         build_rev = '-UNKNOWN-'
359     if lenv['BF_DEBUG']:
360         build_type = "Debug"
361         build_cflags = ' '.join(lenv['CFLAGS'] + lenv['CCFLAGS'] + lenv['BF_DEBUG_CCFLAGS'] + lenv['CPPFLAGS'])
362         build_cxxflags = ' '.join(lenv['CCFLAGS'] + lenv['CXXFLAGS'] + lenv['CPPFLAGS'])
363     else:
364         build_type = "Release"
365         build_cflags = ' '.join(lenv['CFLAGS'] + lenv['CCFLAGS'] + lenv['REL_CFLAGS'] + lenv['REL_CCFLAGS'] + lenv['CPPFLAGS'])
366         build_cxxflags = ' '.join(lenv['CCFLAGS'] + lenv['CXXFLAGS'] + lenv['REL_CXXFLAGS'] + lenv['REL_CCFLAGS'] + lenv['CPPFLAGS'])
367
368     build_linkflags = ' '.join(lenv['PLATFORM_LINKFLAGS'])
369
370     obj = []
371     if lenv['BF_BUILDINFO']:
372         lenv.Append (CPPDEFINES = ['BUILD_TIME=\\"%s\\"'%(build_time),
373                                     'BUILD_DATE=\\"%s\\"'%(build_date),
374                                     'BUILD_TYPE=\\"%s\\"'%(build_type),
375                                     'BUILD_REV=\\"%s\\"'%(build_rev),
376                                     'WITH_BUILDINFO',
377                                     'BUILD_PLATFORM=\\"%s:%s\\"'%(platform.system(), platform.architecture()[0]),
378                                     'BUILD_CFLAGS=\\"%s\\"'%(build_cflags),
379                                     'BUILD_CXXFLAGS=\\"%s\\"'%(build_cxxflags),
380                                     'BUILD_LINKFLAGS=\\"%s\\"'%(build_linkflags),
381                                     'BUILD_SYSTEM=\\"SCons\\"'
382                     ])
383
384         lenv.Append (CPPPATH = [root_build_dir+'source/blender/blenkernel'])
385
386         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type, ['#source/creator/buildinfo.c'])]
387
388     return obj
389
390 ##### END LIB STUFF ############
391
392 ##### ACTION STUFF #############
393
394 def my_print_cmd_line(self, s, target, source, env):
395     sys.stdout.write(' ' * 70 + '\r')
396     sys.stdout.flush()
397     sys.stdout.write(s + "\r")
398     sys.stdout.flush()
399
400 def my_compile_print(target, source, env):
401     a = '%s' % (source[0])
402     d, f = os.path.split(a)
403     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
404
405 def my_moc_print(target, source, env):
406     a = '%s' % (source[0])
407     d, f = os.path.split(a)
408     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
409
410 def my_linking_print(target, source, env):
411     t = '%s' % (target[0])
412     d, f = os.path.split(t)
413     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
414
415 def my_program_print(target, source, env):
416     t = '%s' % (target[0])
417     d, f = os.path.split(t)
418     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
419
420 def msvc_hack(env):
421     static_lib = SCons.Tool.createStaticLibBuilder(env)
422     program = SCons.Tool.createProgBuilder(env)
423     
424     env['BUILDERS']['Library'] = static_lib
425     env['BUILDERS']['StaticLibrary'] = static_lib
426     env['BUILDERS']['Program'] = program
427         
428 def set_quiet_output(env):
429     mycaction = Action("$CCCOM", strfunction=my_compile_print)
430     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
431     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
432     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
433     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
434     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
435
436     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
437     static_ob.add_action('.c', mycaction)
438     static_ob.add_action('.cpp', mycppaction)
439     shared_ob.add_action('.c', myshcaction)
440     shared_ob.add_action('.cpp', myshcppaction)
441
442     static_lib = SCons.Builder.Builder(action = mylibaction,
443                                        emitter = '$LIBEMITTER',
444                                        prefix = '$LIBPREFIX',
445                                        suffix = '$LIBSUFFIX',
446                                        src_suffix = '$OBJSUFFIX',
447                                        src_builder = 'StaticObject')
448
449     program = SCons.Builder.Builder(action = mylinkaction,
450                                     emitter = '$PROGEMITTER',
451                                     prefix = '$PROGPREFIX',
452                                     suffix = '$PROGSUFFIX',
453                                     src_suffix = '$OBJSUFFIX',
454                                     src_builder = 'Object',
455                                     target_scanner = SCons.Defaults.ProgScan)
456
457     env['BUILDERS']['Object'] = static_ob
458     env['BUILDERS']['StaticObject'] = static_ob
459     env['BUILDERS']['StaticLibrary'] = static_lib
460     env['BUILDERS']['Library'] = static_lib
461     env['BUILDERS']['Program'] = program
462     if env['BF_LINE_OVERWRITE']:
463         SCons.Action._ActionAction.print_cmd_line = my_print_cmd_line
464
465 def untar_pybundle(from_tar,to_dir,exclude_re):
466     tar= tarfile.open(from_tar, mode='r')
467     exclude_re= list(exclude_re) #single re object or list of re objects
468     debug= 0 #list files instead of unpacking
469     good= []
470     if debug: print '\nFiles not being unpacked:\n'
471     for name in tar.getnames():
472         is_bad= 0
473         for r in exclude_re:
474             if r.match(name):
475                 is_bad=1
476                 if debug: print name
477                 break
478         if not is_bad:
479             good.append(tar.getmember(name))
480     if debug:
481         print '\nFiles being unpacked:\n'
482         for g in good:
483             print g
484     else:
485         tar.extractall(to_dir, good)
486
487 def my_winpybundle_print(target, source, env):
488     pass
489
490 def WinPyBundle(target=None, source=None, env=None):
491     import re
492     py_tar= env.subst( env['LCGDIR'] )
493     if py_tar[0]=='#':
494         py_tar= py_tar[1:]
495     if env['BF_DEBUG']:
496         py_tar+= '/release/python' + env['BF_PYTHON_VERSION'].replace('.','') + '_d.tar.gz'
497     else:
498         py_tar+= '/release/python' + env['BF_PYTHON_VERSION'].replace('.','') + '.tar.gz'
499
500     py_target = env.subst( env['BF_INSTALLDIR'] )
501     if py_target[0]=='#':
502         py_target=py_target[1:]
503     py_target = os.path.join(py_target, VERSION, 'python', 'lib')
504     def printexception(func,path,ex):
505         if os.path.exists(path): #do not report if path does not exist. eg on a fresh build.
506             print str(func) + ' failed on ' + str(path)
507     print "Trying to remove existing py bundle."
508     shutil.rmtree(py_target, False, printexception)
509     exclude_re=[re.compile('.*/test/.*'),
510                 re.compile('^config/.*'),
511                 re.compile('^config-*/.*'),
512                 re.compile('^distutils/.*'),
513                 re.compile('^idlelib/.*'),
514                 re.compile('^lib2to3/.*'),
515                 re.compile('^tkinter/.*'),
516                 re.compile('^_tkinter_d.pyd'),
517                 re.compile('^turtledemo'),
518                 re.compile('^turtle.py'),
519                 ]
520
521     print "Unpacking '" + py_tar + "' to '" + py_target + "'"
522     untar_pybundle(py_tar,py_target,exclude_re)
523
524 def  my_appit_print(target, source, env):
525     a = '%s' % (target[0])
526     d, f = os.path.split(a)
527     return "making bundle for " + f
528
529 def AppIt(target=None, source=None, env=None):
530     import shutil
531     import commands
532     import os.path
533     
534     
535     a = '%s' % (target[0])
536     builddir, b = os.path.split(a)
537     libdir = env['LCGDIR'][1:]
538     osxarch = env['MACOSX_ARCHITECTURE']
539     installdir = env['BF_INSTALLDIR']
540     print("compiled architecture: %s"%(osxarch))
541     print("Installing to %s"%(installdir))
542     # TODO, use tar.
543     python_zip = 'python_' + osxarch + '.zip' # set specific python_arch.zip
544     if env['WITH_OSX_STATICPYTHON']:
545         print("unzipping to app-bundle: %s"%(python_zip))
546     else:
547         print("dynamic build - make sure to have python3.x-framework installed")
548     bldroot = env.Dir('.').abspath
549     binary = env['BINARYKIND']
550      
551     sourcedir = bldroot + '/source/darwin/%s.app'%binary
552     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
553     targetinfo = installdir +'/' + "%s.app/Contents/Info.plist"%binary
554     cmd = installdir + '/' +'%s.app'%binary
555     
556     if os.path.isdir(cmd):
557         shutil.rmtree(cmd)
558     shutil.copytree(sourcedir, cmd)
559     cmd = "cat %s | sed s/\$\{MACOSX_BUNDLE_SHORT_VERSION_STRING\}/%s/ | "%(sourceinfo,VERSION)
560     cmd += "sed s/\$\{MACOSX_BUNDLE_LONG_VERSION_STRING\}/%s,\ %s/g > %s"%(VERSION,time.strftime("%Y-%b-%d"),targetinfo)
561     commands.getoutput(cmd)
562     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,installdir, binary, binary)
563     commands.getoutput(cmd)
564     cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/'%(installdir, binary, VERSION)
565     commands.getoutput(cmd)
566     cmd = installdir + '/%s.app/Contents/MacOS/%s'%(binary,VERSION)
567
568     # blenderplayer doesn't need all the files
569     if binary == 'blender':
570         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/datafiles'%(installdir, binary, VERSION)
571         commands.getoutput(cmd)
572         cmd = 'cp -R %s/release/datafiles/locale %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
573         commands.getoutput(cmd)
574         cmd = 'cp -R %s/release/datafiles/fonts %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
575         commands.getoutput(cmd)
576         cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/%s/'%(bldroot,installdir,binary,VERSION)
577         commands.getoutput(cmd)
578
579         if VERSION_RELEASE_CYCLE == "release":
580             cmd = 'rm -rf %s/%s.app/Contents/MacOS/%s/scripts/addons_contrib'%(installdir,binary,VERSION)
581             commands.getoutput(cmd)
582
583         if env['WITH_BF_CYCLES']:
584             croot = '%s/intern/cycles' % (bldroot)
585             cinstalldir = '%s/%s.app/Contents/MacOS/%s/scripts/addons/cycles' % (installdir,binary,VERSION)
586
587             cmd = 'mkdir %s' % (cinstalldir)
588             commands.getoutput(cmd)
589             cmd = 'mkdir %s/kernel' % (cinstalldir)
590             commands.getoutput(cmd)
591             cmd = 'cp -R %s/blender/addon/*.py %s/' % (croot, cinstalldir)
592             commands.getoutput(cmd)
593             cmd = 'cp -R %s/doc/license %s/license' % (croot, cinstalldir)
594             commands.getoutput(cmd)
595             cmd = 'cp -R %s/kernel/*.h %s/kernel/*.cl %s/kernel/*.cu %s/kernel/' % (croot, croot, croot, cinstalldir)
596             commands.getoutput(cmd)
597             cmd = 'cp -R %s/kernel/svm %s/util/util_color.h %s/util/util_math.h %s/util/util_transform.h %s/util/util_types.h %s/kernel/' % (croot, croot, croot, croot, croot, cinstalldir)
598             commands.getoutput(cmd)
599
600     if env['WITH_OSX_STATICPYTHON']:
601         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/python/'%(installdir,binary, VERSION)
602         commands.getoutput(cmd)
603         cmd = 'unzip -q %s/release/%s -d %s/%s.app/Contents/MacOS/%s/python/'%(libdir,python_zip,installdir,binary,VERSION)
604         commands.getoutput(cmd)
605
606     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(installdir,binary, binary)
607     commands.getoutput(cmd)
608     cmd = 'find %s/%s.app -name .svn -prune -exec rm -rf {} \;'%(installdir, binary)
609     commands.getoutput(cmd)
610     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(installdir, binary)
611     commands.getoutput(cmd)
612     cmd = 'find %s/%s.app -name __MACOSX -exec rm -rf {} \;'%(installdir, binary)
613     commands.getoutput(cmd)
614     if env['CC'].endswith('4.6.1'): # for correct errorhandling with gcc 4.6.1 we need the gcc.dylib to link, thus distribute in app-bundle
615         cmd = 'mkdir %s/%s.app/Contents/MacOS/lib'%(installdir, binary)
616         commands.getoutput(cmd)
617         instname = env['BF_CXX']
618         cmd = 'cp %s/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/'%(instname, installdir, binary)
619         commands.getoutput(cmd)
620         cmd = 'install_name_tool -id @executable_path/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/libgcc_s.1.dylib'%(installdir, binary)
621         commands.getoutput(cmd)
622         cmd = 'install_name_tool -change %s/lib/libgcc_s.1.dylib  @executable_path/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/%s'%(instname, installdir, binary, binary)
623         commands.getoutput(cmd)
624         cmd = 'rm -rf  %s/set_simulation_threads.app'%(installdir) # first clear omp_num_threads applescript
625         commands.getoutput(cmd)
626         cmd = 'cp -R %s/source/darwin/set_simulation_threads.app %s/'%(bldroot, installdir) # copy the omp_num_threads applescript
627         commands.getoutput(cmd)
628
629 # extract copy system python, be sure to update other build systems
630 # when making changes to the files that are copied.
631 def my_unixpybundle_print(target, source, env):
632     pass
633
634 def UnixPyBundle(target=None, source=None, env=None):
635     # Any Unix except osx
636     #-- VERSION/python/lib/python3.1
637     
638     import commands
639     
640     def run(cmd):
641         print 'Install command:', cmd
642         commands.getoutput(cmd)
643
644     dir = os.path.join(env['BF_INSTALLDIR'], VERSION)
645
646     py_src =    env.subst( env['BF_PYTHON_LIBPATH'] + '/python'+env['BF_PYTHON_VERSION'] )
647     py_target =    env.subst( dir + '/python/lib/python'+env['BF_PYTHON_VERSION'] )
648     
649     # This is a bit weak, but dont install if its been installed before, makes rebuilds quite slow.
650     if os.path.exists(py_target):
651         print 'Using existing python from:'
652         print '\t"%s"' %            py_target
653         print '\t(skipping copy)\n'
654         return
655
656     # Copied from source/creator/CMakeLists.txt, keep in sync.
657     print 'Install python from:'
658     print '\t"%s" into...' % py_src
659     print '\t"%s"\n' % py_target
660
661     run("rm -rf '%s'" % py_target)
662     try:
663         os.makedirs(os.path.dirname(py_target)) # the final part is copied
664     except:
665         pass
666
667     run("cp -R '%s' '%s'" % (py_src, os.path.dirname(py_target)))
668     run("rm -rf '%s/distutils'" % py_target)
669     run("rm -rf '%s/lib2to3'" % py_target)
670     run("rm -rf '%s/config'" % py_target)
671
672     for f in os.listdir(py_target):
673         if f.startswith("config-"):
674             run("rm -rf '%s/%s'" % (py_target, f))
675
676     run("rm -rf '%s/site-packages'" % py_target)
677     run("mkdir '%s/site-packages'" % py_target)    # python needs it.'
678     run("rm -rf '%s/idlelib'" % py_target)
679     run("rm -rf '%s/tkinter'" % py_target)
680     run("rm -rf '%s/turtledemo'" % py_target)
681     run("rm -r '%s/turtle.py'" % py_target)
682     run("rm -f '%s/lib-dynload/_tkinter.so'" % py_target)
683
684     run("find '%s' -type d -name 'test' -prune -exec rm -rf {} ';'" % py_target)
685     run("find '%s' -type d -name '__pycache__' -exec rm -rf {} ';'" % py_target)
686     run("find '%s' -name '*.py[co]' -exec rm -rf {} ';'" % py_target)
687     run("find '%s' -name '*.so' -exec strip -s {} ';'" % py_target)
688     
689
690 #### END ACTION STUFF #########
691
692 def bsc(env, target, source):
693     
694     bd = os.path.dirname(target[0].abspath)
695     bscfile = '\"'+target[0].abspath+'\"'
696     bscpathcollect = '\"'+bd + os.sep + '*.sbr\"'
697     bscpathtmp = '\"'+bd + os.sep + 'bscmake.tmp\"'
698
699     os.system('dir /b/s '+bscpathcollect+' >'+bscpathtmp)
700
701     myfile = open(bscpathtmp[1:-1], 'r')
702     lines = myfile.readlines()
703     myfile.close()
704
705     newfile = open(bscpathtmp[1:-1], 'w')
706     for l in lines:
707         newfile.write('\"'+l[:-1]+'\"\n')
708     newfile.close()
709                 
710     os.system('bscmake /nologo /n /o'+bscfile+' @'+bscpathtmp)
711     os.system('del '+bscpathtmp)
712
713 class BlenderEnvironment(SConsEnvironment):
714
715     PyBundleActionAdded = False
716
717     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
718         global libs
719         if not self or not libname or not source:
720             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
721             self.Exit()
722         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross', 'win64-vc'):
723             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
724             self.Exit()
725         
726         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
727         lenv = self.Clone()
728         if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
729             res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
730         else:
731             res = lenv.RES(root_build_dir+'lib/'+libname, source)
732
733         
734         SConsEnvironment.Default(self, res)
735         resources.append(res)
736
737     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None, cc_compileflags=None, cxx_compileflags=None, cc_compilerchange=None, cxx_compilerchange=None):
738         global vcp
739         if not self or not libname or not sources:
740             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
741             self.Exit()
742
743         def list_substring(quickie, libname):
744             for q in quickie:
745                 if libname.find(q) != -1:
746                     return True
747             return False
748
749         if list_substring(quickie, libname) or len(quickie)==0:
750             if list_substring(quickdebug, libname):
751                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname +bc.ENDC+bc.OKBLUE+ " (debug mode)" + bc.ENDC
752             else:
753                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname + bc.ENDC
754             lenv = self.Clone()
755             lenv.Append(CPPPATH=includes)
756             lenv.Append(CPPDEFINES=defines)
757             if lenv['BF_DEBUG'] or (libname in quickdebug):
758                 lenv.Append(CFLAGS = lenv['BF_DEBUG_CFLAGS'])
759                 lenv.Append(CCFLAGS = lenv['BF_DEBUG_CCFLAGS'])
760                 lenv.Append(CXXFLAGS = lenv['BF_DEBUG_CXXFLAGS'])
761             else:
762                 lenv.Append(CFLAGS = lenv['REL_CFLAGS'])
763                 lenv.Append(CCFLAGS = lenv['REL_CCFLAGS'])
764                 lenv.Append(CXXFLAGS = lenv['REL_CXXFLAGS'])
765             if lenv['BF_PROFILE']:
766                 lenv.Append(CFLAGS = lenv['BF_PROFILE_CFLAGS'])
767                 lenv.Append(CCFLAGS = lenv['BF_PROFILE_CCFLAGS'])
768                 lenv.Append(CXXFLAGS = lenv['BF_PROFILE_CXXFLAGS'])
769             if compileflags:
770                 lenv.Replace(CFLAGS = compileflags)
771             if cc_compileflags:
772                 lenv.Replace(CCFLAGS = cc_compileflags)
773             if cxx_compileflags:
774                 lenv.Replace(CXXFLAGS = cxx_compileflags)
775             if cc_compilerchange:
776                 lenv.Replace(CC = cc_compilerchange)
777             if cxx_compilerchange:
778                 lenv.Replace(CXX = cxx_compilerchange)
779             lenv.Append(CFLAGS = lenv['C_WARN'])
780             lenv.Append(CCFLAGS = lenv['CC_WARN'])
781             lenv.Append(CXXFLAGS = lenv['CXX_WARN'])
782
783             if lenv['OURPLATFORM'] == 'win64-vc':
784                 lenv.Append(LINKFLAGS = ['/MACHINE:X64'])
785
786             if lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
787                 if lenv['BF_DEBUG']:
788                     lenv.Append(CCFLAGS = ['/MTd'])
789                 else:
790                     lenv.Append(CCFLAGS = ['/MT'])
791             
792             targetdir = root_build_dir+'lib/' + libname
793             if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
794                 targetdir = '#'+targetdir
795             lib = lenv.Library(target= targetdir, source=sources)
796             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
797             if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
798                 #if targetdir[0] == '#':
799                 #    targetdir = targetdir[1:-1]
800                 print "! ",targetdir+ '.vcproj' # + self['MSVSPROJECTSUFFIX']
801                 vcproject = self.MSVSProject(target = targetdir + '.vcproj', # + self['MSVSPROJECTSUFFIX'],
802                          srcs = sources,
803                          buildtarget = lib,
804                          variant = 'Release',
805                          auto_build_solution=0)
806                 vcp.append(vcproject)
807                 SConsEnvironment.Default(self, vcproject)
808         else:
809             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
810         # note: libs is a global
811         add_lib_to_dict(self, libs, libtype, libname, priority)
812
813     def BlenderProg(self=None, builddir=None, progname=None, sources=None, libs=None, libpath=None, binarykind=''):
814         global vcp
815         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
816         lenv = self.Clone()
817         lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
818         if lenv['OURPLATFORM'] in ('win32-vc', 'cygwin', 'win64-vc'):
819             if lenv['BF_DEBUG']:
820                 lenv.Prepend(LINKFLAGS = ['/DEBUG','/PDB:'+progname+'.pdb','/NODEFAULTLIB:libcmt'])
821         if  lenv['OURPLATFORM']=='linux':
822             if lenv['WITH_BF_PYTHON']:
823                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
824         if  lenv['OURPLATFORM']=='sunos5':
825             if lenv['WITH_BF_PYTHON']:
826                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
827             if lenv['CXX'].endswith('CC'):
828                 lenv.Replace(LINK = '$CXX')
829         if  lenv['OURPLATFORM']=='darwin':
830             if lenv['WITH_BF_PYTHON']:
831                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
832             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
833         if lenv['BF_PROFILE']:
834             lenv.Append(LINKFLAGS = lenv['BF_PROFILE_LINKFLAGS'])
835         if root_build_dir[0]==os.sep or root_build_dir[1]==':':
836             lenv.Append(LIBPATH=root_build_dir + '/lib')
837         lenv.Append(LIBPATH=libpath)
838         lenv.Append(LIBS=libs)
839         if lenv['WITH_BF_QUICKTIME']:
840             lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
841             lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
842         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
843         if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc') and lenv['BF_BSC']:
844             f = lenv.File(progname + '.bsc', builddir)
845             brs = lenv.Command(f, prog, [bsc])
846             SConsEnvironment.Default(self, brs)
847         SConsEnvironment.Default(self, prog)
848         if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc') and progname == 'blender':
849             print "! ",builddir + "/" + progname + '.sln'
850             sln = self.MSVSProject(target = builddir + "/" + progname + '.sln',
851                      projects= vcp,
852                      variant = 'Release')
853             SConsEnvironment.Default(self, sln)
854         program_list.append(prog)
855         if  lenv['OURPLATFORM']=='darwin':
856             lenv['BINARYKIND'] = binarykind
857             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
858         elif os.sep == '/' and lenv['OURPLATFORM'] != 'linuxcross': # any unix (except cross-compilation)
859             if lenv['WITH_BF_PYTHON']:
860                 if not lenv['WITHOUT_BF_INSTALL'] and not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
861                     lenv.AddPostAction(prog,Action(UnixPyBundle,strfunction=my_unixpybundle_print))
862                     BlenderEnvironment.PyBundleActionAdded = True
863         elif lenv['OURPLATFORM'].startswith('win') or lenv['OURPLATFORM'] == 'linuxcross': # windows or cross-compilation
864             if lenv['WITH_BF_PYTHON']:
865                 if not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
866                     lenv.AddPostAction(prog,Action(WinPyBundle,strfunction=my_winpybundle_print))
867                     BlenderEnvironment.PyBundleActionAdded = True
868         return prog
869
870     def Glob(lenv, pattern):
871         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
872         files = []
873         for i in glob.glob(path + pattern):
874             files.append(string.replace(i, path, ''))
875         return files