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