SConstruct
author Radek Brich <radek.brich@devl.cz>
Wed, 19 Aug 2009 09:18:29 +0200
branchpyrit
changeset 101 50a994a57849
parent 100 c005054bf4c1
child 102 de3e9ea18f56
permissions -rw-r--r--
vcproj build target only for cc=msvc, fix sdl-config

Help("""
Targets:
    all              - build everything,

    libs             - build all libraries
    demos            - build all demos
    models           - download/prepare all models
    docs             - compile doxygen documentation

    libs = (static-lib, python-module)
    static-lib       - ray tracer library to link with
    python-module    - ray tracer module for Python

    demos = (python-demos, cc-demos)
    python-demos     - Python demos, this depends on python-module
    cc-demos         - C++ demos

    models = (local-models, download-models)
    local-models     - prepare local models
    download-models  - download models which are not locally available

    no-docs = (libs, demos, models)
                     - everything but docs
    no-download = (libs, demos, local-models)
                     - everything but docs and downloadable models

    vcproj           - generate project files for Visual C++ Studio

Default target is no-download.

Options:
""")

import os, sys

EnsurePythonVersion(2, 3)
EnsureSConsVersion(0, 97)

Decider('MD5-timestamp')
SConsignFile('build/.sconsign.dblite')

if sys.platform == 'win32':
	tools = ['mingw']
else:
	tools = ['default']

env = Environment(tools = tools, CPPPATH = ['.','#include','#build/include'])

vars = Variables(files=['build/.varscache'])
vars.AddVariables(
	BoolVariable('simd', 'allow SSE intrinsics', True),
	('precision', 'floating point number precision (single/double)', "single"),
	('flags', 'add additional compiler flags', ""),
	BoolVariable('force_flags', "use only flags specified by 'flags' option (do not autodetect arch/sse flags)", False),
	('ldflags', 'add additional linker flags', ""),
	BoolVariable('profile', "enable gcc's profiling support (-pg)", False),
)
if env['PLATFORM'] == 'win32':
	vars.AddVariables(
		BoolVariable('mingw', 'use Mingw and GCC compiler, if available', False),
		('pythonpath', 'path to Python installation',
			'C:\\Python%c%c' % (sys.version[0], sys.version[2])),
	)
else:
	vars.AddVariables(
		BoolVariable('intelc', 'use Intel C++ Compiler, if available', False),
	)


vars.Update(env)
vars.Save('build/.varscache', env)
Help(vars.GenerateHelpText(env))

if env.GetOption('help') == True or env.GetOption('clean') == True:
    Return()

### configure

platform = 'unknown'
def CheckPlatform(context):
	global platform
	context.Message('Platform is... ')
	if sys.platform[:5] == 'linux':
		platform = 'linux'
	elif env['PLATFORM'] == 'posix':
		platform = 'posix'
	elif env['PLATFORM'] == 'win32':
		platform = 'win32'
	context.Result(platform)
	return True

intelcversion = ''
def CheckIntelC(context):
	global intelc, intelcversion
	context.Message('Checking for IntelC compiler... ')
	intelc = Tool("intelc").exists(env) == True
	if intelc:
		testenv = Environment()
		Tool("intelc").generate(testenv)
		intelcversion = str(testenv['INTEL_C_COMPILER_VERSION']/10.)
		context.Result(intelcversion)
	else:
		context.Result(intelc)
	return intelc

def CheckGCC(context):
	global gcc, gccversion
	context.Message('Checking for GCC compiler... ')
	testenv = Environment()
	gcc = "g++" in testenv['TOOLS']
	if gcc:
		gccversion = testenv['CCVERSION']
		context.Result(gccversion)
	else:
		gccversion = ''
		context.Result(False)
	return gcc

def CheckMSVC(context):
	global msvc, msvcversion
	context.Message('Checking for MSVC compiler... ')
	testenv = Environment()
	msvc = "msvc" in testenv['TOOLS']
	if msvc:
		msvcversion = testenv['MSVS_VERSION']
		context.Result(msvcversion)
	else:
		msvcversion = ''
		context.Result(False)
	return msvc

