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