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