- modified patch #4681, for scons compiling
[blender.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 # disable elbeem (fluidsim) compilation?
163 if env['BF_NO_ELBEEM'] == 'true':
164         env['CPPFLAGS'].append('-DDISABLE_ELBEEM')
165         env['CXXFLAGS'].append('-DDISABLE_ELBEEM')
166         env['CCFLAGS'].append('-DDISABLE_ELBEEM')
167
168 #check for additional debug libnames
169
170 if env.has_key('BF_DEBUG_LIBS'):
171         B.quickdebug += env['BF_DEBUG_LIBS']
172
173 printdebug = B.arguments.get('BF_LISTDEBUG', 0)
174
175 # see if this linux distro has libalut
176
177 if env['OURPLATFORM'] == 'linux2' :
178     if env['WITH_BF_OPENAL']:
179         mylib_test_source_file = """
180         #include "AL/alut.h"
181         int main(int argc, char **argv)
182         {
183                 alutGetMajorVersion();
184                 return 0;
185         }
186         """
187
188         def CheckFreeAlut(context,env):
189             context.Message( B.bc.OKGREEN + "Linux platform detected:\n  checking for FreeAlut... " + B.bc.ENDC )
190             env['LIBS'] = 'alut'
191             result = context.TryLink(mylib_test_source_file, '.c')
192             context.Result(result)
193             return result
194
195         env2 = env.Copy( LIBPATH = env['BF_OPENAL'] ) 
196         conf = Configure( env2, {'CheckFreeAlut' : CheckFreeAlut}, '.sconf_temp', '/dev/null' )
197         if conf.CheckFreeAlut( env2 ):
198             env['BF_OPENAL_LIB'] += ' alut'
199         del env2
200         for root, dirs, files in os.walk('.sconf_temp', topdown=False):
201             for name in files:
202                 os.remove(os.path.join(root, name))
203             for name in dirs:
204                 os.rmdir(os.path.join(root, name))
205         os.rmdir(root)
206
207 if len(B.quickdebug) > 0 and printdebug != 0:
208         print B.bc.OKGREEN + "Buildings these libs with debug symbols:" + B.bc.ENDC
209         for l in B.quickdebug:
210                 print "\t" + l
211
212 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
213 if 'blenderplayer' in B.targets:
214     env['WITH_BF_PLAYER'] = True
215
216 if 'blendernogame' in B.targets:
217     env['WITH_BF_GAMEENGINE'] = False
218
219 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
220 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
221 B.root_build_dir = env['BF_BUILDDIR']
222 env['BUILDDIR'] = B.root_build_dir
223 if not B.root_build_dir[-1]==os.sep:
224     B.root_build_dir += os.sep
225
226 # We do a shortcut for clean when no quicklist is given: just delete
227 # builddir without reading in SConscripts
228 do_clean = None
229 if 'clean' in B.targets:
230     do_clean = True
231
232 if not quickie and do_clean:
233     print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
234     dirs = os.listdir(B.root_build_dir)
235     for dir in dirs:
236         if os.path.isdir(B.root_build_dir + dir) == 1:
237             print "clean dir %s"%(B.root_build_dir+dir)
238             shutil.rmtree(B.root_build_dir+dir)
239     print B.bc.OKGREEN+'...done'+B.bc.ENDC
240     Exit()
241
242 if not os.path.isdir ( B.root_build_dir):
243     os.makedirs ( B.root_build_dir )
244     os.makedirs ( B.root_build_dir + 'source' )
245     os.makedirs ( B.root_build_dir + 'intern' )
246     os.makedirs ( B.root_build_dir + 'extern' )
247     os.makedirs ( B.root_build_dir + 'lib' )
248     os.makedirs ( B.root_build_dir + 'bin' )
249
250 Help(opts.GenerateHelpText(env))
251
252 # default is new quieter output, but if you need to see the 
253 # commands, do 'scons BF_QUIET=0'
254 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
255 if bf_quietoutput=='1':
256     B.set_quiet_output(env)
257 else:
258     if toolset=='msvc':
259         B.msvc_hack(env)
260
261 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
262 env.SConsignFile(B.root_build_dir+'scons-signatures')
263 B.init_lib_dict()
264
265 ##### END SETUP ##########
266
267 Export('env')
268 #Export('root_build_dir') # this one is still needed for makesdna
269 ##TODO: improve makesdna usage
270
271 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
272 SConscript(B.root_build_dir+'/intern/SConscript')
273 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
274 SConscript(B.root_build_dir+'/extern/SConscript')
275 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
276 SConscript(B.root_build_dir+'/source/SConscript')
277
278 # now that we have read all SConscripts, we know what
279 # libraries will be built. Create list of
280 # libraries to give as objects to linking phase
281 mainlist = []
282 for tp in B.possible_types:
283     if not tp == 'player' and not tp == 'player2':
284         mainlist += B.create_blender_liblist(env, tp)
285
286 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
287     B.propose_priorities()
288
289 dobj = B.buildinfo(env, "dynamic") + B.resources
290 thestatlibs, thelibincs = B.setup_staticlibs(env)
291 thesyslibs = B.setup_syslibs(env)
292
293 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
294 if env['WITH_BF_PLAYER']:
295     playerlist = B.create_blender_liblist(env, 'player')
296     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
297
298 ##### Now define some targets
299
300
301 #------------ INSTALL
302
303 blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
304
305 #-- .blender
306 dotblendlist = []
307 dottargetlist = []
308 for dp, dn, df in os.walk('bin/.blender'):
309     if 'CVS' in dn:
310         dn.remove('CVS')
311     for f in df:
312         dotblendlist.append(dp+os.sep+f)
313         dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
314
315 dotblenderinstall = []
316 for targetdir,srcfile in zip(dottargetlist, dotblendlist):
317     td, tf = os.path.split(targetdir)
318     dotblenderinstall.append(env.Install(dir=td, source=srcfile))
319
320 #-- .blender/scripts
321 scriptinstall = []
322 scriptpath='release/scripts'
323 for dp, dn, df in os.walk(scriptpath):
324     if 'CVS' in dn:
325         dn.remove('CVS')
326     dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
327     source=[dp+os.sep+f for f in df]
328     scriptinstall.append(env.Install(dir=dir,source=source))
329
330 #-- plugins
331 pluglist = []
332 plugtargetlist = []
333 for tp, tn, tf in os.walk('release/plugins'):
334     if 'CVS' in tn:
335         tn.remove('CVS')
336     for f in tf:
337         pluglist.append(tp+os.sep+f)
338         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
339
340 plugininstall = []
341 for targetdir,srcfile in zip(plugtargetlist, pluglist):
342     td, tf = os.path.split(targetdir)
343     plugininstall.append(env.Install(dir=td, source=srcfile))
344
345 textlist = []
346 texttargetlist = []
347 for tp, tn, tf in os.walk('release/text'):
348     if 'CVS' in tn:
349         tn.remove('CVS')
350     for f in tf:
351         textlist.append(tp+os.sep+f)
352
353 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
354
355 allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
356
357 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
358     dllsources = ['#../lib/windows/gettext/lib/gnu_gettext.dll',
359                         '#../lib/windows/png/lib/libpng.dll',
360                         '#../lib/windows/python/lib/python24.dll',
361                         '#release/windows/extra/python24.zip',
362                         '#release/windows/extra/zlib.pyd',
363                         '#../lib/windows/sdl/lib/SDL.dll',
364                         '#../lib/windows/zlib/lib/zlib.dll',
365                         '#../lib/windows/tiff/lib/libtiff.dll']
366     if env['OURPLATFORM'] == 'win32-mingw':
367         dllsources += ['#../lib/windows/pthreads/lib/pthreadGC2.dll']
368     else:
369         dllsources += ['#../lib/windows/pthreads/lib/pthreadVC2.dll']
370     windlls = env.Install(dir=env['BF_INSTALLDIR'], source = dllsources)
371     allinstall += windlls
372
373 installtarget = env.Alias('install', allinstall)
374 bininstalltarget = env.Alias('install-bin', blenderinstall)
375
376 if env['WITH_BF_PLAYER']:
377     blenderplayer = env.Alias('blenderplayer', B.program_list)
378     Depends(blenderplayer,installtarget)
379
380 if not env['WITH_BF_GAMEENGINE']:
381     blendernogame = env.Alias('blendernogame', B.program_list)
382     Depends(blendernogame,installtarget)
383
384 Default(B.program_list)
385 Default(installtarget)
386
387 #------------ RELEASE
388 # TODO: zipup the installation
389
390 #------------ BLENDERPLAYER
391 # TODO: build stubs and link into blenderplayer
392
393 #------------ EPYDOC
394 # TODO: run epydoc
395