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