[ #4035 ] patch to make scons compile with ffmpeg in mingw
[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     a = '%s' % (target[0])
287     builddir, b = os.path.split(a)
288     bldroot = env.Dir('.').abspath
289     binary = env['BINARYKIND']
290     
291     sourcedir = bldroot + '/source/darwin/%s.app'%binary
292     sourceinfo = bldroot + "/source/darwin/%s.app/Contents/Info.plist"%binary
293     targetinfo = builddir +'/' + "%s.app/Contents/Info.plist"%binary
294     cmd = builddir + '/' +'%s.app'%binary
295     
296     if os.path.isdir(cmd):
297         shutil.rmtree(cmd)
298     shutil.copytree(sourcedir, cmd)
299     cmd = "cat %s | sed s/VERSION/`cat release/VERSION`/ | sed s/DATE/`date +'%%Y-%%b-%%d'`/ > %s"%(sourceinfo,targetinfo)
300     commands.getoutput(cmd)
301     cmd = 'cp %s/%s %s/%s.app/Contents/MacOS/%s'%(builddir, binary,builddir, binary, binary)
302     commands.getoutput(cmd)
303     cmd = 'mkdir %s/%s.app/Contents/MacOS/.blender/'%(builddir, binary)
304     print cmd
305     commands.getoutput(cmd)
306     cmd = builddir + '/%s.app/Contents/MacOS/.blender'%binary
307     shutil.copy(bldroot + '/bin/.blender/.bfont.ttf', cmd)
308     shutil.copy(bldroot + '/bin/.blender/.Blanguages', cmd)
309     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/Resources/'%(bldroot,builddir,binary)
310     commands.getoutput(cmd) 
311     cmd = 'cp -R %s/bin/.blender/locale %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
312     commands.getoutput(cmd) 
313     cmd = 'cp %s/bin/.blender/.Blanguages %s/%s.app/Contents/Resources/'%(bldroot,builddir,binary)
314     commands.getoutput(cmd) 
315     cmd = 'cp -R %s/release/scripts %s/%s.app/Contents/MacOS/.blender/'%(bldroot,builddir,binary)
316     commands.getoutput(cmd)
317     cmd = 'chmod +x  %s/%s.app/Contents/MacOS/%s'%(builddir,binary, binary)
318     commands.getoutput(cmd)
319     cmd = 'find %s/%s.app -name CVS -prune -exec rm -rf {} \;'%(builddir, binary)
320     commands.getoutput(cmd)
321     cmd = 'find %s/%s.app -name .DS_Store -exec rm -rf {} \;'%(builddir, binary)
322     commands.getoutput(cmd)
323
324 #### END ACTION STUFF #########
325
326 class BlenderEnvironment(SConsEnvironment):
327
328     def BlenderRes(self=None, libname=None, source=None, libtype=['core'], priority=[100]):
329         global libs
330         if not self or not libname or not source:
331             print bc.FAIL+'Cannot continue.  Missing argument for BlenderRes '+libname+bc.ENDC
332             Exit()
333         if self['OURPLATFORM'] not in ('win32-vc','win32-mingw','linuxcross'):
334             print bc.FAIL+'BlenderRes is for windows only!'+bc.END
335             Exit()
336         
337         print bc.HEADER+'Configuring resource '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC
338         lenv = self.Copy()
339         res = lenv.RES('#'+root_build_dir+'lib/'+libname, source)
340       
341         SConsEnvironment.Default(self, res)
342         resources.append(res)
343
344     def BlenderLib(self=None, libname=None, sources=None, includes=[], defines=[], libtype='common', priority = 100, compileflags=None):
345         if not self or not libname or not sources:
346             print bc.FAIL+'Cannot continue. Missing argument for BuildBlenderLib '+libname+bc.ENDC
347             Exit()
348         if libname in quickie or len(quickie)==0:
349             if libname in quickdebug: 
350                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname +bc.ENDC+bc.OKBLUE+ " (debug mode)" + bc.ENDC
351             else:
352                 print bc.HEADER+'Configuring library '+bc.ENDC+bc.OKGREEN+libname + bc.ENDC
353             lenv = self.Copy()
354             lenv.Append(CPPPATH=includes)
355             lenv.Append(CPPDEFINES=defines)
356             if lenv['WITH_BF_GAMEENGINE']:
357                     lenv.Append(CPPDEFINES=['GAMEBLENDER=1'])
358             if lenv['BF_DEBUG'] or (libname in quickdebug):
359                     lenv.Append(CCFLAGS = Split(lenv['BF_DEBUG_FLAGS']), CXXFLAGS = Split(lenv['BF_DEBUG_FLAGS']))
360             else:
361                     lenv.Append(CCFLAGS = lenv['REL_CFLAGS'], CXXFLAGS = lenv['REL_CCFLAGS'])
362             if lenv['BF_PROFILE']:
363                     lenv.Append(CCFLAGS = Split(lenv['BF_PROFILE_FLAGS']), CXXFLAGS = Split(lenv['BF_PROFILE_FLAGS']))
364             if compileflags:
365                 lenv.Append(CCFLAGS = compileflags)
366                 lenv.Append(CXXFLAGS = compileflags)
367             lenv.Append(CCFLAGS = Split(lenv['C_WARN']))
368             lenv.Append(CXXFLAGS = Split(lenv['CC_WARN']))
369             lib = lenv.Library(target= '#'+root_build_dir+'lib/'+libname, source=sources)
370             SConsEnvironment.Default(self, lib) # we add to default target, because this way we get some kind of progress info during build
371         else:
372             print bc.WARNING+'Not building '+bc.ENDC+bc.OKGREEN+libname+bc.ENDC+' for '+bc.OKBLUE+'BF_QUICK'+bc.ENDC
373         # note: libs is a global
374         add_lib_to_dict(libs, libtype, libname, priority)
375
376     def BlenderProg(self=None, builddir=None, progname=None, sources=None, includes=None, libs=None, libpath=None, binarykind=''):
377         print bc.HEADER+'Configuring program '+bc.ENDC+bc.OKGREEN+progname+bc.ENDC
378         lenv = self.Copy()
379         if lenv['OURPLATFORM'] in ['win32-vc', 'cygwin']:
380             lenv.Append(LINKFLAGS = Split(lenv['PLATFORM_LINKFLAGS']))
381         if  lenv['OURPLATFORM']=='linux2':
382             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
383             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
384         if  lenv['OURPLATFORM']=='sunos5':
385             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
386             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
387         if  lenv['OURPLATFORM']=='darwin':
388             lenv.Append(LINKFLAGS = lenv['PLATFORM_LINKFLAGS'])
389             lenv.Append(LINKFLAGS = lenv['BF_PYTHON_LINKFLAGS'])
390             lenv.Append(LINKFLAGS = lenv['BF_OPENGL_LINKFLAGS'])
391         if lenv['BF_PROFILE']:
392                 lenv.Append(LINKFLAGS = lenv['BF_PROFILE_FLAGS'])
393         lenv.Append(CPPPATH=includes)
394         lenv.Append(LIBPATH=libpath)
395         lenv.Append(LIBS=libs)
396         if lenv['WITH_BF_QUICKTIME']:
397              lenv.Append(LIBS = lenv['BF_QUICKTIME_LIB'])
398              lenv.Append(LIBPATH = lenv['BF_QUICKTIME_LIBPATH'])
399         prog = lenv.Program(target=builddir+'bin/'+progname, source=sources)
400         SConsEnvironment.Default(self, prog)
401         program_list.append(prog)
402         if  lenv['OURPLATFORM']=='darwin':
403             lenv['BINARYKIND'] = binarykind
404             lenv.AddPostAction(prog,Action(AppIt,strfunction=my_appit_print))
405
406     def Glob(lenv, pattern):
407         path = string.replace(GetBuildPath(lenv,'SConscript'),'SConscript', '')
408         files = []
409         for i in glob.glob(path + pattern):
410             files.append(string.replace(i, path, ''))
411         return files