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