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