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