def CheckCPUFlags(context):
	global cpu, cpuflags_gcc, cpuflags_intelc
	context.Message('Checking CPU arch and flags... ')
	env.Execute('@$CC tools/cpuflags.c -o tools/cpuflags')
	(cpu, cpuflags_gcc, cpuflags_intelc) = os.popen('tools'+os.sep+'cpuflags %s %s'
		% (''.join(gccversion.rsplit('.',1)), intelcversion) ).read().split('\n')[:3]
	context.Result(cpu)
	return True

conf_dir = "#build/.sconf_temp"
log_file="#build/config.log"
config_h="#build/include/config.h"
conf = Configure(env, conf_dir=conf_dir, log_file=log_file, config_h=config_h,
    clean=False, help=False,
	custom_tests = {
		'CheckPlatform' : CheckPlatform, 'CheckCPUFlags' : CheckCPUFlags,
		'CheckIntelC' : CheckIntelC, 'CheckGCC' : CheckGCC, 'CheckMSVC' : CheckMSVC})
conf.CheckPlatform()

conf.CheckGCC()
if platform == 'win32':
	conf.CheckMSVC()
	intelc = False
else:
	conf.CheckIntelC()
	msvc=False

if intelc and (not gcc or conf.env['intelc']):
	Tool('intelc').generate(conf.env)
	cc = 'intelc'
elif msvc and (not gcc or not conf.env['mingw']):
	Tool('default').generate(conf.env)
	conf.Define("MSVC")
	cc = 'msvc'
elif gcc:
	cc = 'gcc'
else:
	cc = 'none'

if cc == 'intelc' or cc == 'gcc':
	conf.CheckCPUFlags()

if platform == 'win32' and cc == 'gcc':
	conf.env.Append(LIBPATH=["C:/mingw/lib", "C:/msys/mingw/lib"])
	conf.env.Append(CPPPATH=["C:/mingw/include", "C:/msys/mingw/include"])

add_flags = ''
if cc == 'gcc':
	add_flags += cpuflags_gcc + ' -ffast-math '
if cc == 'intelc':
	add_flags += cpuflags_intelc + ' '
if cc == 'msvc':
	add_flags += '/fp:fast '
	if conf.env['simd']:
		add_flags += '/arch:SSE '

if conf.env['force_flags']:
	add_flags = conf.env['flags'] + ' '
else:
	add_flags += conf.env['flags'] + ' '

if conf.env['precision'] == 'double':
	conf.Define("PYRIT_DOUBLE")

if not conf.env['simd'] or conf.env['precision'] == 'double':
	conf.Define("NO_SIMD")

if cc == 'intelc':
	conf.env.Append(CCFLAGS="-O3 -w1 " + add_flags)
elif cc == 'gcc':
	conf.env.Append(CCFLAGS="-O3 -Wall -pipe " + add_flags)
	# Other useful flags:
	# -Wunsafe-loop-optimizations -Wpointer-arith -Wcast-align -Wconversion
	# -Wmissing-noreturn -Winline -Wdisabled-optimization
elif cc == 'msvc':
	conf.env.Append(CCFLAGS="/Ox /Ob2 /GS- /Gy /GF /GR- /Zp16 /MD /EHsc /vmb " + add_flags)
else:
	print "No supported compiler found."
	Exit(1)

print "Using compiler: " + cc
print "Additional flags: " + add_flags

if conf.env['profile'] and cc == 'gcc':
	conf.env.Append(CCFLAGS="-pg", LINKFLAGS="-pg")

if conf.env['ldflags']:
	conf.env.Append(LINKFLAGS=conf.env['ldflags'])


# configure pthread
pthread = True
if platform == 'win32':
	if cc == 'msvc':
		if not conf.CheckLib('pthreadVC2'):
			pthread = False
	elif cc == 'gcc':
		if not conf.CheckLib('pthreadGC2'):
			pthread = False
else:
	conf.env.Append(CCFLAGS="-pthread ")

if not pthread:
	print 'Error: Cannot build without pthread.'
	Exit(1)


