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