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