* make BulletSoftBody own small lib to make sure bullet libs don't grow too large...
[blender-staging.git] / SConstruct
1 #!/usr/bin/env python
2 # $Id$
3 # ***** BEGIN GPL LICENSE BLOCK *****
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 #
19 # The Original Code is Copyright (C) 2006, Blender Foundation
20 # All rights reserved.
21 #
22 # The Original Code is: all of this file.
23 #
24 # Contributor(s): Nathan Letwory.
25 #
26 # ***** END GPL LICENSE BLOCK *****
27 #
28 # Main entry-point for the SCons building system
29 # Set up some custom actions and target/argument handling
30 # Then read all SConscripts and build
31
32 import sys
33 import os
34 import os.path
35 import string
36 import shutil
37 import glob
38 import re
39 from tempfile import mkdtemp
40
41 import tools.Blender
42 import tools.btools
43 import tools.bcolors
44
45 BlenderEnvironment = tools.Blender.BlenderEnvironment
46 btools = tools.btools
47 B = tools.Blender
48
49 ### globals ###
50 platform = sys.platform
51 quickie = None
52 quickdebug = None
53 nsis_build = None
54
55 ##### BEGIN SETUP #####
56
57 B.possible_types = ['core', 'common', 'blender', 'intern',
58                     'international', 'game', 'game2',
59                     'player', 'player2', 'system']
60
61 B.binarykind = ['blender' , 'blenderplayer']
62 ##################################
63 # target and argument validation #
64 ##################################
65 # XX cheating for BF_FANCY, we check for BF_FANCY before args are validated
66 use_color = ARGUMENTS.get('BF_FANCY', '1')
67 if platform=='win32':
68     use_color = None
69
70 if not use_color=='1':
71     B.bc.disable()
72     
73  #on defaut white Os X terminal, some colors are totally unlegible
74 if platform=='darwin':
75     B.bc.OKGREEN = '\033[34m'
76     B.bc.WARNING = '\033[36m'
77
78 # arguments
79 print B.bc.HEADER+'Command-line arguments'+B.bc.ENDC
80 B.arguments = btools.validate_arguments(ARGUMENTS, B.bc)
81 btools.print_arguments(B.arguments, B.bc)
82
83 # targets
84 print B.bc.HEADER+'Command-line targets'+B.bc.ENDC
85 B.targets = btools.validate_targets(COMMAND_LINE_TARGETS, B.bc)
86 btools.print_targets(B.targets, B.bc)
87
88 ##########################
89 # setting up environment #
90 ##########################
91
92 # handling cmd line arguments & config file
93
94 # first check cmdline for toolset and we create env to work on
95 quickie = B.arguments.get('BF_QUICK', None)
96 quickdebug = B.arguments.get('BF_QUICKDEBUG', None)
97
98 if quickdebug:
99     B.quickdebug=string.split(quickdebug, ',')
100 else:
101     B.quickdebug=[]
102
103 if quickie:
104     B.quickie=string.split(quickie,',')
105 else:
106     B.quickie=[]
107     
108 toolset = B.arguments.get('BF_TOOLSET', None)
109 if toolset:
110     print "Using " + toolset
111     if toolset=='mstoolkit':
112         env = BlenderEnvironment(ENV = os.environ)
113         env.Tool('mstoolkit', ['tools'])
114     else:
115         env = BlenderEnvironment(tools=[toolset], ENV = os.environ)
116         if env:
117             btools.SetupSpawn(env)
118 else:
119     env = BlenderEnvironment(ENV = os.environ)
120
121 if not env:
122     print "Could not create a build environment"
123     Exit()
124
125
126 cc = B.arguments.get('CC', None)
127 cxx = B.arguments.get('CXX', None)
128 if cc:
129     env['CC'] = cc
130 if cxx:
131     env['CXX'] = cxx
132
133 if env['CC'] in ['cl', 'cl.exe'] and sys.platform=='win32':
134     platform = 'win32-vc'
135 elif env['CC'] in ['gcc'] and sys.platform=='win32':
136     platform = 'win32-mingw'
137
138 env.SConscriptChdir(0)
139
140 crossbuild = B.arguments.get('BF_CROSS', None)
141 if crossbuild and platform!='win32':
142     platform = 'linuxcross'
143
144 env['OURPLATFORM'] = platform
145
146 configfile = 'config'+os.sep+platform+'-config.py'
147
148 if os.path.exists(configfile):
149     print B.bc.OKGREEN + "Using config file: " + B.bc.ENDC + configfile
150 else:
151     print B.bc.FAIL + configfile + " doesn't exist" + B.bc.ENDC
152
153 if crossbuild and env['PLATFORM'] != 'win32':
154     print B.bc.HEADER+"Preparing for crossbuild"+B.bc.ENDC
155     env.Tool('crossmingw', ['tools'])
156     # todo: determine proper libs/includes etc.
157     # Needed for gui programs, console programs should do without it
158     env.Append(LINKFLAGS=['-mwindows'])
159
160 userconfig = B.arguments.get('BF_CONFIG', 'user-config.py')
161 # first read platform config. B.arguments will override
162 optfiles = [configfile]
163 if os.path.exists(userconfig):
164     print B.bc.OKGREEN + "Using user-config file: " + B.bc.ENDC + userconfig
165     optfiles += [userconfig]
166 else:
167     print B.bc.WARNING + userconfig + " not found, no user overrides" + B.bc.ENDC
168
169 opts = btools.read_opts(optfiles, B.arguments)
170 opts.Update(env)
171
172 if not env['BF_FANCY']:
173     B.bc.disable()
174
175 # disable elbeem (fluidsim) compilation?
176 if env['BF_NO_ELBEEM'] == 1:
177     env['CPPFLAGS'].append('-DDISABLE_ELBEEM')
178     env['CXXFLAGS'].append('-DDISABLE_ELBEEM')
179     env['CCFLAGS'].append('-DDISABLE_ELBEEM')
180
181 if env['WITH_BF_OPENMP'] == 1:
182         if env['OURPLATFORM']=='win32-vc':
183                 env['CCFLAGS'].append('/openmp')
184                 env['CPPFLAGS'].append('/openmp')
185                 env['CXXFLAGS'].append('/openmp')
186         else:
187             if env['CC'][-3:] == 'icc': # to be able to handle CC=/opt/bla/icc case
188                 env.Append(LINKFLAGS=['-openmp', '-static-intel'])
189                 env['CCFLAGS'].append('-openmp')
190                 env['CPPFLAGS'].append('-openmp')
191                 env['CXXFLAGS'].append('-openmp')
192             else:
193                 env.Append(CCFLAGS=['-fopenmp']) 
194                 env.Append(CPPFLAGS=['-fopenmp'])
195                 env.Append(CXXFLAGS=['-fopenmp'])
196                 # env.Append(LINKFLAGS=['-fprofile-generate'])
197
198 #check for additional debug libnames
199
200 if env.has_key('BF_DEBUG_LIBS'):
201     B.quickdebug += env['BF_DEBUG_LIBS']
202
203 printdebug = B.arguments.get('BF_LISTDEBUG', 0)
204
205 # see if this linux distro has libalut
206
207 if env['OURPLATFORM'] == 'linux2' :
208     if env['WITH_BF_OPENAL']:
209         mylib_test_source_file = """
210         #include "AL/alut.h"
211         int main(int argc, char **argv)
212         {
213             alutGetMajorVersion();
214             return 0;
215         }
216         """
217
218         def CheckFreeAlut(context,env):
219             context.Message( B.bc.OKGREEN + "Linux platform detected:\n  checking for FreeAlut... " + B.bc.ENDC )
220             env['LIBS'] = 'alut'
221             result = context.TryLink(mylib_test_source_file, '.c')
222             context.Result(result)
223             return result
224
225         env2 = env.Copy( LIBPATH = env['BF_OPENAL'] ) 
226         sconf_temp = mkdtemp()
227         conf = Configure( env2, {'CheckFreeAlut' : CheckFreeAlut}, sconf_temp, '/dev/null' )
228         if conf.CheckFreeAlut( env2 ):
229             env['BF_OPENAL_LIB'] += ' alut'
230         del env2
231         root = ''
232         for root, dirs, files in os.walk(sconf_temp, topdown=False):
233             for name in files:
234                 os.remove(os.path.join(root, name))
235             for name in dirs:
236                 os.rmdir(os.path.join(root, name))
237         if root: os.rmdir(root)
238
239 if len(B.quickdebug) > 0 and printdebug != 0:
240     print B.bc.OKGREEN + "Buildings these libs with debug symbols:" + B.bc.ENDC
241     for l in B.quickdebug:
242         print "\t" + l
243
244 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
245 if 'blenderplayer' in B.targets:
246     env['WITH_BF_PLAYER'] = True
247
248 if 'blendernogame' in B.targets:
249     env['WITH_BF_GAMEENGINE'] = False
250
251 if 'blenderlite' in B.targets:
252     env['WITH_BF_GAMEENGINE'] = False
253     env['WITH_BF_OPENAL'] = False
254     env['WITH_BF_OPENEXR'] = False
255     env['WITH_BF_ICONV'] = False
256     env['WITH_BF_INTERNATIONAL'] = False
257     env['WITH_BF_OPENJPEG'] = False
258     env['WITH_BF_FFMPEG'] = False
259     env['WITH_BF_QUICKTIME'] = False
260     env['WITH_BF_YAFRAY'] = False
261     env['WITH_BF_REDCODE'] = False
262     env['WITH_BF_FTGL'] = False
263
264 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
265 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
266 B.root_build_dir = env['BF_BUILDDIR']
267 env['BUILDDIR'] = B.root_build_dir
268 if not B.root_build_dir[-1]==os.sep:
269     B.root_build_dir += os.sep
270     
271 # We do a shortcut for clean when no quicklist is given: just delete
272 # builddir without reading in SConscripts
273 do_clean = None
274 if 'clean' in B.targets:
275     do_clean = True
276
277 if not quickie and do_clean:
278     if os.path.exists(B.root_build_dir):
279         print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
280         dirs = os.listdir(B.root_build_dir)
281         for entry in dirs:
282             if os.path.isdir(B.root_build_dir + entry) == 1:
283                 print "clean dir %s"%(B.root_build_dir+entry)
284                 shutil.rmtree(B.root_build_dir+entry)
285             else: # remove file
286                 print "remove file %s"%(B.root_build_dir+entry)
287                 os.remove(B.root_build_dir+entry)
288         for confile in ['extern/ffmpeg/config.mak', 'extern/x264/config.mak',
289                 'extern/xvidcore/build/generic/platform.inc']:
290             if os.path.exists(confile):
291                 print "clean file %s"%confile
292                 os.remove(confile)
293         print B.bc.OKGREEN+'...done'+B.bc.ENDC
294     else:
295         print B.bc.HEADER+'Already Clean, nothing to do.'+B.bc.ENDC
296     Exit()
297
298 if not os.path.isdir ( B.root_build_dir):
299     os.makedirs ( B.root_build_dir )
300     os.makedirs ( B.root_build_dir + 'source' )
301     os.makedirs ( B.root_build_dir + 'intern' )
302     os.makedirs ( B.root_build_dir + 'extern' )
303     os.makedirs ( B.root_build_dir + 'lib' )
304     os.makedirs ( B.root_build_dir + 'bin' )
305
306 Help(opts.GenerateHelpText(env))
307
308 # default is new quieter output, but if you need to see the 
309 # commands, do 'scons BF_QUIET=0'
310 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
311 if env['BF_QUIET']:
312     B.set_quiet_output(env)
313 else:
314     if toolset=='msvc':
315         B.msvc_hack(env)
316
317 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
318 env.SConsignFile(B.root_build_dir+'scons-signatures')
319 B.init_lib_dict()
320
321 ##### END SETUP ##########
322
323 Export('env')
324
325 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
326 SConscript(B.root_build_dir+'/intern/SConscript')
327 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
328 SConscript(B.root_build_dir+'/extern/SConscript')
329 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
330 SConscript(B.root_build_dir+'/source/SConscript')
331
332 # now that we have read all SConscripts, we know what
333 # libraries will be built. Create list of
334 # libraries to give as objects to linking phase
335 mainlist = []
336 for tp in B.possible_types:
337     if not tp == 'player' and not tp == 'player2':
338         mainlist += B.create_blender_liblist(env, tp)
339
340 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
341     B.propose_priorities()
342
343 dobj = B.buildinfo(env, "dynamic") + B.resources
344 thestatlibs, thelibincs = B.setup_staticlibs(env)
345 thesyslibs = B.setup_syslibs(env)
346
347 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
348 if env['WITH_BF_PLAYER']:
349     playerlist = B.create_blender_liblist(env, 'player')
350     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
351
352 ##### Now define some targets
353
354
355 #------------ INSTALL
356
357 #-- binaries
358 blenderinstall = []
359 if  env['OURPLATFORM']=='darwin':
360     for prg in B.program_list:
361         bundle = '%s.app' % prg[0]
362         bundledir = os.path.dirname(bundle)
363         for dp, dn, df in os.walk(bundle):
364             if 'CVS' in dn:
365                 dn.remove('CVS')
366             if '.svn' in dn:
367                 dn.remove('.svn')
368             dir=env['BF_INSTALLDIR']+dp[len(bundledir):]
369             source=[dp+os.sep+f for f in df]
370             blenderinstall.append(env.Install(dir=dir,source=source))
371 else:
372     blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
373
374 #-- .blender
375 #- dont do .blender and scripts for darwin, it is already in the bundle
376 dotblendlist = []
377 dottargetlist = []
378 scriptinstall = []
379
380 if  env['OURPLATFORM']!='darwin':
381         for dp, dn, df in os.walk('bin/.blender'):
382             if 'CVS' in dn:
383                 dn.remove('CVS')
384             if '.svn' in dn:
385                 dn.remove('.svn')
386             for f in df:
387                 dotblendlist.append(dp+os.sep+f)
388                 dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
389
390         dotblenderinstall = []
391         for targetdir,srcfile in zip(dottargetlist, dotblendlist):
392             td, tf = os.path.split(targetdir)
393             dotblenderinstall.append(env.Install(dir=td, source=srcfile))
394         
395         #-- .blender/scripts    
396         scriptpath='release/scripts'
397         for dp, dn, df in os.walk(scriptpath):
398             if 'CVS' in dn:
399                 dn.remove('CVS')
400             if '.svn' in dn:
401                 dn.remove('.svn')
402             dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
403             source=[dp+os.sep+f for f in df]
404             scriptinstall.append(env.Install(dir=dir,source=source))
405
406 #-- plugins
407 pluglist = []
408 plugtargetlist = []
409 for tp, tn, tf in os.walk('release/plugins'):
410     if 'CVS' in tn:
411         tn.remove('CVS')
412     if '.svn' in tn:
413         tn.remove('.svn')
414     for f in tf:
415         print ">>>", env['BF_INSTALLDIR'], tp, f
416         pluglist.append(tp+os.sep+f)
417         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
418
419 # header files for plugins
420 pluglist.append('source/blender/blenpluginapi/documentation.h')
421 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'documentation.h')
422 pluglist.append('source/blender/blenpluginapi/externdef.h')
423 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'externdef.h')
424 pluglist.append('source/blender/blenpluginapi/floatpatch.h')
425 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'floatpatch.h')
426 pluglist.append('source/blender/blenpluginapi/iff.h')
427 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'iff.h')
428 pluglist.append('source/blender/blenpluginapi/plugin.h')
429 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'plugin.h')
430 pluglist.append('source/blender/blenpluginapi/util.h')
431 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'util.h')
432 pluglist.append('source/blender/blenpluginapi/plugin.DEF')
433 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep + 'plugin.def')
434
435 plugininstall = []
436 for targetdir,srcfile in zip(plugtargetlist, pluglist):
437     td, tf = os.path.split(targetdir)
438     plugininstall.append(env.Install(dir=td, source=srcfile))
439
440 textlist = []
441 texttargetlist = []
442 for tp, tn, tf in os.walk('release/text'):
443     if 'CVS' in tn:
444         tn.remove('CVS')
445     if '.svn' in tn:
446         tn.remove('.svn')
447     for f in tf:
448         textlist.append(tp+os.sep+f)
449
450 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
451
452 if  env['OURPLATFORM']=='darwin':
453         allinstall = [blenderinstall, plugininstall, textinstall]
454 else:
455         allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
456
457 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
458     dllsources = ['${LCGDIR}/gettext/lib/gnu_gettext.dll',
459                         '${LCGDIR}/png/lib/libpng.dll',
460                         '#release/windows/extra/python25.zip',
461                         '#release/windows/extra/zlib.pyd',
462                         '${LCGDIR}/sdl/lib/SDL.dll',
463                         '${LCGDIR}/zlib/lib/zlib.dll',
464                         '${LCGDIR}/tiff/lib/libtiff.dll']
465     if env['BF_DEBUG']:
466         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}_d.dll')
467     else:
468         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}.dll')
469     if env['OURPLATFORM'] == 'win32-mingw':
470         dllsources += ['${LCGDIR}/pthreads/lib/pthreadGC2.dll']
471     else:
472         dllsources += ['${LCGDIR}/pthreads/lib/pthreadVC2.dll']
473     if env['WITH_BF_ICONV']:
474         dllsources += ['${LCGDIR}/iconv/lib/iconv.dll']
475     if env['WITH_BF_FFMPEG']:
476         dllsources += ['${LCGDIR}/ffmpeg/lib/avcodec-51.dll',
477                         '${LCGDIR}/ffmpeg/lib/avformat-52.dll',
478                         '${LCGDIR}/ffmpeg/lib/avdevice-52.dll',
479                         '${LCGDIR}/ffmpeg/lib/avutil-49.dll',
480                         '${LCGDIR}/ffmpeg/lib/libfaad-0.dll',
481                         '${LCGDIR}/ffmpeg/lib/libfaac-0.dll',
482                         '${LCGDIR}/ffmpeg/lib/libmp3lame-0.dll',
483                         '${LCGDIR}/ffmpeg/lib/libx264-59.dll',
484                         '${LCGDIR}/ffmpeg/lib/xvidcore.dll',
485                         '${LCGDIR}/ffmpeg/lib/swscale-0.dll']
486     windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)
487     allinstall += windlls
488
489 installtarget = env.Alias('install', allinstall)
490 bininstalltarget = env.Alias('install-bin', blenderinstall)
491
492 nsisaction = env.Action(btools.NSIS_Installer, btools.NSIS_print)
493 nsiscmd = env.Command('nsisinstaller', None, nsisaction)
494 nsisalias = env.Alias('nsis', nsiscmd)
495
496 if env['WITH_BF_PLAYER']:
497     blenderplayer = env.Alias('blenderplayer', B.program_list)
498     Depends(blenderplayer,installtarget)
499
500 if not env['WITH_BF_GAMEENGINE']:
501     blendernogame = env.Alias('blendernogame', B.program_list)
502     Depends(blendernogame,installtarget)
503
504 if 'blenderlite' in B.targets:
505         blenderlite = env.Alias('blenderlite', B.program_list)
506         Depends(blenderlite,installtarget)
507
508 Depends(nsiscmd, allinstall)
509
510 Default(B.program_list)
511
512 if not env['WITHOUT_BF_INSTALL']:
513         Default(installtarget)
514
515 #------------ RELEASE
516 # TODO: zipup the installation
517
518 #------------ BLENDERPLAYER
519 # TODO: build stubs and link into blenderplayer
520
521 #------------ EPYDOC
522 # TODO: run epydoc
523