206 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# SConstruct
 | 
						|
# vim: set ft=python:
 | 
						|
# Eryn Wells <eryn@erynwells.me>
 | 
						|
 | 
						|
 | 
						|
#
 | 
						|
# DEFAULT CONFIGURATION VALUES
 | 
						|
#
 | 
						|
 | 
						|
# Settings for the default toolchain binaries. Setting these overrides the
 | 
						|
# defaults, provided your the given binary exists.
 | 
						|
CC = None
 | 
						|
CXX = None
 | 
						|
AS = None
 | 
						|
LINK = 'clang++'
 | 
						|
 | 
						|
CCFLAGS = ['-Wall', '-Wextra', '-pedantic']
 | 
						|
 | 
						|
 | 
						|
#
 | 
						|
# BUILD STUFF BELOW HERE
 | 
						|
#
 | 
						|
 | 
						|
import os
 | 
						|
import os.path
 | 
						|
import sys
 | 
						|
import SCons.Errors
 | 
						|
 | 
						|
 | 
						|
def which(program):
 | 
						|
    def is_executable(path):
 | 
						|
        return os.path.exists(path) and os.access(path, os.X_OK)
 | 
						|
 | 
						|
    path, name = os.path.split(program)
 | 
						|
    if path:
 | 
						|
        if is_executable(program):
 | 
						|
            return program
 | 
						|
    else:
 | 
						|
        pathext = [''] + os.environ.get('PATHEXT', '').split(os.pathsep)
 | 
						|
        for path in os.environ.get('PATH', '').split(os.pathsep):
 | 
						|
            exe = os.path.join(path, program)
 | 
						|
            for ext in pathext:
 | 
						|
                candidate = exe + ext
 | 
						|
                if is_executable(candidate):
 | 
						|
                    return candidate
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def get_bool_argument(arg):
 | 
						|
    try:
 | 
						|
        return bool(int(arg))
 | 
						|
    except ValueError:
 | 
						|
        pass
 | 
						|
    if arg in ('False', 'FALSE', 'false', ''):
 | 
						|
        return False
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def set_toolchain_binary(env, var, user_binary, binaries=()):
 | 
						|
    if user_binary and which(user_binary):
 | 
						|
        env[var] = user_binary
 | 
						|
        return
 | 
						|
    for c in binaries:
 | 
						|
        if which(c):
 | 
						|
            env[var] = c
 | 
						|
            break
 | 
						|
 | 
						|
common_env = Environment()
 | 
						|
set_toolchain_binary(common_env, 'CC', CC, ('clang', 'gcc'))
 | 
						|
set_toolchain_binary(common_env, 'CXX', CXX, ('clang++', 'g++'))
 | 
						|
set_toolchain_binary(common_env, 'AS', AS)
 | 
						|
set_toolchain_binary(common_env, 'LINK', LINK)
 | 
						|
 | 
						|
 | 
						|
def verbose_build_cmds():
 | 
						|
    def generate_comstr(action):
 | 
						|
        return '{:>25}: $TARGET'.format(action)
 | 
						|
    common_env['ARCOMSTR'] = generate_comstr('Archiving')
 | 
						|
    common_env['ASCOMSTR'] = generate_comstr('Assembling')
 | 
						|
    common_env['ASPPCOMSTR'] = generate_comstr('Assembling')
 | 
						|
    common_env['CCCOMSTR'] = generate_comstr('Building (C)')
 | 
						|
    common_env['CXXCOMSTR'] = generate_comstr('Building (C++)')
 | 
						|
    common_env['LINKCOMSTR'] = generate_comstr('Linking')
 | 
						|
    common_env['RANLIBCOMSTR'] = generate_comstr('Indexing')
 | 
						|
    common_env['SHCCCOMSTR'] = generate_comstr('Building (C, Shared)')
 | 
						|
    common_env['SHCXXCOMSTR'] = generate_comstr('Building (C++, Shared)')
 | 
						|
    common_env['SHLINKCOMSTR'] = generate_comstr('Linking (Shared)')
 | 
						|
 | 
						|
 | 
						|
def succinct_build_cmds():
 | 
						|
    def generate_comstr(action):
 | 
						|
        return '  [{:^6}] $TARGET'.format(action)
 | 
						|
    common_env['ARCOMSTR'] = generate_comstr('AR')
 | 
						|
    common_env['ASCOMSTR'] = generate_comstr('AS')
 | 
						|
    common_env['ASPPCOMSTR'] = generate_comstr('AS')
 | 
						|
    common_env['CCCOMSTR'] = generate_comstr('CC')
 | 
						|
    common_env['CXXCOMSTR'] = generate_comstr('CXX')
 | 
						|
    common_env['LINKCOMSTR'] = generate_comstr('LINK')
 | 
						|
    common_env['RANLIBCOMSTR'] = generate_comstr('RANLIB')
 | 
						|
    common_env['SHCCCOMSTR'] = generate_comstr('SHCC')
 | 
						|
    common_env['SHCXXCOMSTR'] = generate_comstr('SHCXX')
 | 
						|
    common_env['SHLINKCOMSTR'] = generate_comstr('SHLINK')
 | 
						|
 | 
						|
 | 
						|