# configure libpng
conf.CheckLibWithHeader('zlib', 'zlib.h', 'C')
if conf.CheckLibWithHeader('libpng', 'png.h', 'C'):
	conf.Define('HAVE_PNG')

env = conf.Finish()


# configure Python
pyenv = env.Clone()
have_python = True
if platform == 'win32':
	pythonver = '%c%c' % (sys.version[0], sys.version[2])
	pythonlib = 'python'+pythonver
	pythonpath = [env['pythonpath']]
	pyenv.Append(CPPPATH=[s+'\\include' for s in pythonpath])
	pyenv.Append(LIBPATH=[s+'\\libs' for s in pythonpath])
	pyenv.Replace(SHLIBSUFFIX='.pyd')
	conf = Configure(pyenv, conf_dir=conf_dir, log_file=log_file, config_h=config_h,
	                 clean=False, help=False)
	if not conf.CheckLib(pythonlib):
		have_python = False
	pyenv = conf.Finish()
else:
	try:
		pyenv.ParseConfig('python-config --includes --libs')
	except:
		have_python = False

if not have_python:
	print "Error: Python is required."
	Exit(1)

# configure SDL
sdlenv = env.Clone()
if cc == 'msvc':
	sdlenv.Append(LIBS=['SDL', 'SDLmain'])
	sdlenv.Append(LINKFLAGS="/SUBSYSTEM:WINDOWS")
else:
	try:
		sdlenv.ParseConfig('sdl-config --cflags')
		sdlenv.ParseConfig('sdl-config --libs')
	except:
		pass

def CheckSDL(context):
	global have_sdl
	context.Message('Checking for SDL... ')
	if context.TryLink("#include <SDL.h>\n"+
		"int main(int argc,char **argv){return 0;}", '.cc'):
		context.Result(1)
		return True
	else:
		context.Result("no (some demos won't be built)")
	return False

conf = Configure(sdlenv, conf_dir=conf_dir, log_file=log_file, config_h=config_h,
    clean=False, help=False,
	custom_tests = {'CheckSDL' : CheckSDL} )
have_sdl = conf.CheckSDL()
sdlenv = conf.Finish()


### build targets

Export('env pyenv sdlenv have_sdl cc')
lib = SConscript('src/SConscript', build_dir='build/lib', duplicate=0,
	exports={'buildmodule':False})
if cc == 'msvc':
	(pymodule, modvcproj) = SConscript('src/SConscript', build_dir='build/pymodule', duplicate=0,
		exports={'buildmodule':True})
	ccdemvcproj = SConscript('ccdemos/SConscript', build_dir='build/ccdemos', duplicate=0, exports='lib')
	demosvcproj = SConscript('demos/SConscript', exports='pymodule')
else:
	pymodule = SConscript('src/SConscript', build_dir='build/pymodule', duplicate=0,
		exports={'buildmodule':True})
	SConscript('ccdemos/SConscript', build_dir='build/ccdemos', duplicate=0, exports='lib')
	SConscript('demos/SConscript', exports='pymodule')

SConscript('tests/SConscript', build_dir='build/tests', duplicate=0, exports='lib')
SConscript('models/SConscript')

env.Alias('demos', ['cc-demos', 'python-demos'])
env.Alias('libs', ['static-lib', 'python-module'])
env.Alias('docs', Command('docs/html', [], 'doxygen'))
env.Clean('docs', ['docs/html'])
env.Alias('no-docs', ['libs', 'demos', 'models'])
env.Alias('no-download', ['libs', 'demos', 'local-models'])
env.Alias('all', ['no-docs', 'docs'])
env.Alias('pyrit', 'no-download')
Default('pyrit')

if cc == 'msvc':
	vcsol = env.MSVSSolution(
		target = 'pyrit' + env['MSVSSOLUTIONSUFFIX'],
		projects = [modvcproj, ccdemvcproj, demosvcproj],
		variant = 'Release')

	env.Depends(vcsol, [modvcproj, ccdemvcproj, demosvcproj]);
	env.Alias('vcproj', vcsol)