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