No tabs in scons! Sorry for that..
[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
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     if not lenv['WITH_BF_FREETYPE_STATIC']:
222         syslibs += Split(lenv['BF_FREETYPE_LIB'])
223     if lenv['WITH_BF_PYTHON'] and not lenv['WITH_BF_STATICPYTHON']:
224         if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc', 'win32-mingw'):
225             syslibs.append(lenv['BF_PYTHON_LIB']+'_d')
226         else:
227             syslibs.append(lenv['BF_PYTHON_LIB'])
228     if lenv['WITH_BF_INTERNATIONAL'] and not lenv['WITH_BF_GETTEXT_STATIC']:
229         syslibs += Split(lenv['BF_GETTEXT_LIB'])
230     if lenv['WITH_BF_OPENAL']:
231         if not lenv['WITH_BF_STATICOPENAL']:
232             syslibs += Split(lenv['BF_OPENAL_LIB'])
233     if lenv['WITH_BF_OPENMP'] and lenv['CC'] != 'icc':
234         if lenv['CC'] == 'cl.exe':
235             syslibs += ['vcomp']
236         else:
237             syslibs += ['gomp']
238     if lenv['WITH_BF_ICONV']:
239         syslibs += Split(lenv['BF_ICONV_LIB'])
240     if lenv['WITH_BF_OIIO']:
241         syslibs += Split(lenv['BF_OIIO_LIB'])
242
243     if lenv['WITH_BF_OPENEXR'] and not lenv['WITH_BF_STATICOPENEXR']:
244         syslibs += Split(lenv['BF_OPENEXR_LIB'])
245     if lenv['WITH_BF_TIFF'] and not lenv['WITH_BF_STATICTIFF']:
246         syslibs += Split(lenv['BF_TIFF_LIB'])
247     if lenv['WITH_BF_ZLIB'] and not lenv['WITH_BF_STATICZLIB']:
248         syslibs += Split(lenv['BF_ZLIB_LIB'])
249     if lenv['WITH_BF_FFMPEG'] and not lenv['WITH_BF_STATICFFMPEG']:
250         syslibs += Split(lenv['BF_FFMPEG_LIB'])
251         if lenv['WITH_BF_OGG']:
252             syslibs += Split(lenv['BF_OGG_LIB'])
253     if lenv['WITH_BF_JACK']:
254             syslibs += Split(lenv['BF_JACK_LIB'])
255     if lenv['WITH_BF_SNDFILE'] and not lenv['WITH_BF_STATICSNDFILE']:
256             syslibs += Split(lenv['BF_SNDFILE_LIB'])
257     if lenv['WITH_BF_FFTW3'] and not lenv['WITH_BF_STATICFFTW3']:
258         syslibs += Split(lenv['BF_FFTW3_LIB'])
259     if lenv['WITH_BF_SDL']:
260         syslibs += Split(lenv['BF_SDL_LIB'])
261     if not lenv['WITH_BF_STATICOPENGL']:
262         syslibs += Split(lenv['BF_OPENGL_LIB'])
263     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw','linuxcross', 'win64-vc'):
264         syslibs += Split(lenv['BF_PTHREADS_LIB'])
265     if lenv['WITH_BF_COLLADA']:
266         syslibs.append(lenv['BF_PCRE_LIB'])
267         if lenv['BF_DEBUG']:
268             syslibs += [colladalib+'_d' for colladalib in Split(lenv['BF_OPENCOLLADA_LIB'])]
269         else:
270             syslibs += Split(lenv['BF_OPENCOLLADA_LIB'])
271         syslibs.append(lenv['BF_EXPAT_LIB'])
272
273     if lenv['WITH_BF_JEMALLOC']:
274         if not lenv['WITH_BF_STATICJEMALLOC']:
275             syslibs += Split(lenv['BF_JEMALLOC_LIB'])
276
277     if lenv['OURPLATFORM']=='linux':
278         if lenv['WITH_BF_3DMOUSE']:
279             if not lenv['WITH_BF_STATIC3DMOUSE']:
280                 syslibs += Split(lenv['BF_3DMOUSE_LIB'])
281                 
282     if lenv['WITH_BF_BOOST']:
283         syslibs += Split(lenv['BF_BOOST_LIB'])
284
285     syslibs += Split(lenv['BF_JPEG_LIB'])
286     syslibs += Split(lenv['BF_PNG_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     sourcedir = bldroot + '/source/darwin/%s.app'%binary
554     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
555     targetinfo = installdir +'/' + "%s.app/Contents/Info.plist"%binary
556     cmd = installdir + '/' +'%s.app'%binary
557     
558     if os.path.isdir(cmd):
559         shutil.rmtree(cmd)
560     shutil.copytree(sourcedir, cmd)
561     cmd = "cat %s | sed s/\$\{MACOSX_BUNDLE_SHORT_VERSION_STRING\}/%s/ | "%(sourceinfo,VERSION)
562     cmd += "sed s/\$\{MACOSX_BUNDLE_LONG_VERSION_STRING\}/%s,\ %s/g > %s"%(VERSION,time.strftime("%Y-%b-%d"),targetinfo)
563     commands.getoutput(cmd)
564     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,installdir, binary, binary)
565     commands.getoutput(cmd)
566     cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/'%(installdir, binary, VERSION)
567     commands.getoutput(cmd)
568     cmd = installdir + '/%s.app/Contents/MacOS/%s'%(binary,VERSION)
569
570     # blenderplayer doesn't need all the files
571     if binary == 'blender':
572         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/datafiles'%(installdir, binary, VERSION)
573         commands.getoutput(cmd)
574         cmd = 'cp -R %s/release/datafiles/locale %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
575         commands.getoutput(cmd)
576         cmd = 'cp -R %s/release/datafiles/fonts %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
577         commands.getoutput(cmd)
578         cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/%s/'%(bldroot,installdir,binary,VERSION)
579         commands.getoutput(cmd)
580
581         if env['WITH_BF_CYCLES']:
582             croot = '%s/intern/cycles' % (bldroot)
583             cinstalldir = '%s/%s.app/Contents/MacOS/%s/scripts/addons/cycles' % (installdir,binary,VERSION)
584
585             cmd = 'mkdir %s' % (cinstalldir)
586             commands.getoutput(cmd)
587             cmd = 'mkdir %s/kernel' % (cinstalldir)
588             commands.getoutput(cmd)
589             cmd = 'cp -R %s/blender/addon/*.py %s/' % (croot, cinstalldir)
590             commands.getoutput(cmd)
591             cmd = 'cp -R %s/doc/license %s/license' % (croot, cinstalldir)
592             commands.getoutput(cmd)
593             cmd = 'cp -R %s/kernel/*.h %s/kernel/*.cl %s/kernel/*.cu %s/kernel/' % (croot, croot, croot, cinstalldir)
594             commands.getoutput(cmd)
595             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)
596             commands.getoutput(cmd)
597
598     if env['WITH_OSX_STATICPYTHON']:
599         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/python/'%(installdir,binary, VERSION)
600         commands.getoutput(cmd)
601         cmd = 'unzip -q %s/release/%s -d %s/%s.app/Contents/MacOS/%s/python/'%(libdir,python_zip,installdir,binary,VERSION)
602         commands.getoutput(cmd)
603
604     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(installdir,binary, binary)
605     commands.getoutput(cmd)
606     cmd = 'find %s/%s.app -name .svn -prune -exec rm -rf {} \;'%(installdir, binary)
607     commands.getoutput(cmd)
608     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(installdir, binary)
609     commands.getoutput(cmd)
610     cmd = 'find %s/%s.app -name __MACOSX -exec rm -rf {} \;'%(installdir, binary)
611     commands.getoutput(cmd)
612     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
613         cmd = 'mkdir %s/%s.app/Contents/MacOS/lib'%(installdir, binary)
614         commands.getoutput(cmd)
615         instname = env['BF_CXX']
616         cmd = 'cp %s/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/'%(instname, installdir, binary)
617         commands.getoutput(cmd)
618         cmd = 'install_name_tool -id @executable_path/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/libgcc_s.1.dylib'%(installdir, binary)
619         commands.getoutput(cmd)
620         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)
621         commands.getoutput(cmd)
622         cmd = 'rm -rf  %s/set_simulation_threads.app'%(installdir) # first clear omp_num_threads applescript
623         commands.getoutput(cmd)
624         cmd = 'cp -R %s/source/darwin/set_simulation_threads.app %s/'%(bldroot, installdir) # copy the omp_num_threads applescript
625         commands.getoutput(cmd)
626
627 # extract copy system python, be sure to update other build systems
628 # when making changes to the files that are copied.
629 def my_unixpybundle_print(target, source, env):
630     pass
631
632 def UnixPyBundle(target=None, source=None, env=None):
633     # Any Unix except osx
634     #-- VERSION/python/lib/python3.1
635     
636     import commands
637     
638     def run(cmd):
639         print 'Install command:', cmd
640         commands.getoutput(cmd)
641
642     dir = os.path.join(env['BF_INSTALLDIR'], VERSION)
643
644     py_src =    env.subst( env['BF_PYTHON_LIBPATH'] + '/python'+env['BF_PYTHON_VERSION'] )
645     py_target =    env.subst( dir + '/python/lib/python'+env['BF_PYTHON_VERSION'] )
646     
647     # This is a bit weak, but dont install if its been installed before, makes rebuilds quite slow.
648     if os.path.exists(py_target):
649         print 'Using existing python from:'
650         print '\t"%s"' %            py_target
651         print '\t(skipping copy)\n'
652         return
653
654     # Copied from source/creator/CMakeLists.txt, keep in sync.
655     print 'Install python from:'
656     print '\t"%s" into...' % py_src
657     print '\t"%s"\n' % py_target
658
659     run("rm -rf '%s'" % py_target)
660     try:
661         os.makedirs(os.path.dirname(py_target)) # the final part is copied
662     except:
663         pass
664
665     run("cp -R '%s' '%s'" % (py_src, os.path.dirname(py_target)))
666     run("rm -rf '%s/distutils'" % py_target)
667     run("rm -rf '%s/lib2to3'" % py_target)
668     run("rm -rf '%s/config'" % py_target)
669
670     for f in os.listdir(py_target):
671         if f.startswith("config-"):
672             run("rm -rf '%s/%s'" % (py_target, f))
673
674     run("rm -rf '%s/site-packages'" % py_target)
675     run("mkdir '%s/site-packages'" % py_target)    # python needs it.'
676     run("rm -rf '%s/idlelib'" % py_target)
677     run("rm -rf '%s/tkinter'" % py_target)
678     run("rm -rf '%s/turtledemo'" % py_target)
679     run("rm -r '%s/turtle.py'" % py_target)
680     run("rm -f '%s/lib-dynload/_tkinter.so'" % py_target)
681
682     run("find '%s' -type d -name 'test' -prune -exec rm -rf {} ';'" % py_target)
683     run("find '%s' -type d -name '__pycache__' -exec rm -rf {} ';'" % py_target)
684     run("find '%s' -name '*.py[co]' -exec rm -rf {} ';'" % py_target)
685     run("find '%s' -name '*.so' -exec strip -s {} ';'" % py_target)
686     
687
688 #### END ACTION STUFF #########
689
690 def bsc(env, target, source):
691     
692     bd = os.path.dirname(target[0].abspath)
693     bscfile = '\"'+target[0].abspath+'\"'
694     bscpathcollect = '\"'+bd + os.sep + '*.sbr\"'
695     bscpathtmp = '\"'+bd + os.sep + 'bscmake.tmp\"'
696
697     os.system('dir /b/s '+bscpathcollect+' >'+bscpathtmp)
698
699     myfile = open(bscpathtmp[1:-1], 'r')
700     lines = myfile.readlines()
701     myfile.close()
702
703     newfile = open(bscpathtmp[1:-1], 'w')
704     for l in lines:
705         newfile.write('\"'+l[:-1]+'\"\n')
706     newfile.close()
707                 
708     os.system('bscmake /nologo /n /o'+bscfile+' @'+bscpathtmp)
709     os.system('del '+bscpathtmp)
710
711 class BlenderEnvironment(SConsEnvironment):
712
713     PyBundleActionAdded = False
714
715     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
716         global libs
717         if not self or not libname or not source:
718             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
719             self.Exit()
720         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross', 'win64-vc'):
721             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
722             self.Exit()
723         
724         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
725         lenv = self.Clone()
726         if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
727             res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
728         else:
729             res = lenv.RES(root_build_dir+'lib/'+libname, source)
730
731         
732         SConsEnvironment.Default(self, res)
733         resources.append(res)
734
735     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):
736         global vcp
737         if not self or not libname or not sources:
738             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
739             self.Exit()
740
741         def list_substring(quickie, libname):
742             for q in quickie:
743                 if libname.find(q) != -1:
744                     return True
745             return False
746
747         if list_substring(quickie, libname) or len(quickie)==0:
748             if list_substring(quickdebug, libname):
749                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname +bc.ENDC+bc.OKBLUE+ " (debug mode)" + bc.ENDC
750             else:
751                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname + bc.ENDC
752             lenv = self.Clone()
753             lenv.Append(CPPPATH=includes)
754             lenv.Append(CPPDEFINES=defines)
755             if lenv['BF_DEBUG'] or (libname in quickdebug):
756                     lenv.Append(CFLAGS = lenv['BF_DEBUG_CFLAGS'])
757                     lenv.Append(CCFLAGS = lenv['BF_DEBUG_CCFLAGS'])
758                     lenv.Append(CXXFLAGS = lenv['BF_DEBUG_CXXFLAGS'])
759             else:
760                     lenv.Append(CFLAGS = lenv['REL_CFLAGS'])
761                     lenv.Append(CCFLAGS = lenv['REL_CCFLAGS'])
762                     lenv.Append(CXXFLAGS = lenv['REL_CXXFLAGS'])
763             if lenv['BF_PROFILE']:
764                     lenv.Append(CFLAGS = lenv['BF_PROFILE_CFLAGS'])
765                     lenv.Append(CCFLAGS = lenv['BF_PROFILE_CCFLAGS'])
766                     lenv.Append(CXXFLAGS = lenv['BF_PROFILE_CXXFLAGS'])
767             if compileflags:
768                 lenv.Replace(CFLAGS = compileflags)
769             if cc_compileflags:
770                 lenv.Replace(CCFLAGS = cc_compileflags)
771             if cxx_compileflags:
772                 lenv.Replace(CXXFLAGS = cxx_compileflags)
773             if cc_compilerchange:
774                 lenv.Replace(CC = cc_compilerchange)
775             if cxx_compilerchange:
776                 lenv.Replace(CXX = cxx_compilerchange)
777             lenv.Append(CFLAGS = lenv['C_WARN'])
778             lenv.Append(CCFLAGS = lenv['CC_WARN'])
779             lenv.Append(CXXFLAGS = lenv['CXX_WARN'])
780
781             if lenv['OURPLATFORM'] == 'win64-vc':
782                 lenv.Append(LINKFLAGS = ['/MACHINE:X64'])
783
784             if lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
785                 if lenv['BF_DEBUG']:
786                     lenv.Append(CCFLAGS = ['/MTd'])
787                 else:
788                     lenv.Append(CCFLAGS = ['/MT'])
789             
790             targetdir = root_build_dir+'lib/' + libname
791             if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
792                 targetdir = '#'+targetdir
793             lib = lenv.Library(target= targetdir, source=sources)
794             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
795             if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
796                 #if targetdir[0] == '#':
797                 #    targetdir = targetdir[1:-1]
798                 print "! ",targetdir+ '.vcproj' # + self['MSVSPROJECTSUFFIX']
799                 vcproject = self.MSVSProject(target = targetdir + '.vcproj', # + self['MSVSPROJECTSUFFIX'],
800                          srcs = sources,
801                          buildtarget = lib,
802                          variant = 'Release',
803                          auto_build_solution=0)
804                 vcp.append(vcproject)
805                 SConsEnvironment.Default(self, vcproject)
806         else:
807             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
808         # note: libs is a global
809         add_lib_to_dict(self, libs, libtype, libname, priority)
810
811     def BlenderProg(self=None, builddir=None, progname=None, sources=None, libs=None, libpath=None, binarykind=''):
812         global vcp
813         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
814         lenv = self.Clone()
815         lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
816         if lenv['OURPLATFORM'] in ('win32-vc', 'cygwin', 'win64-vc'):
817             if lenv['BF_DEBUG']:
818                 lenv.Prepend(LINKFLAGS = ['/DEBUG','/PDB:'+progname+'.pdb','/NODEFAULTLIB:libcmt'])
819         if  lenv['OURPLATFORM']=='linux':
820             if lenv['WITH_BF_PYTHON']:
821                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
822         if  lenv['OURPLATFORM']=='sunos5':
823             if lenv['WITH_BF_PYTHON']:
824                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
825             if lenv['CXX'].endswith('CC'):
826                  lenv.Replace(LINK = '$CXX')
827         if  lenv['OURPLATFORM']=='darwin':
828             if lenv['WITH_BF_PYTHON']:
829                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
830             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
831         if lenv['BF_PROFILE']:
832             lenv.Append(LINKFLAGS = lenv['BF_PROFILE_LINKFLAGS'])
833         if root_build_dir[0]==os.sep or root_build_dir[1]==':':
834             lenv.Append(LIBPATH=root_build_dir + '/lib')
835         lenv.Append(LIBPATH=libpath)
836         lenv.Append(LIBS=libs)
837         if lenv['WITH_BF_QUICKTIME']:
838              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
839              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
840         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
841         if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc') and lenv['BF_BSC']:
842             f = lenv.File(progname + '.bsc', builddir)
843             brs = lenv.Command(f, prog, [bsc])
844             SConsEnvironment.Default(self, brs)
845         SConsEnvironment.Default(self, prog)
846         if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc') and progname == 'blender':
847             print "! ",builddir + "/" + progname + '.sln'
848             sln = self.MSVSProject(target = builddir + "/" + progname + '.sln',
849                      projects= vcp,
850                      variant = 'Release')
851             SConsEnvironment.Default(self, sln)
852         program_list.append(prog)
853         if  lenv['OURPLATFORM']=='darwin':
854             lenv['BINARYKIND'] = binarykind
855             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
856         elif os.sep == '/' and lenv['OURPLATFORM'] != 'linuxcross': # any unix (except cross-compilation)
857             if lenv['WITH_BF_PYTHON']:
858                 if not lenv['WITHOUT_BF_INSTALL'] and not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
859                     lenv.AddPostAction(prog,Action(UnixPyBundle,strfunction=my_unixpybundle_print))
860                     BlenderEnvironment.PyBundleActionAdded = True
861         elif lenv['OURPLATFORM'].startswith('win') or lenv['OURPLATFORM'] == 'linuxcross': # windows or cross-compilation
862             if lenv['WITH_BF_PYTHON']:
863                 if not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
864                     lenv.AddPostAction(prog,Action(WinPyBundle,strfunction=my_winpybundle_print))
865                     BlenderEnvironment.PyBundleActionAdded = True
866         return prog
867
868     def Glob(lenv, pattern):
869         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
870         files = []
871         for i in glob.glob(path + pattern):
872             files.append(string.replace(i, path, ''))
873         return files