Add Fh/Rot Fh to Bullet.
[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.path
18 import string
19 import glob
20 import time
21 import sys
22
23 from SCons.Script.SConscript import SConsEnvironment
24 import SCons.Action
25 import SCons.Util
26 import SCons.Builder
27 import SCons.Tool
28 import bcolors
29 bc = bcolors.bcolors()
30
31 Split = SCons.Util.Split
32 Action = SCons.Action.Action
33 Builder = SCons.Builder.Builder
34 GetBuildPath = SConsEnvironment.GetBuildPath
35
36 # a few globals
37 root_build_dir = ''
38 quickie = None # Anything else than None if BF_QUICK has been passed
39 quicklist = [] # The list of libraries/programs to compile during a quickie
40 program_list = [] # A list holding Nodes to final binaries, used to create installs
41 arguments = None
42 targets = None
43 resources = []
44
45 #some internals
46 blenderdeps = [] # don't manipulate this one outside this module!
47
48 ##### LIB STUFF ##########
49
50 possible_types = ['core'] # can be set in ie. SConstruct
51 libs = {}
52
53 def getresources():
54     return resources
55
56 def init_lib_dict():
57     for pt in possible_types:
58         libs[pt] = {}
59
60 # helper func for add_lib_to_dict
61 def internal_lib_to_dict(dict = None, libtype = None, libname = None, priority = 100):
62     if not libname in dict[libtype]:
63         done = None
64         while not done:
65             if dict[libtype].has_key(priority):
66                 priority = priority + 1
67             else:
68                 done = True
69         dict[libtype][priority] = libname
70
71 # libtype and priority can both be lists, for defining lib in multiple places
72 def add_lib_to_dict(env, dict = None, libtype = None, libname = None, priority = 100):
73     if not dict or not libtype or not libname:
74         print "Passed wrong arg"
75         env.Exit()
76
77     if type(libtype) is str and type(priority) is int:
78         internal_lib_to_dict(dict, libtype, libname, priority)
79     elif type(libtype) is list and type(priority) is list:
80         if len(libtype)==len(priority):
81             for lt, p in zip(libtype, priority):
82                 internal_lib_to_dict(dict, lt, libname, p)
83         else:
84             print "libtype and priority lists are unequal in length"
85             env.Exit()
86     else:
87         print "Wrong type combinations for libtype and priority. Only str and int or list and list"
88         env.Exit()
89
90 def create_blender_liblist(lenv = None, libtype = None):
91     if not lenv or not libtype:
92         print "missing arg"
93
94     lst = []
95     if libtype in possible_types:
96         curlib = libs[libtype]
97         sortlist = curlib.keys()
98         sortlist.sort()
99         for sk in sortlist:
100             v = curlib[sk]
101             lst.append('#' + root_build_dir + 'lib/'+lenv['LIBPREFIX'] + v + lenv['LIBSUFFIX'])
102
103     return lst
104
105 ## TODO: static linking
106 def setup_staticlibs(lenv):
107     statlibs = [
108         #here libs for static linking
109     ]
110     libincs = [
111         '/usr/lib',
112         lenv['BF_PYTHON_LIBPATH'],
113         lenv['BF_OPENGL_LIBPATH'],
114         lenv['BF_JPEG_LIBPATH'],
115         lenv['BF_PNG_LIBPATH'],
116         lenv['BF_ZLIB_LIBPATH'],
117         lenv['BF_ICONV_LIBPATH']
118         ]
119
120     if lenv['WITH_BF_SDL']:
121         libincs += Split(lenv['BF_SDL_LIBPATH'])
122     if lenv['WITH_BF_FFMPEG']:
123         libincs += Split(lenv['BF_FFMPEG_LIBPATH'])
124     if lenv['WITH_BF_STATICCXX']:
125         statlibs += Split(lenv['BF_CXX_LIB_STATIC'])
126     if lenv['WITH_BF_OPENEXR']:
127         libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
128         if lenv['WITH_BF_STATICOPENEXR']:
129             statlibs += Split(lenv['BF_OPENEXR_LIB_STATIC'])
130     if lenv['WITH_BF_INTERNATIONAL']:
131         libincs += Split(lenv['BF_GETTEXT_LIBPATH'])
132         libincs += Split(lenv['BF_FREETYPE_LIBPATH'])
133     if lenv['WITH_BF_OPENAL']:
134         libincs += Split(lenv['BF_OPENAL_LIBPATH'])
135         if lenv['WITH_BF_STATICOPENAL']:
136             statlibs += Split(lenv['BF_OPENAL_LIB_STATIC'])
137     if lenv['WITH_BF_STATICOPENGL']:
138         statlibs += Split(lenv['BF_OPENGL_LIB_STATIC'])
139
140     if lenv['WITH_BF_STATICPYTHON']:
141         statlibs += Split(lenv['BF_PYTHON_LIB_STATIC'])
142
143     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross'):
144         libincs += Split(lenv['BF_PTHREADS_LIBPATH'])
145
146     return statlibs, libincs
147
148 def setup_syslibs(lenv):
149     syslibs = [
150         
151         lenv['BF_JPEG_LIB'],
152         lenv['BF_PNG_LIB'],
153         lenv['BF_ZLIB_LIB']
154         ]
155
156     if not lenv['WITH_BF_STATICPYTHON']:
157         if lenv['BF_DEBUG']==1 and lenv['OURPLATFORM'] in ('win32-vc'):
158             syslibs.append(lenv['BF_PYTHON_LIB']+'_d')
159         else:
160             syslibs.append(lenv['BF_PYTHON_LIB'])
161     if lenv['WITH_BF_INTERNATIONAL']:
162         syslibs += Split(lenv['BF_FREETYPE_LIB'])
163         syslibs += Split(lenv['BF_GETTEXT_LIB'])
164     if lenv['WITH_BF_OPENAL']:
165         if not lenv['WITH_BF_STATICOPENAL']:
166             syslibs += Split(lenv['BF_OPENAL_LIB'])
167     if lenv['WITH_BF_OPENMP'] and lenv['CC'] != 'icc':
168         syslibs += ['gomp']
169     if lenv['WITH_BF_ICONV']:
170         syslibs += Split(lenv['BF_ICONV_LIB'])
171     if lenv['WITH_BF_OPENEXR']:
172         if not lenv['WITH_BF_STATICOPENEXR']:
173             syslibs += Split(lenv['BF_OPENEXR_LIB'])
174     if lenv['WITH_BF_FFMPEG']:
175         syslibs += Split(lenv['BF_FFMPEG_LIB'])
176     if lenv['WITH_BF_SDL']:
177         syslibs += Split(lenv['BF_SDL_LIB'])
178     if not lenv['WITH_BF_STATICOPENGL']:
179         syslibs += Split(lenv['BF_OPENGL_LIB'])
180     if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw','linuxcross'):
181         syslibs += Split(lenv['BF_PTHREADS_LIB'])
182
183     syslibs += Split(lenv['LLIBS'])
184
185     return syslibs
186
187 def propose_priorities():
188     print bc.OKBLUE+"Priorities:"+bc.ENDC
189     for t in possible_types:
190         print bc.OKGREEN+"\t"+t+bc.ENDC
191         new_priority = 0
192         curlib = libs[t]
193         sortlist = curlib.keys()
194         sortlist.sort()
195
196         for sk in sortlist:
197             v = curlib[sk]
198             #for p,v in sorted(libs[t].iteritems()):
199             print "\t\t",new_priority, v
200             new_priority += 5
201
202 ## TODO: see if this can be made in an emitter
203 def buildinfo(lenv, build_type):
204     """
205     Generate a buildinfo object
206     """
207     build_date = time.strftime ("%Y-%m-%d")
208     build_time = time.strftime ("%H:%M:%S")
209     build_rev = os.popen('svnversion').read()[:-1] # remove \n
210
211     obj = []
212     if lenv['BF_BUILDINFO']==1: #user_options_dict['USE_BUILDINFO'] == 1:
213         if sys.platform=='win32':
214             build_info_file = open("source/creator/winbuildinfo.h", 'w')
215             build_info_file.write("char *build_date=\"%s\";\n"%build_date)
216             build_info_file.write("char *build_time=\"%s\";\n"%build_time)
217             build_info_file.write("char *build_rev=\"%s\";\n"%build_rev)
218             build_info_file.write("char *build_platform=\"win32\";\n")
219             build_info_file.write("char *build_type=\"dynamic\";\n")
220             build_info_file.close()
221             lenv.Append (CPPDEFINES = ['NAN_BUILDINFO', 'BUILD_DATE'])
222         else:
223             lenv.Append (CPPDEFINES = ['BUILD_TIME=\'"%s"\''%(build_time),
224                                         'BUILD_DATE=\'"%s"\''%(build_date),
225                                         'BUILD_TYPE=\'"dynamic"\'',
226                                         'BUILD_REV=\'"%s"\''%(build_rev),
227                                         'NAN_BUILDINFO',
228                                         'BUILD_PLATFORM=\'"%s"\''%(sys.platform)])
229         obj = [lenv.Object (root_build_dir+'source/creator/%s_buildinfo'%build_type,
230                         [root_build_dir+'source/creator/buildinfo.c'])]
231     return obj
232
233 ##### END LIB STUFF ############
234
235 ##### ACTION STUFF #############
236
237 def my_compile_print(target, source, env):
238     a = '%s' % (source[0])
239     d, f = os.path.split(a)
240     return bc.OKBLUE+"Compiling"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
241
242 def my_moc_print(target, source, env):
243     a = '%s' % (source[0])
244     d, f = os.path.split(a)
245     return bc.OKBLUE+"Creating MOC"+bc.ENDC+ " ==> '"+bc.OKGREEN+"%s" %(f) + "'"+bc.ENDC
246
247 def my_linking_print(target, source, env):
248     t = '%s' % (target[0])
249     d, f = os.path.split(t)
250     return bc.OKBLUE+"Linking library"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
251
252 def my_program_print(target, source, env):
253     t = '%s' % (target[0])
254     d, f = os.path.split(t)
255     return bc.OKBLUE+"Linking program"+bc.ENDC +" ==> '"+bc.OKGREEN+"%s" % (f) + "'"+bc.ENDC
256
257 def msvc_hack(env):
258     static_lib = SCons.Tool.createStaticLibBuilder(env)
259     program = SCons.Tool.createProgBuilder(env)
260     
261     env['BUILDERS']['Library'] = static_lib
262     env['BUILDERS']['StaticLibrary'] = static_lib
263     env['BUILDERS']['Program'] = program
264         
265 def set_quiet_output(env):
266     mycaction = Action("$CCCOM", strfunction=my_compile_print)
267     myshcaction = Action("$SHCCCOM", strfunction=my_compile_print)
268     mycppaction = Action("$CXXCOM", strfunction=my_compile_print)
269     myshcppaction = Action("$SHCXXCOM", strfunction=my_compile_print)
270     mylibaction = Action("$ARCOM", strfunction=my_linking_print)
271     mylinkaction = Action("$LINKCOM", strfunction=my_program_print)
272
273     static_ob, shared_ob = SCons.Tool.createObjBuilders(env)
274     static_ob.add_action('.c', mycaction)
275     static_ob.add_action('.cpp', mycppaction)
276     shared_ob.add_action('.c', myshcaction)
277     shared_ob.add_action('.cpp', myshcppaction)
278
279     static_lib = SCons.Builder.Builder(action = mylibaction,
280                                        emitter = '$LIBEMITTER',
281                                        prefix = '$LIBPREFIX',
282                                        suffix = '$LIBSUFFIX',
283                                        src_suffix = '$OBJSUFFIX',
284                                        src_builder = 'StaticObject')
285
286     program = SCons.Builder.Builder(action = mylinkaction,
287                                     emitter = '$PROGEMITTER',
288                                     prefix = '$PROGPREFIX',
289                                     suffix = '$PROGSUFFIX',
290                                     src_suffix = '$OBJSUFFIX',
291                                     src_builder = 'Object',
292                                     target_scanner = SCons.Defaults.ProgScan)
293
294     env['BUILDERS']['Object'] = static_ob
295     env['BUILDERS']['StaticObject'] = static_ob
296     env['BUILDERS']['StaticLibrary'] = static_lib
297     env['BUILDERS']['Library'] = static_lib
298     env['BUILDERS']['Program'] = program
299
300 def  my_appit_print(target, source, env):
301     a = '%s' % (target[0])
302     d, f = os.path.split(a)
303     return "making bundle for " + f
304
305 def AppIt(target=None, source=None, env=None):
306     import shutil
307     import commands
308     import os.path
309     
310     
311     a = '%s' % (target[0])
312     builddir, b = os.path.split(a)
313
314     bldroot = env.Dir('.').abspath
315     binary = env['BINARYKIND']
316      
317     if b=='verse':
318         print bc.OKBLUE+"no bundle for verse"+bc.ENDC 
319         return 0
320    
321     
322     sourcedir = bldroot + '/source/darwin/%s.app'%binary
323     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
324     targetinfo = builddir +'/' + "%s.app/Contents/Info.plist"%binary
325     cmd = builddir + '/' +'%s.app'%binary
326     
327     if os.path.isdir(cmd):
328         shutil.rmtree(cmd)
329     shutil.copytree(sourcedir, cmd)
330     cmd = "cat %s | sed s/VERSION/`cat release/VERSION`/ | sed s/DATE/`date +'%%Y-%%b-%%d'`/ > %s"%(sourceinfo,targetinfo)
331     commands.getoutput(cmd)
332     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,builddir, binary, binary)
333     commands.getoutput(cmd)
334     cmd = 'mkdir %s/%s.app/Contents/MacOS/.blender/'%(builddir, binary)
335     print cmd
336     commands.getoutput(cmd)
337     cmd = builddir + '/%s.app/Contents/MacOS/.blender'%binary
338     shutil.copy(bldroot + '/bin/.blender/.bfont.ttf', cmd)
339     shutil.copy(bldroot + '/bin/.blender/.Blanguages', cmd)
340     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/Resources/'%(bldroot,builddir,binary)
341     commands.getoutput(cmd) 
342     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
343     commands.getoutput(cmd) 
344     cmd = 'cp %s/bin/.blender/.Blanguages %s/%s.app/Contents/Resources/'%(bldroot,builddir,binary)
345     commands.getoutput(cmd) 
346     cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
347     commands.getoutput(cmd)
348     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(builddir,binary, binary)
349     commands.getoutput(cmd)
350     cmd = 'find %s/%s.app -name .svn -prune -exec rm -rf {} \;'%(builddir, binary)
351     commands.getoutput(cmd)
352     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(builddir, binary)
353     commands.getoutput(cmd)
354
355 #### END ACTION STUFF #########
356
357 def bsc(env, target, source):
358     
359     bd = os.path.dirname(target[0].abspath)
360     bscfile = '\"'+target[0].abspath+'\"'
361     bscpathcollect = '\"'+bd + os.sep + '*.sbr\"'
362     bscpathtmp = '\"'+bd + os.sep + 'bscmake.tmp\"'
363
364     os.system('dir /b/s '+bscpathcollect+' >'+bscpathtmp)
365
366     myfile = open(bscpathtmp[1:-1], 'r')
367     lines = myfile.readlines()
368     myfile.close()
369
370     newfile = open(bscpathtmp[1:-1], 'w')
371     for l in lines:
372         newfile.write('\"'+l[:-1]+'\"\n')
373     newfile.close()
374                 
375     os.system('bscmake /nologo /n /o'+bscfile+' @'+bscpathtmp)
376     os.system('del '+bscpathtmp)
377
378 class BlenderEnvironment(SConsEnvironment):
379
380     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
381         global libs
382         if not self or not libname or not source:
383             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
384             self.Exit()
385         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross'):
386             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
387             self.Exit()
388         
389         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
390         lenv = self.Copy()
391         res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
392       
393         SConsEnvironment.Default(self, res)
394         resources.append(res)
395
396     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None):
397         if not self or not libname or not sources:
398             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
399             self.Exit()
400         if libname in quickie or len(quickie)==0:
401             if libname in quickdebug: 
402                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname +bc.ENDC+bc.OKBLUE+ " (debug mode)" + bc.ENDC
403             else:
404                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname + bc.ENDC
405             lenv = self.Copy()
406             lenv.Append(CPPPATH=includes)
407             lenv.Append(CPPDEFINES=defines)
408             if lenv['WITH_BF_GAMEENGINE']:
409                     lenv.Append(CPPDEFINES=['GAMEBLENDER=1'])
410             if lenv['WITH_BF_BULLET']:
411                     lenv.Append(CPPDEFINES=['WITH_BULLET=1'])
412             # debug or not
413             # CXXFLAGS defaults to CCFLAGS, therefore
414             #  we Replace() rather than Append() to CXXFLAGS the first time
415             lenv.Replace(CXXFLAGS = lenv['CCFLAGS'])
416             if lenv['BF_DEBUG'] or (libname in quickdebug):
417                     lenv.Append(CCFLAGS = Split(lenv['BF_DEBUG_FLAGS']))
418                     lenv.Append( CXXFLAGS = Split(lenv['BF_DEBUG_FLAGS']))
419             else:
420                     lenv.Append(CCFLAGS = lenv['REL_CFLAGS'])
421                     lenv.Append(CXXFLAGS = lenv['REL_CCFLAGS'])
422             if lenv['BF_PROFILE']:
423                     lenv.Append(CCFLAGS = Split(lenv['BF_PROFILE_FLAGS']),
424                                 CXXFLAGS = Split(lenv['BF_PROFILE_FLAGS']))
425             if compileflags:
426                 lenv.Append(CCFLAGS = compileflags)
427                 lenv.Append(CXXFLAGS = compileflags)
428             lenv.Append(CCFLAGS = Split(lenv['C_WARN']))
429             lenv.Append(CXXFLAGS = Split(lenv['CC_WARN']))
430             lib = lenv.Library(target= '#'+root_build_dir+'lib/'+libname, source=sources)
431             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
432         else:
433             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
434         # note: libs is a global
435         add_lib_to_dict(self, libs, libtype, libname, priority)
436
437     def BlenderProg(self=None, builddir=None, progname=None, sources=None, includes=None, libs=None, libpath=None, binarykind=''):
438         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
439         lenv = self.Copy()
440         if lenv['OURPLATFORM'] in ['win32-vc', 'cygwin']:
441             lenv.Append(LINKFLAGS = Split(lenv['PLATFORM_LINKFLAGS']))
442             if lenv['BF_DEBUG']:
443                 lenv.Prepend(LINKFLAGS = ['/DEBUG','/PDB:'+progname+'.pdb'])
444         if  lenv['OURPLATFORM']=='linux2':
445             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
446             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
447         if  lenv['OURPLATFORM']=='sunos5':
448             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
449             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
450             if lenv['CXX'].endswith('CC'):
451                  lenv.Replace(LINK = '$CXX')
452         if  lenv['OURPLATFORM']=='darwin':
453             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
454             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
455             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
456         if lenv['BF_PROFILE']:
457                 lenv.Append(LINKFLAGS = lenv['BF_PROFILE_FLAGS'])
458         lenv.Append(CPPPATH=includes)
459         lenv.Append(LIBPATH=libpath)
460         lenv.Append(LIBS=libs)
461         if lenv['WITH_BF_QUICKTIME']:
462              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
463              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
464         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
465         if lenv['BF_DEBUG'] and lenv['OURPLATFORM']=='win32-vc' and lenv['BF_BSC']:
466             f = lenv.File(progname + '.bsc', builddir)
467             brs = lenv.Command(f, prog, [bsc])
468             SConsEnvironment.Default(self, brs)
469         SConsEnvironment.Default(self, prog)
470         program_list.append(prog)
471         if  lenv['OURPLATFORM']=='darwin':
472             lenv['BINARYKIND'] = binarykind
473             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
474         return prog
475
476     def Glob(lenv, pattern):
477         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
478         files = []
479         for i in glob.glob(path + pattern):
480             files.append(string.replace(i, path, ''))
481         return files