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