use mkdtemp for the scons tempfile path rather then '.sconf_temp', this way 2 blender...
[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 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         for root, dirs, files in os.walk(sconf_temp, topdown=False):
234             for name in files:
235                 os.remove(os.path.join(root, name))
236             for name in dirs:
237                 os.rmdir(os.path.join(root, name))
238         os.rmdir(root)
239
240 if len(B.quickdebug) > 0 and printdebug != 0:
241     print B.bc.OKGREEN + "Buildings these libs with debug symbols:" + B.bc.ENDC
242     for l in B.quickdebug:
243         print "\t" + l
244
245 # check target for blenderplayer. Set WITH_BF_PLAYER if found on cmdline
246 if 'blenderplayer' in B.targets:
247     env['WITH_BF_PLAYER'] = True
248
249 if 'blendernogame' in B.targets:
250     env['WITH_BF_GAMEENGINE'] = False
251
252 # lastly we check for root_build_dir ( we should not do before, otherwise we might do wrong builddir
253 #B.root_build_dir = B.arguments.get('BF_BUILDDIR', '..'+os.sep+'build'+os.sep+platform+os.sep)
254 B.root_build_dir = env['BF_BUILDDIR']
255 env['BUILDDIR'] = B.root_build_dir
256 if not B.root_build_dir[-1]==os.sep:
257     B.root_build_dir += os.sep
258     
259 # We do a shortcut for clean when no quicklist is given: just delete
260 # builddir without reading in SConscripts
261 do_clean = None
262 if 'clean' in B.targets:
263     do_clean = True
264
265 if not quickie and do_clean:
266     if os.path.exists(B.root_build_dir):
267         print B.bc.HEADER+'Cleaning...'+B.bc.ENDC
268         dirs = os.listdir(B.root_build_dir)
269         for dir in dirs:
270             if os.path.isdir(B.root_build_dir + dir) == 1:
271                 print "clean dir %s"%(B.root_build_dir+dir)
272                 shutil.rmtree(B.root_build_dir+dir)
273         print B.bc.OKGREEN+'...done'+B.bc.ENDC
274     else:
275         print B.bc.HEADER+'Already Clean, nothing to do.'+B.bc.ENDC
276     Exit()
277
278 if not os.path.isdir ( B.root_build_dir):
279     os.makedirs ( B.root_build_dir )
280     os.makedirs ( B.root_build_dir + 'source' )
281     os.makedirs ( B.root_build_dir + 'intern' )
282     os.makedirs ( B.root_build_dir + 'extern' )
283     os.makedirs ( B.root_build_dir + 'lib' )
284     os.makedirs ( B.root_build_dir + 'bin' )
285
286 Help(opts.GenerateHelpText(env))
287
288 # default is new quieter output, but if you need to see the 
289 # commands, do 'scons BF_QUIET=0'
290 bf_quietoutput = B.arguments.get('BF_QUIET', '1')
291 if env['BF_QUIET']:
292     B.set_quiet_output(env)
293 else:
294     if toolset=='msvc':
295         B.msvc_hack(env)
296
297 print B.bc.HEADER+'Building in '+B.bc.ENDC+B.root_build_dir
298 env.SConsignFile(B.root_build_dir+'scons-signatures')
299 B.init_lib_dict()
300
301 ##### END SETUP ##########
302
303 Export('env')
304
305 BuildDir(B.root_build_dir+'/intern', 'intern', duplicate=0)
306 SConscript(B.root_build_dir+'/intern/SConscript')
307 BuildDir(B.root_build_dir+'/extern', 'extern', duplicate=0)
308 SConscript(B.root_build_dir+'/extern/SConscript')
309 BuildDir(B.root_build_dir+'/source', 'source', duplicate=0)
310 SConscript(B.root_build_dir+'/source/SConscript')
311
312 # now that we have read all SConscripts, we know what
313 # libraries will be built. Create list of
314 # libraries to give as objects to linking phase
315 mainlist = []
316 for tp in B.possible_types:
317     if not tp == 'player' and not tp == 'player2':
318         mainlist += B.create_blender_liblist(env, tp)
319
320 if B.arguments.get('BF_PRIORITYLIST', '0')=='1':
321     B.propose_priorities()
322
323 dobj = B.buildinfo(env, "dynamic") + B.resources
324 thestatlibs, thelibincs = B.setup_staticlibs(env)
325 thesyslibs = B.setup_syslibs(env)
326
327 env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
328 if env['WITH_BF_PLAYER']:
329     playerlist = B.create_blender_liblist(env, 'player')
330     env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist + thestatlibs, [], thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
331
332 ##### Now define some targets
333
334
335 #------------ INSTALL
336
337 #-- binaries
338 blenderinstall = []
339 if  env['OURPLATFORM']=='darwin':
340     for prg in B.program_list:
341         bundle = '%s.app' % prg[0]
342         bundledir = os.path.dirname(bundle)
343         for dp, dn, df in os.walk(bundle):
344             if 'CVS' in dn:
345                 dn.remove('CVS')
346             if '.svn' in dn:
347                 dn.remove('.svn')
348             dir=env['BF_INSTALLDIR']+dp[len(bundledir):]
349             source=[dp+os.sep+f for f in df]
350             blenderinstall.append(env.Install(dir=dir,source=source))
351 else:
352     blenderinstall = env.Install(dir=env['BF_INSTALLDIR'], source=B.program_list)
353
354 #-- .blender
355 #- dont do .blender and scripts for darwin, it is already in the bundle
356 dotblendlist = []
357 dottargetlist = []
358 scriptinstall = []
359
360 if  env['OURPLATFORM']!='darwin':
361         for dp, dn, df in os.walk('bin/.blender'):
362             if 'CVS' in dn:
363                 dn.remove('CVS')
364             if '.svn' in dn:
365                 dn.remove('.svn')
366             for f in df:
367                 dotblendlist.append(dp+os.sep+f)
368                 dottargetlist.append(env['BF_INSTALLDIR']+dp[3:]+os.sep+f)
369
370         dotblenderinstall = []
371         for targetdir,srcfile in zip(dottargetlist, dotblendlist):
372             td, tf = os.path.split(targetdir)
373             dotblenderinstall.append(env.Install(dir=td, source=srcfile))
374         
375         #-- .blender/scripts    
376         scriptpath='release/scripts'
377         for dp, dn, df in os.walk(scriptpath):
378             if 'CVS' in dn:
379                 dn.remove('CVS')
380             if '.svn' in dn:
381                 dn.remove('.svn')
382             dir=env['BF_INSTALLDIR']+'/.blender/scripts'+dp[len(scriptpath):]
383             source=[dp+os.sep+f for f in df]
384             scriptinstall.append(env.Install(dir=dir,source=source))
385
386 #-- plugins
387 pluglist = []
388 plugtargetlist = []
389 for tp, tn, tf in os.walk('release/plugins'):
390     if 'CVS' in tn:
391         tn.remove('CVS')
392     if '.svn' in tn:
393         tn.remove('.svn')
394     for f in tf:
395         pluglist.append(tp+os.sep+f)
396         plugtargetlist.append(env['BF_INSTALLDIR']+tp[7:]+os.sep+f)
397
398 plugininstall = []
399 for targetdir,srcfile in zip(plugtargetlist, pluglist):
400     td, tf = os.path.split(targetdir)
401     plugininstall.append(env.Install(dir=td, source=srcfile))
402
403 textlist = []
404 texttargetlist = []
405 for tp, tn, tf in os.walk('release/text'):
406     if 'CVS' in tn:
407         tn.remove('CVS')
408     if '.svn' in tn:
409         tn.remove('.svn')
410     for f in tf:
411         textlist.append(tp+os.sep+f)
412
413 textinstall = env.Install(dir=env['BF_INSTALLDIR'], source=textlist)
414
415 if  env['OURPLATFORM']=='darwin':
416         allinstall = [blenderinstall, plugininstall, textinstall]
417 else:
418         allinstall = [blenderinstall, dotblenderinstall, scriptinstall, plugininstall, textinstall]
419
420 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw'):
421     dllsources = ['${LCGDIR}/gettext/lib/gnu_gettext.dll',
422                         '${LCGDIR}/png/lib/libpng.dll',
423                         '#release/windows/extra/python25.zip',
424                         '#release/windows/extra/zlib.pyd',
425                         '${LCGDIR}/sdl/lib/SDL.dll',
426                         '${LCGDIR}/zlib/lib/zlib.dll',
427                         '${LCGDIR}/tiff/lib/libtiff.dll']
428     if env['BF_DEBUG']:
429         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}_d.dll')
430     else:
431         dllsources.append('${LCGDIR}/python/lib/${BF_PYTHON_LIB}.dll')
432     if env['OURPLATFORM'] == 'win32-mingw':
433         dllsources += ['${LCGDIR}/pthreads/lib/pthreadGC2.dll']
434     else:
435         dllsources += ['${LCGDIR}/pthreads/lib/pthreadVC2.dll']
436     if env['WITH_BF_ICONV']:
437         dllsources += ['${LCGDIR}/iconv/lib/iconv.dll']
438 #    if env['WITH_BF_FFMPEG']:
439 #        dllsources += ['${LCGDIR}/ffmpeg/lib/avcodec-51.dll',
440 #                        '${LCGDIR}/ffmpeg/lib/avformat-51.dll',
441 #                        '${LCGDIR}/ffmpeg/lib/avutil-49.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