Add compiles support for Cycles.
[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     if lenv['WITH_BF_OIIO']:
289         syslibs += Split(lenv['BF_OIIO_LIB'])
290     if lenv['WITH_BF_BOOST']:
291         syslibs += Split(lenv['BF_BOOST_LIB'])
292
293     syslibs += lenv['LLIBS']
294
295     return syslibs
296
297 def propose_priorities():
298     print bc.OKBLUE+"Priorities:"+bc.ENDC
299     for t in possible_types:
300         print bc.OKGREEN+"\t"+t+bc.ENDC
301         new_priority = 0
302         curlib = libs[t]
303         sortlist = curlib.keys()
304         sortlist.sort()
305
306         for sk in sortlist:
307             v = curlib[sk]
308             #for p,v in sorted(libs[t].iteritems()):
309             print "\t\t",new_priority, v
310             new_priority += 5
311
312 # emits the necessary file objects for creator.c, to be used in creating
313 # the final blender executable
314 def creator(env):
315     sources = ['creator.c']# + Blender.buildinfo(env, "dynamic") + Blender.resources
316
317     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']]
318
319     defs = []
320     if env['WITH_BF_QUICKTIME']:
321         incs.append(env['BF_QUICKTIME_INC'])
322         defs.append('WITH_QUICKTIME')
323
324     if env['WITH_BF_BINRELOC']:
325         incs.append('#/extern/binreloc/include')
326         defs.append('WITH_BINRELOC')
327
328     if env['WITH_BF_OPENEXR']:
329         defs.append('WITH_OPENEXR')
330
331     if env['WITH_BF_TIFF']:
332         defs.append('WITH_TIFF')
333
334     if env['WITH_BF_SDL']:
335         defs.append('WITH_SDL')
336
337     if env['WITH_BF_LIBMV']:
338         incs.append('#/extern/libmv')
339         defs.append('WITH_LIBMV')
340
341     if env['WITH_BF_PYTHON']:
342         incs.append('#/source/blender/python')
343         defs.append('WITH_PYTHON')
344         if env['BF_DEBUG']:
345             defs.append('_DEBUG')
346
347     if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
348         incs.append(env['BF_PTHREADS_INC'])
349
350     env.Append(CPPDEFINES=defs)
351     env.Append(CPPPATH=incs)
352     obj = [env.Object(root_build_dir+'source/creator/creator/creator', ['#source/creator/creator.c'])]
353
354     return obj
355
356 ## TODO: see if this can be made in an emitter
357 def buildinfo(lenv, build_type):
358     """
359     Generate a buildinfo object
360     """
361     build_date = time.strftime ("%Y-%m-%d")
362     build_time = time.strftime ("%H:%M:%S")
363     build_rev = os.popen('svnversion').read()[:-1] # remove \n
364     if build_rev == '': 
365         build_rev = '-UNKNOWN-'
366     if lenv['BF_DEBUG']:
367         build_type = "Debug"
368         build_cflags = ' '.join(lenv['CFLAGS'] + lenv['CCFLAGS'] + lenv['BF_DEBUG_CCFLAGS'] + lenv['CPPFLAGS'])
369         build_cxxflags = ' '.join(lenv['CCFLAGS'] + lenv['CXXFLAGS'] + lenv['CPPFLAGS'])
370     else:
371         build_type = "Release"
372         build_cflags = ' '.join(lenv['CFLAGS'] + lenv['CCFLAGS'] + lenv['REL_CFLAGS'] + lenv['REL_CCFLAGS'] + lenv['CPPFLAGS'])
373         build_cxxflags = ' '.join(lenv['CCFLAGS'] + lenv['CXXFLAGS'] + lenv['REL_CXXFLAGS'] + lenv['REL_CCFLAGS'] + lenv['CPPFLAGS'])
374
375     build_linkflags = ' '.join(lenv['PLATFORM_LINKFLAGS'])
376
377     obj = []
378     if lenv['BF_BUILDINFO']:
379         lenv.Append (CPPDEFINES = ['BUILD_TIME=\\"%s\\"'%(build_time),
380                                     'BUILD_DATE=\\"%s\\"'%(build_date),
381                                     'BUILD_TYPE=\\"%s\\"'%(build_type),
382                                     'BUILD_REV=\\"%s\\"'%(build_rev),
383                                     'WITH_BUILDINFO',
384                                     'BUILD_PLATFORM=\\"%s:%s\\"'%(platform.system(), platform.architecture()[0]),
385                                     'BUILD_CFLAGS=\\"%s\\"'%(build_cflags),
386                                     'BUILD_CXXFLAGS=\\"%s\\"'%(build_cxxflags),
387                                     'BUILD_LINKFLAGS=\\"%s\\"'%(build_linkflags),
388                                     'BUILD_SYSTEM=\\"SCons\\"'
389                     ])
390
391         lenv.Append (CPPPATH = [root_build_dir+'source/blender/blenkernel'])
392
393         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type, ['#source/creator/buildinfo.c'])]
394
395     return obj
396
397 ##### END LIB STUFF ############
398
399 ##### ACTION STUFF #############
400
401 def my_print_cmd_line(self, s, target, source, env):
402     sys.stdout.write(' ' * 70 + '\r')
403     sys.stdout.flush()
404     sys.stdout.write(s + "\r")
405     sys.stdout.flush()
406
407 def my_compile_print(target, source, env):
408     a = '%s' % (source[0])
409     d, f = os.path.split(a)
410     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
411
412 def my_moc_print(target, source, env):
413     a = '%s' % (source[0])
414     d, f = os.path.split(a)
415     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
416
417 def my_linking_print(target, source, env):
418     t = '%s' % (target[0])
419     d, f = os.path.split(t)
420     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
421
422 def my_program_print(target, source, env):
423     t = '%s' % (target[0])
424     d, f = os.path.split(t)
425     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
426
427 def msvc_hack(env):
428     static_lib = SCons.Tool.createStaticLibBuilder(env)
429     program = SCons.Tool.createProgBuilder(env)
430     
431     env['BUILDERS']['Library'] = static_lib
432     env['BUILDERS']['StaticLibrary'] = static_lib
433     env['BUILDERS']['Program'] = program
434         
435 def set_quiet_output(env):
436     mycaction = Action("$CCCOM", strfunction=my_compile_print)
437     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
438     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
439     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
440     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
441     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
442
443     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
444     static_ob.add_action('.c', mycaction)
445     static_ob.add_action('.cpp', mycppaction)
446     shared_ob.add_action('.c', myshcaction)
447     shared_ob.add_action('.cpp', myshcppaction)
448
449     static_lib = SCons.Builder.Builder(action = mylibaction,
450                                        emitter = '$LIBEMITTER',
451                                        prefix = '$LIBPREFIX',
452                                        suffix = '$LIBSUFFIX',
453                                        src_suffix = '$OBJSUFFIX',
454                                        src_builder = 'StaticObject')
455
456     program = SCons.Builder.Builder(action = mylinkaction,
457                                     emitter = '$PROGEMITTER',
458                                     prefix = '$PROGPREFIX',
459                                     suffix = '$PROGSUFFIX',
460                                     src_suffix = '$OBJSUFFIX',
461                                     src_builder = 'Object',
462                                     target_scanner = SCons.Defaults.ProgScan)
463
464     env['BUILDERS']['Object'] = static_ob
465     env['BUILDERS']['StaticObject'] = static_ob
466     env['BUILDERS']['StaticLibrary'] = static_lib
467     env['BUILDERS']['Library'] = static_lib
468     env['BUILDERS']['Program'] = program
469     if env['BF_LINE_OVERWRITE']:
470         SCons.Action._ActionAction.print_cmd_line = my_print_cmd_line
471
472 def untar_pybundle(from_tar,to_dir,exclude_re):
473     tar= tarfile.open(from_tar, mode='r')
474     exclude_re= list(exclude_re) #single re object or list of re objects
475     debug= 0 #list files instead of unpacking
476     good= []
477     if debug: print '\nFiles not being unpacked:\n'
478     for name in tar.getnames():
479         is_bad= 0
480         for r in exclude_re:
481             if r.match(name):
482                 is_bad=1
483                 if debug: print name
484                 break
485         if not is_bad:
486             good.append(tar.getmember(name))
487     if debug:
488         print '\nFiles being unpacked:\n'
489         for g in good:
490             print g
491     else:
492         tar.extractall(to_dir, good)
493
494 def my_winpybundle_print(target, source, env):
495     pass
496
497 def WinPyBundle(target=None, source=None, env=None):
498     import re
499     py_tar= env.subst( env['LCGDIR'] )
500     if py_tar[0]=='#':
501         py_tar= py_tar[1:]
502     if env['BF_DEBUG']:
503         py_tar+= '/release/python' + env['BF_PYTHON_VERSION'].replace('.','') + '_d.tar.gz'
504     else:
505         py_tar+= '/release/python' + env['BF_PYTHON_VERSION'].replace('.','') + '.tar.gz'
506
507     py_target = env.subst( env['BF_INSTALLDIR'] )
508     if py_target[0]=='#':
509         py_target=py_target[1:]
510     py_target = os.path.join(py_target, VERSION, 'python', 'lib')
511     def printexception(func,path,ex):
512         if os.path.exists(path): #do not report if path does not exist. eg on a fresh build.
513             print str(func) + ' failed on ' + str(path)
514     print "Trying to remove existing py bundle."
515     shutil.rmtree(py_target, False, printexception)
516     exclude_re=[re.compile('.*/test/.*'),
517                 re.compile('^config/.*'),
518                 re.compile('^config-*/.*'),
519                 re.compile('^distutils/.*'),
520                 re.compile('^idlelib/.*'),
521                 re.compile('^lib2to3/.*'),
522                 re.compile('^tkinter/.*'),
523                 re.compile('^_tkinter_d.pyd'),
524                 re.compile('^turtledemo'),
525                 re.compile('^turtle.py'),
526                 ]
527
528     print "Unpacking '" + py_tar + "' to '" + py_target + "'"
529     untar_pybundle(py_tar,py_target,exclude_re)
530
531 def  my_appit_print(target, source, env):
532     a = '%s' % (target[0])
533     d, f = os.path.split(a)
534     return "making bundle for " + f
535
536 def AppIt(target=None, source=None, env=None):
537     import shutil
538     import commands
539     import os.path
540     
541     
542     a = '%s' % (target[0])
543     builddir, b = os.path.split(a)
544     libdir = env['LCGDIR'][1:]
545     osxarch = env['MACOSX_ARCHITECTURE']
546     installdir = env['BF_INSTALLDIR']
547     print("compiled architecture: %s"%(osxarch))
548     print("Installing to %s"%(installdir))
549     # TODO, use tar.
550     python_zip = 'python_' + osxarch + '.zip' # set specific python_arch.zip
551     if env['WITH_OSX_STATICPYTHON']:
552         print("unzipping to app-bundle: %s"%(python_zip))
553     else:
554         print("dynamic build - make sure to have python3.x-framework installed")
555     bldroot = env.Dir('.').abspath
556     binary = env['BINARYKIND']
557      
558     if b=='verse':
559         print bc.OKBLUE+"no bundle for verse"+bc.ENDC 
560         return 0
561     
562     sourcedir = bldroot + '/source/darwin/%s.app'%binary
563     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
564     targetinfo = installdir +'/' + "%s.app/Contents/Info.plist"%binary
565     cmd = installdir + '/' +'%s.app'%binary
566     
567     if os.path.isdir(cmd):
568         shutil.rmtree(cmd)
569     shutil.copytree(sourcedir, cmd)
570     cmd = "cat %s | sed s/\$\{MACOSX_BUNDLE_SHORT_VERSION_STRING\}/%s/ | "%(sourceinfo,VERSION)
571     cmd += "sed s/\$\{MACOSX_BUNDLE_LONG_VERSION_STRING\}/%s,\ %s/g > %s"%(VERSION,time.strftime("%Y-%b-%d"),targetinfo)
572     commands.getoutput(cmd)
573     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,installdir, binary, binary)
574     commands.getoutput(cmd)
575     cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/'%(installdir, binary, VERSION)
576     commands.getoutput(cmd)
577     cmd = installdir + '/%s.app/Contents/MacOS/%s'%(binary,VERSION)
578
579     # blenderplayer doesn't need all the files
580     if binary == 'blender':
581         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/datafiles'%(installdir, binary, VERSION)
582         commands.getoutput(cmd)
583         cmd = 'cp -R %s/release/datafiles/locale %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
584         commands.getoutput(cmd)
585         cmd = 'cp -R %s/release/datafiles/fonts %s/%s.app/Contents/MacOS/%s/datafiles/'%(bldroot,installdir,binary,VERSION)
586         commands.getoutput(cmd)
587         cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/%s/'%(bldroot,installdir,binary,VERSION)
588         commands.getoutput(cmd)
589
590     if env['WITH_OSX_STATICPYTHON']:
591         cmd = 'mkdir %s/%s.app/Contents/MacOS/%s/python/'%(installdir,binary, VERSION)
592         commands.getoutput(cmd)
593         cmd = 'unzip -q %s/release/%s -d %s/%s.app/Contents/MacOS/%s/python/'%(libdir,python_zip,installdir,binary,VERSION)
594         commands.getoutput(cmd)
595
596     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(installdir,binary, binary)
597     commands.getoutput(cmd)
598     cmd = 'find %s/%s.app -name .svn -prune -exec rm -rf {} \;'%(installdir, binary)
599     commands.getoutput(cmd)
600     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(installdir, binary)
601     commands.getoutput(cmd)
602     cmd = 'find %s/%s.app -name __MACOSX -exec rm -rf {} \;'%(installdir, binary)
603     commands.getoutput(cmd)
604     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
605         cmd = 'mkdir %s/%s.app/Contents/MacOS/lib'%(installdir, binary)
606         commands.getoutput(cmd)
607         instname = env['BF_CXX']
608         cmd = 'cp %s/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/'%(instname, installdir, binary)
609         commands.getoutput(cmd)
610         cmd = 'install_name_tool -id @executable_path/lib/libgcc_s.1.dylib %s/%s.app/Contents/MacOS/lib/libgcc_s.1.dylib'%(installdir, binary)
611         commands.getoutput(cmd)
612         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)
613         commands.getoutput(cmd)
614         cmd = 'rm -rf  %s/set_simulation_threads.app'%(installdir) # first clear omp_num_threads applescript
615         commands.getoutput(cmd)
616         cmd = 'cp -R %s/source/darwin/set_simulation_threads.app %s/'%(bldroot, installdir) # copy the omp_num_threads applescript
617         commands.getoutput(cmd)
618
619 # extract copy system python, be sure to update other build systems
620 # when making changes to the files that are copied.
621 def my_unixpybundle_print(target, source, env):
622     pass
623
624 def UnixPyBundle(target=None, source=None, env=None):
625     # Any Unix except osx
626     #-- VERSION/python/lib/python3.1
627     
628     import commands
629     
630     def run(cmd):
631         print 'Install command:', cmd
632         commands.getoutput(cmd)
633
634     dir = os.path.join(env['BF_INSTALLDIR'], VERSION)
635
636     py_src =    env.subst( env['BF_PYTHON_LIBPATH'] + '/python'+env['BF_PYTHON_VERSION'] )
637     py_target =    env.subst( dir + '/python/lib/python'+env['BF_PYTHON_VERSION'] )
638     
639     # This is a bit weak, but dont install if its been installed before, makes rebuilds quite slow.
640     if os.path.exists(py_target):
641         print 'Using existing python from:'
642         print '\t"%s"' %            py_target
643         print '\t(skipping copy)\n'
644         return
645
646     # Copied from source/creator/CMakeLists.txt, keep in sync.
647     print 'Install python from:'
648     print '\t"%s" into...' % py_src
649     print '\t"%s"\n' % py_target
650
651     run("rm -rf '%s'" % py_target)
652     try:
653         os.makedirs(os.path.dirname(py_target)) # the final part is copied
654     except:
655         pass
656
657     run("cp -R '%s' '%s'" % (py_src, os.path.dirname(py_target)))
658     run("rm -rf '%s/distutils'" % py_target)
659     run("rm -rf '%s/lib2to3'" % py_target)
660     run("rm -rf '%s/config'" % py_target)
661
662     for f in os.listdir(py_target):
663         if f.startswith("config-"):
664             run("rm -rf '%s/%s'" % (py_target, f))
665
666     run("rm -rf '%s/site-packages'" % py_target)
667     run("mkdir '%s/site-packages'" % py_target)    # python needs it.'
668     run("rm -rf '%s/idlelib'" % py_target)
669     run("rm -rf '%s/tkinter'" % py_target)
670     run("rm -rf '%s/turtledemo'" % py_target)
671     run("rm -r '%s/turtle.py'" % py_target)
672     run("rm -f '%s/lib-dynload/_tkinter.so'" % py_target)
673
674     run("find '%s' -type d -name 'test' -prune -exec rm -rf {} ';'" % py_target)
675     run("find '%s' -type d -name '__pycache__' -exec rm -rf {} ';'" % py_target)
676     run("find '%s' -name '*.py[co]' -exec rm -rf {} ';'" % py_target)
677     run("find '%s' -name '*.so' -exec strip -s {} ';'" % py_target)
678     
679
680 #### END ACTION STUFF #########
681
682 def bsc(env, target, source):
683     
684     bd = os.path.dirname(target[0].abspath)
685     bscfile = '\"'+target[0].abspath+'\"'
686     bscpathcollect = '\"'+bd + os.sep + '*.sbr\"'
687     bscpathtmp = '\"'+bd + os.sep + 'bscmake.tmp\"'
688
689     os.system('dir /b/s '+bscpathcollect+' >'+bscpathtmp)
690
691     myfile = open(bscpathtmp[1:-1], 'r')
692     lines = myfile.readlines()
693     myfile.close()
694
695     newfile = open(bscpathtmp[1:-1], 'w')
696     for l in lines:
697         newfile.write('\"'+l[:-1]+'\"\n')
698     newfile.close()
699                 
700     os.system('bscmake /nologo /n /o'+bscfile+' @'+bscpathtmp)
701     os.system('del '+bscpathtmp)
702
703 class BlenderEnvironment(SConsEnvironment):
704
705     PyBundleActionAdded = False
706
707     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
708         global libs
709         if not self or not libname or not source:
710             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
711             self.Exit()
712         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross', 'win64-vc'):
713             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
714             self.Exit()
715         
716         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
717         lenv = self.Clone()
718         if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
719             res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
720         else:
721             res = lenv.RES(root_build_dir+'lib/'+libname, source)
722
723         
724         SConsEnvironment.Default(self, res)
725         resources.append(res)
726
727     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):
728         global vcp
729         if not self or not libname or not sources:
730             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
731             self.Exit()
732
733         def list_substring(quickie, libname):
734             for q in quickie:
735                 if libname.find(q) != -1:
736                     return True
737             return False
738
739         if list_substring(quickie, libname) or len(quickie)==0:
740             if list_substring(quickdebug, libname):
741                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname +bc.ENDC+bc.OKBLUE+ " (debug mode)" + bc.ENDC
742             else:
743                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname + bc.ENDC
744             lenv = self.Clone()
745             lenv.Append(CPPPATH=includes)
746             lenv.Append(CPPDEFINES=defines)
747             if lenv['BF_DEBUG'] or (libname in quickdebug):
748                     lenv.Append(CFLAGS = lenv['BF_DEBUG_CFLAGS'])
749                     lenv.Append(CCFLAGS = lenv['BF_DEBUG_CCFLAGS'])
750                     lenv.Append(CXXFLAGS = lenv['BF_DEBUG_CXXFLAGS'])
751             else:
752                     lenv.Append(CFLAGS = lenv['REL_CFLAGS'])
753                     lenv.Append(CCFLAGS = lenv['REL_CCFLAGS'])
754                     lenv.Append(CXXFLAGS = lenv['REL_CXXFLAGS'])
755             if lenv['BF_PROFILE']:
756                     lenv.Append(CFLAGS = lenv['BF_PROFILE_CFLAGS'])
757                     lenv.Append(CCFLAGS = lenv['BF_PROFILE_CCFLAGS'])
758                     lenv.Append(CXXFLAGS = lenv['BF_PROFILE_CXXFLAGS'])
759             if compileflags:
760                 lenv.Replace(CFLAGS = compileflags)
761             if cc_compileflags:
762                 lenv.Replace(CCFLAGS = cc_compileflags)
763             if cxx_compileflags:
764                 lenv.Replace(CXXFLAGS = cxx_compileflags)
765             if cc_compilerchange:
766                 lenv.Replace(CC = cc_compilerchange)
767             if cxx_compilerchange:
768                 lenv.Replace(CXX = cxx_compilerchange)
769             lenv.Append(CFLAGS = lenv['C_WARN'])
770             lenv.Append(CCFLAGS = lenv['CC_WARN'])
771             lenv.Append(CXXFLAGS = lenv['CXX_WARN'])
772
773             if lenv['OURPLATFORM'] == 'win64-vc':
774                 lenv.Append(LINKFLAGS = ['/MACHINE:X64'])
775
776             if lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
777                 if lenv['BF_DEBUG']:
778                     lenv.Append(CCFLAGS = ['/MTd'])
779                 else:
780                     lenv.Append(CCFLAGS = ['/MT'])
781             
782             targetdir = root_build_dir+'lib/' + libname
783             if not (root_build_dir[0]==os.sep or root_build_dir[1]==':'):
784                 targetdir = '#'+targetdir
785             lib = lenv.Library(target= targetdir, source=sources)
786             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
787             if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
788                 #if targetdir[0] == '#':
789                 #    targetdir = targetdir[1:-1]
790                 print "! ",targetdir+ '.vcproj' # + self['MSVSPROJECTSUFFIX']
791                 vcproject = self.MSVSProject(target = targetdir + '.vcproj', # + self['MSVSPROJECTSUFFIX'],
792                          srcs = sources,
793                          buildtarget = lib,
794                          variant = 'Release',
795                          auto_build_solution=0)
796                 vcp.append(vcproject)
797                 SConsEnvironment.Default(self, vcproject)
798         else:
799             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
800         # note: libs is a global
801         add_lib_to_dict(self, libs, libtype, libname, priority)
802
803     def BlenderProg(self=None, builddir=None, progname=None, sources=None, libs=None, libpath=None, binarykind=''):
804         global vcp
805         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
806         lenv = self.Clone()
807         lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
808         if lenv['OURPLATFORM'] in ('win32-vc', 'cygwin', 'win64-vc'):
809             if lenv['BF_DEBUG']:
810                 lenv.Prepend(LINKFLAGS = ['/DEBUG','/PDB:'+progname+'.pdb','/NODEFAULTLIB:libcmt'])
811         if  lenv['OURPLATFORM']=='linux':
812             if lenv['WITH_BF_PYTHON']:
813                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
814         if  lenv['OURPLATFORM']=='sunos5':
815             if lenv['WITH_BF_PYTHON']:
816                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
817             if lenv['CXX'].endswith('CC'):
818                  lenv.Replace(LINK = '$CXX')
819         if  lenv['OURPLATFORM']=='darwin':
820             if lenv['WITH_BF_PYTHON']:
821                 lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
822             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
823         if lenv['BF_PROFILE']:
824             lenv.Append(LINKFLAGS = lenv['BF_PROFILE_LINKFLAGS'])
825         if root_build_dir[0]==os.sep or root_build_dir[1]==':':
826             lenv.Append(LIBPATH=root_build_dir + '/lib')
827         lenv.Append(LIBPATH=libpath)
828         lenv.Append(LIBS=libs)
829         if lenv['WITH_BF_QUICKTIME']:
830              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
831              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
832         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
833         if lenv['BF_DEBUG'] and lenv['OURPLATFORM'] in ('win32-vc', 'win64-vc') and lenv['BF_BSC']:
834             f = lenv.File(progname + '.bsc', builddir)
835             brs = lenv.Command(f, prog, [bsc])
836             SConsEnvironment.Default(self, brs)
837         SConsEnvironment.Default(self, prog)
838         if self['BF_MSVS'] and self['OURPLATFORM'] in ('win32-vc', 'win64-vc') and progname == 'blender':
839             print "! ",builddir + "/" + progname + '.sln'
840             sln = self.MSVSProject(target = builddir + "/" + progname + '.sln',
841                      projects= vcp,
842                      variant = 'Release')
843             SConsEnvironment.Default(self, sln)
844         program_list.append(prog)
845         if  lenv['OURPLATFORM']=='darwin':
846             lenv['BINARYKIND'] = binarykind
847             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
848         elif os.sep == '/' and lenv['OURPLATFORM'] != 'linuxcross': # any unix (except cross-compilation)
849             if lenv['WITH_BF_PYTHON']:
850                 if not lenv['WITHOUT_BF_INSTALL'] and not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
851                     lenv.AddPostAction(prog,Action(UnixPyBundle,strfunction=my_unixpybundle_print))
852                     BlenderEnvironment.PyBundleActionAdded = True
853         elif lenv['OURPLATFORM'].startswith('win') or lenv['OURPLATFORM'] == 'linuxcross': # windows or cross-compilation
854             if lenv['WITH_BF_PYTHON']:
855                 if not lenv['WITHOUT_BF_PYTHON_INSTALL'] and not BlenderEnvironment.PyBundleActionAdded:
856                     lenv.AddPostAction(prog,Action(WinPyBundle,strfunction=my_winpybundle_print))
857                     BlenderEnvironment.PyBundleActionAdded = True
858         return prog
859
860     def Glob(lenv, pattern):
861         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
862         files = []
863         for i in glob.glob(path + pattern):
864             files.append(string.replace(i, path, ''))
865         return files