* copy FFMPEG libraries on Windows to BF_INSTALLDIR when done.
[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 = B.arguments.get('BF_CONFIG', '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 # first read platform config. B.arguments will override
161 optfiles = [configfile]
162 if os.path.exists('user-config.py'):
163     print B.bc.OKGREEN + "Using config file: " + B.bc.ENDC + 'user-config.py'
164     optfiles += ['user-config.py']
165 else:
166     print B.bc.WARNING + 'user-config.py' + " not found, no user overrides" + B.bc.ENDC
167
168 opts = btools.read_opts(optfiles, B.arguments)
169 opts.Update(env)
170
171 if not env['BF_FANCY']:
172     B.bc.disable()
173
174 # disable elbeem (fluidsim) compilation?
175 if env['BF_NO_ELBEEM'] == 1:
176     env['CPPFLAGS'].append('-DDISABLE_ELBEEM')
177     env['CXXFLAGS'].append('-DDISABLE_ELBEEM')
178     env['CCFLAGS'].append('-DDISABLE_ELBEEM')
179
180 if env['WITH_BF_OPENMP'] == 1:
181         if env['OURPLATFORM']=='win32-vc':
182                 env['CCFLAGS'].append('/openmp')
183                 env['CPPFLAGS'].append('/openmp')
184                 env['CXXFLAGS'].append('/openmp')
185         else:
186                 if env['CC'] == 'icc':
187                         env.Append(LINKFLAGS=['-openmp', '-static-intel'])
188                         env['CCFLAGS'].append('-openmp')
189                         env['CPPFLAGS'].append('-openmp')
190                         env['CXXFLAGS'].append('-openmp')
191                 else:
192                         env['CCFLAGS'].append('-fopenmp')
193                         env['CPPFLAGS'].append('-fopenmp')
194                         env['CXXFLAGS'].append('-fopenmp')
195
196 #check for additional debug libnames
197
198 if env.has_key('BF_DEBUG_LIBS'):
199     B.quickdebug += env['BF_DEBUG_LIBS']
200
201 printdebug = B.arguments.get('BF_LISTDEBUG', 0)
202
203 # see if this linux distro has libalut
204
205 if env['OURPLATFORM'] == 'linux2' :
206     if env['WITH_BF_OPENAL']:
207         mylib_test_source_file = """
208         #include "AL/alut.h"
209         int main(int argc, char **argv)
210         {
211             alutGetMajorVersion();
212             return 0;
213         }
214         """
215
216         def CheckFreeAlut(context,env):
217             context.Message( B.bc.OKGREEN + "Linux platform detected:\n  checking for FreeAlut... " + B.bc.ENDC )
218             env['LIBS'] = 'alut'
219             result = context.TryLink(mylib_test_source_file, '.c')
220             context.Result(result)
221             return result
222
223         env2 = env.Copy( LIBPATH = env['BF_OPENAL'] ) 
224         sconf_temp = mkdtemp()
225         conf = Configure( env2, {'CheckFreeAlut' : CheckFreeAlut}, sconf_temp, '/dev/null' )
226         if conf.CheckFreeAlut( env2 ):
227             env['BF_OPENAL_LIB'] += ' alut'
228         del env2
229         root = ''
230         for root, dirs, files in os.walk(sconf_temp, topdown=False):
231             for name in files:
232                 os.remove(os.path.join(root, name))
233             for name in dirs:
234                 os.rmdir(os.path.join(root, name))
235         if root: os.rmdir(root)
236
237 if len(B.quickdebug) > 0 and printdebug != 0:
238     print B.bc.OKGREEN + "Buildings these libs with debug symbols:" + B.bc.ENDC
239     for l in B.quickdebug:
240         print "\t" + l
241
242 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
243 if 'blenderplayer' in B.targets:
244     env['WITH_BF_PLAYER'] = True
245
246 if 'blendernogame' in B.targets:
247     env['WITH_BF_GAMEENGINE'] = False
248
249 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
250 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
251 B.root_build_dir = env['BF_BUILDDIR']
252 env['BUILDDIR'] = B.root_build_dir
253 if not B.root_build_dir[-1]==os.sep:
254     B.root_build_dir += os.sep
255     
256 # We do a shortcut for clean when no quicklist is given: just delete
257 # builddir without reading in SConscripts
258 do_clean = None
259 if 'clean' in B.targets:
260     do_clean = True
261
262 if not quickie and do_clean:
263     if os.path.exists(B.root_build_dir):
264         print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
265         dirs = os.listdir(B.root_build_dir)
266         for dir in dirs:
267             if os.path.isdir(B.root_build_dir + dir) == 1:
268                 print "clean dir %s"%(B.root_build_dir+dir)
269                 shutil.rmtree(B.root_build_dir+dir)
270         print B.bc.OKGREEN+'...done'+B.bc.ENDC
271     else:
272         print B.bc.HEADER+'Already Clean, nothing to do.'+B.bc.ENDC
273     Exit()
274
275 if not os.path.isdir ( B.root_build_dir):
276     os.makedirs ( B.root_build_dir )
277     os.makedirs ( B.root_build_dir + 'source' )
278     os.makedirs ( B.root_build_dir + 'intern' )
279     os.makedirs ( B.root_build_dir + 'extern' )
280     os.makedirs ( B.root_build_dir + 'lib' )
281     os.makedirs ( B.root_build_dir + 'bin' )
282
283 Help(opts.GenerateHelpText(env))
284
285 # default is new quieter output, but if you need to see the 
286 # commands, do 'scons BF_QUIET=0'
287 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
288 if env['BF_QUIET']:
289     B.set_quiet_output(env)
290 else:
291     if toolset=='msvc':
292         B.msvc_hack(env)
293
294 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
295 env.SConsignFile(B.root_build_dir+'scons-signatures')
296 B.init_lib_dict()
297
298 ##### END SETUP ##########
299
300 Export('env')
301
302 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
303 SConscript(B.root_build_dir+'/intern/SConscript')
304 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
305 SConscript(B.root_build_dir+'/extern/SConscript')
306 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
307 SConscript(B.root_build_dir+'/source/SConscript')
308
309 # now that we have read all SConscripts, we know what
310 # libraries will be built. Create list of
311 # libraries to give as objects to linking phase
312 mainlist = []
313 for tp in B.possible_types:
314     if not tp == 'player' and not tp == 'player2':
315         mainlist += B.create_blender_liblist(env, tp)
316
317 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
318     B.propose_priorities()
319
320 dobj = B.buildinfo(env, "dynamic") + B.resources
321 thestatlibs, thelibincs = B.setup_staticlibs(env)
322 thesyslibs = B.setup_syslibs(env)
323
324 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
325 if env['WITH_BF_PLAYER']:
326     playerlist = B.create_blender_liblist(env, 'player')
327     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
328
329 ##### Now define some targets
330
331
332 #------------ INSTALL
333
334 #-- binaries
335 blenderinstall = []
336 if  env['OURPLATFORM']=='darwin':
337     for prg in B.program_list:
338         bundle = '%s.app' % prg[0]
339         bundledir = os.path.dirname(bundle)
340         for dp, dn, df in os.walk(bundle):
341             if 'CVS' in dn:
342                 dn.remove('CVS')
343             if '.svn' in dn:
344                 dn.remove('.svn')
345             dir=env['BF_INSTALLDIR']+dp[len(bundledir):]
346             source=[dp+os.sep+f for f in df]
347             blenderinstall.append(env.Install(dir=dir,source=source))
348 else:
349     blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
350
351 #-- .blender
352 #- dont do .blender and scripts for darwin, it is already in the bundle
353 dotblendlist = []
354 dottargetlist = []
355 scriptinstall = []
356
357 if  env['OURPLATFORM']!='darwin':
358         for dp, dn, df in os.walk('bin/.blender'):
359             if 'CVS' in dn:
360                 dn.remove('CVS')
361             if '.svn' in dn:
362                 dn.remove('.svn')
363             for f in df:
364                 dotblendlist.append(dp+os.sep+f)
365                 dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
366
367         dotblenderinstall = []
368         for targetdir,srcfile in zip(dottargetlist, dotblendlist):
369             td, tf = os.path.split(targetdir)
370             dotblenderinstall.append(env.Install(dir=td, source=srcfile))
371         
372         #-- .blender/scripts    
373         scriptpath='release/scripts'
374         for dp, dn, df in os.walk(scriptpath):
375             if 'CVS' in dn:
376                 dn.remove('CVS')
377             if '.svn' in dn:
378                 dn.remove('.svn')
379             dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
380             source=[dp+os.sep+f for f in df]
381             scriptinstall.append(env.Install(dir=dir,source=source))
382
383 #-- plugins
384 pluglist = []
385 plugtargetlist = []
386 for tp, tn, tf in os.walk('release/plugins'):
387     if 'CVS' in tn:
388         tn.remove('CVS')
389     if '.svn' in tn:
390         tn.remove('.svn')
391     for f in tf:
392         pluglist.append(tp+os.sep+f)
393         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
394
395 plugininstall = []
396 for targetdir,srcfile in zip(plugtargetlist, pluglist):
397     td, tf = os.path.split(targetdir)
398     plugininstall.append(env.Install(dir=td, source=srcfile))
399
400 textlist = []
401 texttargetlist = []
402 for tp, tn, tf in os.walk('release/text'):
403     if 'CVS' in tn:
404         tn.remove('CVS')
405     if '.svn' in tn:
406         tn.remove('.svn')
407     for f in tf:
408         textlist.append(tp+os.sep+f)
409
410 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
411
412 if  env['OURPLATFORM']=='darwin':
413         allinstall = [blenderinstall, plugininstall, textinstall]
414 else:
415         allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
416
417 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
418     dllsources = ['${LCGDIR}/gettext/lib/gnu_gettext.dll',
419                         '${LCGDIR}/png/lib/libpng.dll',
420                         '#release/windows/extra/python25.zip',
421                         '#release/windows/extra/zlib.pyd',
422                         '${LCGDIR}/sdl/lib/SDL.dll',
423                         '${LCGDIR}/zlib/lib/zlib.dll',
424                         '${LCGDIR}/tiff/lib/libtiff.dll']
425     if env['BF_DEBUG']:
426         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}_d.dll')
427     else:
428         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}.dll')
429     if env['OURPLATFORM'] == 'win32-mingw':
430         dllsources += ['${LCGDIR}/pthreads/lib/pthreadGC2.dll']
431     else:
432         dllsources += ['${LCGDIR}/pthreads/lib/pthreadVC2.dll']
433     if env['WITH_BF_ICONV']:
434         dllsources += ['${LCGDIR}/iconv/lib/iconv.dll']
435     if env['WITH_BF_FFMPEG']:
436         dllsources += ['${LCGDIR}/ffmpeg/lib/avcodec-51.dll',
437                         '${LCGDIR}/ffmpeg/lib/avformat-52.dll',
438                         '${LCGDIR}/ffmpeg/lib/libfaad-0.dll',
439                         '${LCGDIR}/ffmpeg/lib/libmp3lame-0.dll',
440                         '${LCGDIR}/ffmpeg/lib/xvidcore.dll',
441                         '${LCGDIR}/ffmpeg/lib/swscale-0.dll']
442     windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)
443     allinstall += windlls
444
445 installtarget = env.Alias('install', allinstall)
446 bininstalltarget = env.Alias('install-bin', blenderinstall)
447
448 nsisaction = env.Action(btools.NSIS_Installer, btools.NSIS_print)
449 nsiscmd = env.Command('nsisinstaller', None, nsisaction)
450 nsisalias = env.Alias('nsis', nsiscmd)
451
452 if env['WITH_BF_PLAYER']:
453     blenderplayer = env.Alias('blenderplayer', B.program_list)
454     Depends(blenderplayer,installtarget)
455
456 if not env['WITH_BF_GAMEENGINE']:
457     blendernogame = env.Alias('blendernogame', B.program_list)
458     Depends(blendernogame,installtarget)
459
460 Depends(nsiscmd, allinstall)
461
462 Default(B.program_list)
463
464 if not env['WITHOUT_BF_INSTALL']:
465         Default(installtarget)
466
467 #------------ RELEASE
468 # TODO: zipup the installation
469
470 #------------ BLENDERPLAYER
471 # TODO: build stubs and link into blenderplayer
472
473 #------------ EPYDOC
474 # TODO: run epydoc
475