Bugs #4488 and #4431
[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
43 import tools.Blender
44 import tools.btools
45 import tools.bcolors
46
47 BlenderEnvironment = tools.Blender.BlenderEnvironment
48 btools = tools.btools
49 B = tools.Blender
50
51 ### globals ###
52 platform = sys.platform
53 quickie = None
54 quickdebug = None
55
56 ##### BEGIN SETUP #####
57
58 B.possible_types = ['core', 'common', 'blender', 'intern',
59                     'international', 'game', 'game2',
60                     'player', 'player2', 'system']
61
62 B.binarykind = ['blender' , 'blenderplayer']
63 ##################################
64 # target and argument validation #
65 ##################################
66 # XX cheating for BF_FANCY, we check for BF_FANCY before args are validated
67 use_color = ARGUMENTS.get('BF_FANCY', '1')
68 if platform=='win32':
69     use_color = None
70
71 if not use_color=='1':
72     B.bc.disable()
73
74 # arguments
75 print B.bc.HEADER+'Command-line arguments'+B.bc.ENDC
76 B.arguments = btools.validate_arguments(ARGUMENTS, B.bc)
77 btools.print_arguments(B.arguments, B.bc)
78
79 # targets
80 print B.bc.HEADER+'Command-line targets'+B.bc.ENDC
81 B.targets = btools.validate_targets(COMMAND_LINE_TARGETS, B.bc)
82 btools.print_targets(B.targets, B.bc)
83
84 ##########################
85 # setting up environment #
86 ##########################
87
88 # handling cmd line arguments & config file
89
90 # first check cmdline for toolset and we create env to work on
91 quickie = B.arguments.get('BF_QUICK', None)
92 quickdebug = B.arguments.get('BF_QUICKDEBUG', None)
93
94 if quickdebug:
95         B.quickdebug=string.split(quickdebug, ',')
96 else:
97         B.quickdebug=[]
98                 
99 if quickie:
100     B.quickie=string.split(quickie,',')
101 else:
102     B.quickie=[]
103
104 toolset = B.arguments.get('BF_TOOLSET', None)
105 if toolset:
106     print "Using " + toolset
107     if toolset=='mstoolkit':
108         env = BlenderEnvironment(ENV = os.environ)
109         env.Tool('mstoolkit', ['tools'])
110     else:
111         env = BlenderEnvironment(tools=[toolset], ENV = os.environ)
112 else:
113     env = BlenderEnvironment(ENV = os.environ)
114
115 if not env:
116     print "Could not create a build environment"
117     Exit()
118
119 cc = B.arguments.get('CC', None)
120 cxx = B.arguments.get('CXX', None)
121 if cc:
122         env['CC'] = cc
123 if cxx:
124         env['CXX'] = cxx
125
126 if env['CC'] in ['cl', 'cl.exe'] and sys.platform=='win32':
127     platform = 'win32-vc'
128 elif env['CC'] in ['gcc'] and sys.platform=='win32':
129     platform = 'win32-mingw'
130
131 crossbuild = B.arguments.get('BF_CROSS', None)
132 if crossbuild and platform!='win32':
133     platform = 'linuxcross'
134
135 env['OURPLATFORM'] = platform
136
137 configfile = B.arguments.get('BF_CONFIG', 'config'+os.sep+platform+'-config.py')
138
139 if os.path.exists(configfile):
140     print B.bc.OKGREEN + "Using config file: " + B.bc.ENDC + configfile
141 else:
142     print B.bc.FAIL + configfile + " doesn't exist" + B.bc.ENDC
143
144 if crossbuild and env['PLATFORM'] != 'win32':
145     print B.bc.HEADER+"Preparing for crossbuild"+B.bc.ENDC
146     env.Tool('crossmingw', ['tools'])
147     # todo: determine proper libs/includes etc.
148     # Needed for gui programs, console programs should do without it
149     env.Append(LINKFLAGS=['-mwindows'])
150
151 # first read platform config. B.arguments will override
152 optfiles = [configfile]
153 if os.path.exists('user-config.py'):
154     print B.bc.OKGREEN + "Using config file: " + B.bc.ENDC + 'user-config.py'
155     optfiles += ['user-config.py']
156 else:
157     print B.bc.WARNING + 'user-config.py' + " not found, no user overrides" + B.bc.ENDC
158
159 opts = btools.read_opts(optfiles, B.arguments)
160 opts.Update(env)
161
162 #check for additional debug libnames
163
164 if env.has_key('BF_DEBUG_LIBS'):
165         B.quickdebug += env['BF_DEBUG_LIBS']
166
167 printdebug = B.arguments.get('BF_LISTDEBUG', 0)
168
169 # see if this linux distro has libalut
170
171 if env['OURPLATFORM'] == 'linux2' :
172     if env['WITH_BF_OPENAL']:
173         mylib_test_source_file = """
174         #include "AL/alut.h"
175         int main(int argc, char **argv)
176         {
177                 alutGetMajorVersion();
178                 return 0;
179         }
180         """
181
182         def CheckFreeAlut(context,env):
183             context.Message( B.bc.OKGREEN + "Linux platform detected:\n  checking for FreeAlut... " + B.bc.ENDC )
184             env['LIBS'] = 'alut'
185             result = context.TryLink(mylib_test_source_file, '.c')
186             context.Result(result)
187             return result
188
189         env2 = env.Copy( LIBPATH = env['BF_OPENAL'] ) 
190         conf = Configure( env2, {'CheckFreeAlut' : CheckFreeAlut}, '.sconf_temp', '/dev/null' )
191         if conf.CheckFreeAlut( env2 ):
192             env['BF_OPENAL_LIB'] += ' alut'
193         del env2
194         for root, dirs, files in os.walk('.sconf_temp', topdown=False):
195             for name in files:
196                 os.remove(os.path.join(root, name))
197             for name in dirs:
198                 os.rmdir(os.path.join(root, name))
199         os.rmdir(root)
200
201 if len(B.quickdebug) > 0 and printdebug != 0:
202         print B.bc.OKGREEN + "Buildings these libs with debug symbols:" + B.bc.ENDC
203         for l in B.quickdebug:
204                 print "\t" + l
205
206 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
207 if 'blenderplayer' in B.targets:
208     env['WITH_BF_PLAYER'] = True
209
210 if 'blendernogame' in B.targets:
211     env['WITH_BF_GAMEENGINE'] = False
212
213 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
214 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
215 B.root_build_dir = env['BF_BUILDDIR']
216 env['BUILDDIR'] = B.root_build_dir
217 if not B.root_build_dir[-1]==os.sep:
218     B.root_build_dir += os.sep
219
220 # We do a shortcut for clean when no quicklist is given: just delete
221 # builddir without reading in SConscripts
222 do_clean = None
223 if 'clean' in B.targets:
224     do_clean = True
225
226 if not quickie and do_clean:
227     print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
228     dirs = os.listdir(B.root_build_dir)
229     for dir in dirs:
230         if os.path.isdir(B.root_build_dir + dir) == 1:
231             print "clean dir %s"%(B.root_build_dir+dir)
232             shutil.rmtree(B.root_build_dir+dir)
233     print B.bc.OKGREEN+'...done'+B.bc.ENDC
234     Exit()
235
236 if not os.path.isdir ( B.root_build_dir):
237     os.makedirs ( B.root_build_dir )
238     os.makedirs ( B.root_build_dir + 'source' )
239     os.makedirs ( B.root_build_dir + 'intern' )
240     os.makedirs ( B.root_build_dir + 'extern' )
241     os.makedirs ( B.root_build_dir + 'lib' )
242     os.makedirs ( B.root_build_dir + 'bin' )
243
244 Help(opts.GenerateHelpText(env))
245
246 # default is new quieter output, but if you need to see the 
247 # commands, do 'scons BF_QUIET=0'
248 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
249 if bf_quietoutput=='1':
250     B.set_quiet_output(env)
251 else:
252     if toolset=='msvc':
253         B.msvc_hack(env)
254
255 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
256 env.SConsignFile(B.root_build_dir+'scons-signatures')
257 B.init_lib_dict()
258
259 ##### END SETUP ##########
260
261 Export('env')
262 #Export('root_build_dir') # this one is still needed for makesdna
263 ##TODO: improve makesdna usage
264
265 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
266 SConscript(B.root_build_dir+'/intern/SConscript')
267 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
268 SConscript(B.root_build_dir+'/extern/SConscript')
269 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
270 SConscript(B.root_build_dir+'/source/SConscript')
271
272 # now that we have read all SConscripts, we know what
273 # libraries will be built. Create list of
274 # libraries to give as objects to linking phase
275 mainlist = []
276 for tp in B.possible_types:
277     if not tp == 'player' and not tp == 'player2':
278         mainlist += B.create_blender_liblist(env, tp)
279
280 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
281     B.propose_priorities()
282
283 dobj = B.buildinfo(env, "dynamic") + B.resources
284 thestatlibs, thelibincs = B.setup_staticlibs(env)
285 thesyslibs = B.setup_syslibs(env)
286
287 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
288 if env['WITH_BF_PLAYER']:
289     playerlist = B.create_blender_liblist(env, 'player')
290     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
291
292 ##### Now define some targets
293
294
295 #------------ INSTALL
296
297 blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
298
299 #-- .blender
300 dotblendlist = []
301 dottargetlist = []
302 for dp, dn, df in os.walk('bin/.blender'):
303     if 'CVS' in dn:
304         dn.remove('CVS')
305     for f in df:
306         dotblendlist.append(dp+os.sep+f)
307         dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
308
309 dotblenderinstall = []
310 for targetdir,srcfile in zip(dottargetlist, dotblendlist):
311     td, tf = os.path.split(targetdir)
312     dotblenderinstall.append(env.Install(dir=td, source=srcfile))
313
314 #-- .blender/scripts
315 scriptinstall = []
316 scriptpath='release/scripts'
317 for dp, dn, df in os.walk(scriptpath):
318     if 'CVS' in dn:
319         dn.remove('CVS')
320     dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
321     source=[dp+os.sep+f for f in df]
322     scriptinstall.append(env.Install(dir=dir,source=source))
323
324 #-- plugins
325 pluglist = []
326 plugtargetlist = []
327 for tp, tn, tf in os.walk('release/plugins'):
328     if 'CVS' in tn:
329         tn.remove('CVS')
330     for f in tf:
331         pluglist.append(tp+os.sep+f)
332         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
333
334 plugininstall = []
335 for targetdir,srcfile in zip(plugtargetlist, pluglist):
336     td, tf = os.path.split(targetdir)
337     plugininstall.append(env.Install(dir=td, source=srcfile))
338
339 textlist = []
340 texttargetlist = []
341 for tp, tn, tf in os.walk('release/text'):
342     if 'CVS' in tn:
343         tn.remove('CVS')
344     for f in tf:
345         textlist.append(tp+os.sep+f)
346
347 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
348
349 allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
350
351 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
352     dllsources = ['#../lib/windows/gettext/lib/gnu_gettext.dll',
353                         '#../lib/windows/png/lib/libpng.dll',
354                         '#../lib/windows/python/lib/python24.dll',
355                         '#release/windows/extra/python24.zip',
356                         '#release/windows/extra/zlib.pyd',
357                         '#../lib/windows/sdl/lib/SDL.dll',
358                         '#../lib/windows/zlib/lib/zlib.dll',
359                         '#../lib/windows/tiff/lib/libtiff.dll']
360     if env['OURPLATFORM'] == 'win32-mingw':
361         dllsources += ['#../lib/windows/pthreads/lib/pthreadGC2.dll']
362     else:
363         dllsources += ['#../lib/windows/pthreads/lib/pthreadVC2.dll']
364     windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)
365     allinstall += windlls
366
367 installtarget = env.Alias('install', allinstall)
368 bininstalltarget = env.Alias('install-bin', blenderinstall)
369
370 if env['WITH_BF_PLAYER']:
371     blenderplayer = env.Alias('blenderplayer', B.program_list)
372     Depends(blenderplayer,installtarget)
373
374 if not env['WITH_BF_GAMEENGINE']:
375     blendernogame = env.Alias('blendernogame', B.program_list)
376     Depends(blendernogame,installtarget)
377
378
379 Default(B.program_list)
380 Default(installtarget)
381
382 #------------ RELEASE
383 # TODO: zipup the installation
384
385 #------------ BLENDERPLAYER
386 # TODO: build stubs and link into blenderplayer
387
388 #------------ EPYDOC
389 # TODO: run epydoc
390