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