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