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