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