svn merge -r 16454:16593 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[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 # remove stdc++ from LLIBS if we are building a statc linked CXXFLAGS
245 if env['WITH_BF_STATICCXX']:
246     if 'stdc++' in env['LLIBS']:
247         env['LLIBS'] = env['LLIBS'].replace('stdc++', ' ')
248     else:
249         print '\tcould not remove stdc++ library from LLIBS, WITH_BF_STATICCXX may not work for your platform'
250
251 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
252 if 'blenderplayer' in B.targets:
253     env['WITH_BF_PLAYER'] = True
254
255 if 'blendernogame' in B.targets:
256     env['WITH_BF_GAMEENGINE'] = False
257
258 if 'blenderlite' in B.targets:
259     env['WITH_BF_GAMEENGINE'] = False
260     env['WITH_BF_OPENAL'] = False
261     env['WITH_BF_OPENEXR'] = False
262     env['WITH_BF_ICONV'] = False
263     env['WITH_BF_INTERNATIONAL'] = False
264     env['WITH_BF_OPENJPEG'] = False
265     env['WITH_BF_FFMPEG'] = False
266     env['WITH_BF_QUICKTIME'] = False
267     env['WITH_BF_YAFRAY'] = False
268     env['WITH_BF_REDCODE'] = False
269     env['WITH_BF_FTGL'] = False
270
271 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
272 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
273 B.root_build_dir = env['BF_BUILDDIR']
274 env['BUILDDIR'] = B.root_build_dir
275 if not B.root_build_dir[-1]==os.sep:
276     B.root_build_dir += os.sep
277     
278 # We do a shortcut for clean when no quicklist is given: just delete
279 # builddir without reading in SConscripts
280 do_clean = None
281 if 'clean' in B.targets:
282     do_clean = True
283
284 if not quickie and do_clean:
285     if os.path.exists(B.root_build_dir):
286         print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
287         dirs = os.listdir(B.root_build_dir)
288         for entry in dirs:
289             if os.path.isdir(B.root_build_dir + entry) == 1:
290                 print "clean dir %s"%(B.root_build_dir+entry)
291                 shutil.rmtree(B.root_build_dir+entry)
292             else: # remove file
293                 print "remove file %s"%(B.root_build_dir+entry)
294                 os.remove(B.root_build_dir+entry)
295         for confile in ['extern/ffmpeg/config.mak', 'extern/x264/config.mak',
296                 'extern/xvidcore/build/generic/platform.inc']:
297             if os.path.exists(confile):
298                 print "clean file %s"%confile
299                 os.remove(confile)
300         print B.bc.OKGREEN+'...done'+B.bc.ENDC
301     else:
302         print B.bc.HEADER+'Already Clean, nothing to do.'+B.bc.ENDC
303     Exit()
304
305 if not os.path.isdir ( B.root_build_dir):
306     os.makedirs ( B.root_build_dir )
307     os.makedirs ( B.root_build_dir + 'source' )
308     os.makedirs ( B.root_build_dir + 'intern' )
309     os.makedirs ( B.root_build_dir + 'extern' )
310     os.makedirs ( B.root_build_dir + 'lib' )
311     os.makedirs ( B.root_build_dir + 'bin' )
312
313 Help(opts.GenerateHelpText(env))
314
315 # default is new quieter output, but if you need to see the 
316 # commands, do 'scons BF_QUIET=0'
317 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
318 if env['BF_QUIET']:
319     B.set_quiet_output(env)
320 else:
321     if toolset=='msvc':
322         B.msvc_hack(env)
323
324 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
325 env.SConsignFile(B.root_build_dir+'scons-signatures')
326 B.init_lib_dict()
327
328 ##### END SETUP ##########
329
330 Export('env')
331
332 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
333 SConscript(B.root_build_dir+'/intern/SConscript')
334 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
335 SConscript(B.root_build_dir+'/extern/SConscript')
336 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
337 SConscript(B.root_build_dir+'/source/SConscript')
338
339 # now that we have read all SConscripts, we know what
340 # libraries will be built. Create list of
341 # libraries to give as objects to linking phase
342 mainlist = []
343 for tp in B.possible_types:
344     if not tp == 'player' and not tp == 'player2':
345         mainlist += B.create_blender_liblist(env, tp)
346
347 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
348     B.propose_priorities()
349
350 dobj = B.buildinfo(env, "dynamic") + B.resources
351 thestatlibs, thelibincs = B.setup_staticlibs(env)
352 thesyslibs = B.setup_syslibs(env)
353
354 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
355 if env['WITH_BF_PLAYER']:
356     playerlist = B.create_blender_liblist(env, 'player')
357     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
358
359 ##### Now define some targets
360
361
362 #------------ INSTALL
363
364 #-- binaries
365 blenderinstall = []
366 if  env['OURPLATFORM']=='darwin':
367     for prg in B.program_list:
368         bundle = '%s.app' % prg[0]
369         bundledir = os.path.dirname(bundle)
370         for dp, dn, df in os.walk(bundle):
371             if 'CVS' in dn:
372                 dn.remove('CVS')
373             if '.svn' in dn:
374                 dn.remove('.svn')
375             dir=env['BF_INSTALLDIR']+dp[len(bundledir):]
376             source=[dp+os.sep+f for f in df]
377             blenderinstall.append(env.Install(dir=dir,source=source))
378 else:
379     blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
380
381 #-- .blender
382 #- dont do .blender and scripts for darwin, it is already in the bundle
383 dotblendlist = []
384 dottargetlist = []
385 scriptinstall = []
386
387 if  env['OURPLATFORM']!='darwin':
388         for dp, dn, df in os.walk('bin/.blender'):
389             if 'CVS' in dn:
390                 dn.remove('CVS')
391             if '.svn' in dn:
392                 dn.remove('.svn')
393             for f in df:
394                 dotblendlist.append(dp+os.sep+f)
395                 dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
396
397         dotblenderinstall = []
398         for targetdir,srcfile in zip(dottargetlist, dotblendlist):
399             td, tf = os.path.split(targetdir)
400             dotblenderinstall.append(env.Install(dir=td, source=srcfile))
401         
402         #-- .blender/scripts    
403         scriptpath='release/scripts'
404         for dp, dn, df in os.walk(scriptpath):
405             if 'CVS' in dn:
406                 dn.remove('CVS')
407             if '.svn' in dn:
408                 dn.remove('.svn')
409             dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
410             source=[dp+os.sep+f for f in df]
411             scriptinstall.append(env.Install(dir=dir,source=source))
412
413 #-- plugins
414 pluglist = []
415 plugtargetlist = []
416 for tp, tn, tf in os.walk('release/plugins'):
417     if 'CVS' in tn:
418         tn.remove('CVS')
419     if '.svn' in tn:
420         tn.remove('.svn')
421     for f in tf:
422         print ">>>", env['BF_INSTALLDIR'], tp, f
423         pluglist.append(tp+os.sep+f)
424         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
425
426 # header files for plugins
427 pluglist.append('source/blender/blenpluginapi/documentation.h')
428 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'documentation.h')
429 pluglist.append('source/blender/blenpluginapi/externdef.h')
430 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'externdef.h')
431 pluglist.append('source/blender/blenpluginapi/floatpatch.h')
432 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'floatpatch.h')
433 pluglist.append('source/blender/blenpluginapi/iff.h')
434 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'iff.h')
435 pluglist.append('source/blender/blenpluginapi/plugin.h')
436 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'plugin.h')
437 pluglist.append('source/blender/blenpluginapi/util.h')
438 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep +'util.h')
439 pluglist.append('source/blender/blenpluginapi/plugin.DEF')
440 plugtargetlist.append(env['BF_INSTALLDIR'] + os.sep + 'plugins' + os.sep + 'include' + os.sep + 'plugin.def')
441
442 plugininstall = []
443 for targetdir,srcfile in zip(plugtargetlist, pluglist):
444     td, tf = os.path.split(targetdir)
445     plugininstall.append(env.Install(dir=td, source=srcfile))
446
447 textlist = []
448 texttargetlist = []
449 for tp, tn, tf in os.walk('release/text'):
450     if 'CVS' in tn:
451         tn.remove('CVS')
452     if '.svn' in tn:
453         tn.remove('.svn')
454     for f in tf:
455         textlist.append(tp+os.sep+f)
456
457 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
458
459 if  env['OURPLATFORM']=='darwin':
460         allinstall = [blenderinstall, plugininstall, textinstall]
461 else:
462         allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
463
464 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
465     dllsources = ['${LCGDIR}/gettext/lib/gnu_gettext.dll',
466                         '${LCGDIR}/png/lib/libpng.dll',
467                         '#release/windows/extra/python25.zip',
468                         '#release/windows/extra/zlib.pyd',
469                         '${LCGDIR}/sdl/lib/SDL.dll',
470                         '${LCGDIR}/zlib/lib/zlib.dll',
471                         '${LCGDIR}/tiff/lib/libtiff.dll']
472     if env['BF_DEBUG']:
473         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}_d.dll')
474     else:
475         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}.dll')
476     if env['OURPLATFORM'] == 'win32-mingw':
477         dllsources += ['${LCGDIR}/pthreads/lib/pthreadGC2.dll']
478     else:
479         dllsources += ['${LCGDIR}/pthreads/lib/pthreadVC2.dll']
480     if env['WITH_BF_ICONV']:
481         dllsources += ['${LCGDIR}/iconv/lib/iconv.dll']
482     if env['WITH_BF_FFMPEG']:
483         dllsources += ['${LCGDIR}/ffmpeg/lib/avcodec-51.dll',
484                         '${LCGDIR}/ffmpeg/lib/avformat-52.dll',
485                         '${LCGDIR}/ffmpeg/lib/avdevice-52.dll',
486                         '${LCGDIR}/ffmpeg/lib/avutil-49.dll',
487                         '${LCGDIR}/ffmpeg/lib/libfaad-0.dll',
488                         '${LCGDIR}/ffmpeg/lib/libfaac-0.dll',
489                         '${LCGDIR}/ffmpeg/lib/libmp3lame-0.dll',
490                         '${LCGDIR}/ffmpeg/lib/libx264-59.dll',
491                         '${LCGDIR}/ffmpeg/lib/xvidcore.dll',
492                         '${LCGDIR}/ffmpeg/lib/swscale-0.dll']
493     windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)
494     allinstall += windlls
495
496 installtarget = env.Alias('install', allinstall)
497 bininstalltarget = env.Alias('install-bin', blenderinstall)
498
499 nsisaction = env.Action(btools.NSIS_Installer, btools.NSIS_print)
500 nsiscmd = env.Command('nsisinstaller', None, nsisaction)
501 nsisalias = env.Alias('nsis', nsiscmd)
502
503 if env['WITH_BF_PLAYER']:
504     blenderplayer = env.Alias('blenderplayer', B.program_list)
505     Depends(blenderplayer,installtarget)
506
507 if not env['WITH_BF_GAMEENGINE']:
508     blendernogame = env.Alias('blendernogame', B.program_list)
509     Depends(blendernogame,installtarget)
510
511 if 'blenderlite' in B.targets:
512         blenderlite = env.Alias('blenderlite', B.program_list)
513         Depends(blenderlite,installtarget)
514
515 Depends(nsiscmd, allinstall)
516
517 Default(B.program_list)
518
519 if not env['WITHOUT_BF_INSTALL']:
520         Default(installtarget)
521
522 #------------ RELEASE
523 # TODO: zipup the installation
524
525 #------------ BLENDERPLAYER
526 # TODO: build stubs and link into blenderplayer
527
528 #------------ EPYDOC
529 # TODO: run epydoc
530