BUILD_CMDS = get_bool_argument(ARGUMENTS.get('BUILD_CMDS', False))
 | 
						|
if not BUILD_CMDS:
 | 
						|
    verbose_build_cmds()
 | 
						|
 | 
						|
# Separate environment for building libraries because they often don't use the
 | 
						|
# same CCFLAGS I do.
 | 
						|
lib_env = common_env.Clone()
 | 
						|
 | 
						|
common_env.Append(CCFLAGS=CCFLAGS)
 | 
						|
common_env.Append(CFLAGS=['-std=c99'])
 | 
						|
common_env.Append(CXXFLAGS=['-std=c++11'])
 | 
						|
 | 
						|
# Add color error messages for clang
 | 
						|
if sys.stdout.isatty():
 | 
						|
    if 'clang' in common_env['CC'] or 'clang' in common_env['CXX']:
 | 
						|
        common_env.Append(CCFLAGS=['-fcolor-diagnostics'])
 | 
						|
 | 
						|
BUILD_DIR = Dir('#build')
 | 
						|
LIB_DIR = Dir('#lib')
 | 
						|
SRC_DIR = Dir('#src')
 | 
						|
TEST_DIR = Dir('#test')
 | 
						|
 | 
						|
 | 
						|
def create_env(name, appends=None):
 | 
						|
    output_dir = BUILD_DIR.Dir(name)
 | 
						|
    env = common_env.Clone()
 | 
						|
    # Standard env extensions.
 | 
						|
    env.Append(CPPPATH=[SRC_DIR])
 | 
						|
    # Custom env stuff.
 | 
						|
    env['__name'] = name
 | 
						|
    if appends:
 | 
						|
        for k, v in appends.iteritems():
 | 
						|
            if k.startswith('='):
 | 
						|
                env[k[1:]] = v
 | 
						|
            else:
 | 
						|
                env.Append(**{k: v})
 | 
						|
    return env
 | 
						|
 | 
						|
 | 
						|
def do_sconscript(env, build_env, src_dir, out_dir):
 | 
						|
    sconscript = src_dir.File('SConscript')
 | 
						|
    print 'Reading {}'.format(sconscript)
 | 
						|
    # Swapping env and build_env here is a bit wacky. Doing so means that env is
 | 
						|
    # always the Environment that the SConscript should be building with, while
 | 
						|
    # build_env is the Environment we're using to put everything together.
 | 
						|
    env.SConscript(sconscript,
 | 
						|
                   {'env': build_env, 'build_env': env},
 | 
						|
                   variant_dir=out_dir)
 | 
						|
 | 
						|
 | 
						|
debug_env = create_env('debug', {
 | 
						|
    'CPPDEFINES': ['NDEBUG'],
 | 
						|
    'CCFLAGS': ['-O0', '-g'],
 | 
						|
})
 | 
						|
 | 
						|
beta_env = create_env('beta', {
 | 
						|
    'CPPDEFINES': ['NDEBUG'],
 | 
						|
    'CCFLAGS': ['-O3', '-g'],
 | 
						|
})
 | 
						|
 | 
						|
release_env = create_env('release', {
 | 
						|
    'CPPDEFINES': ['NRELEASE'],
 | 
						|
    'CCFLAGS': ['-O3']
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
modes = {
 | 
						|
    'debug': debug_env,
 | 
						|
    'beta': beta_env,
 | 
						|
    'release': release_env,
 | 
						|
}
 | 
						|
 | 
						|
mode = ARGUMENTS.get('MODE', None)
 | 
						|
build_modes = []
 | 
						|
if mode:
 | 
						|
    # If MODE=foo is specified, build only that mode.
 | 
						|
    build_modes.append(mode)
 | 
						|
else:
 | 
						|
    build_modes = ['debug']
 | 
						|
 | 
						|
for mode in build_modes:
 | 
						|
    try:
 | 
						|
        env = modes[mode]
 | 
						|
    except KeyError:
 | 
						|
        print 'Skipping invalid mode: {}'.format(mode)
 | 
						|
        break
 | 
						|
 | 
						|
    out_dir = BUILD_DIR.Dir(env['__name'])
 | 
						|
 | 
						|
    # Process all lib dirs.
 | 
						|
    for lib in os.listdir(LIB_DIR.abspath):
 | 
						|
        lib_out_dir = out_dir.Dir('lib').Dir(lib)
 | 
						|
        do_sconscript(env, lib_env, LIB_DIR.Dir(lib), lib_out_dir)
 | 
						|
        env.Append(LIBPATH=[lib_out_dir])
 | 
						|
 | 
						|
    # Get source files.
 | 
						|
    src_out_dir = out_dir.Dir('src')
 | 
						|
    do_sconscript(env, env, SRC_DIR, src_out_dir)
 | 
						|
    env.Append(LIBPATH=[src_out_dir])
 | 
						|
 | 
						|
    # Get test binaries.
 | 
						|
    do_sconscript(env, env, TEST_DIR, out_dir.Dir('test'))
 |