Skip to content

Instantly share code, notes, and snippets.

@ringods
Created September 22, 2011 07:14
Show Gist options
  • Save ringods/1234232 to your computer and use it in GitHub Desktop.
Save ringods/1234232 to your computer and use it in GitHub Desktop.
Build setup (added .py extension to SConstruct/SConscript for gist to detect Python code, full path on first line of content)
# thirdparty/site_scons/site_tools/AutoConfig.py
# AutoConfig Builder: Runs ./configure inside a directory.
# This builder takes over the compile and linker flags from the following SCons settings.
# Set all required flags on the environment before calling the builder.
# - CFLAGS
# - CCFLAGS
# - CPPFLAGS
# - CXXFLAGS
# - LINKFLAGS
#
# Variables:
# CONFIGURE_FLAGS -- Sequence of parameter strings to include on the
# configure command line.
# Default: [ ]
# CONFIGURE_BUILDDIR -- The location where generated files will be put.
# Default: 'build'
# CONFIGURE_BUILDVARIANT -- An optional part to the build dir to separate build variants from each other
# Default: ''
# CONFIGURE_PREFIX -- The location that will be used by the Make builder for the install target
# Default: ''
# CONFIGURE_COMSTR -- Parameterized string to use for displaying the invoked command.
# Default: None
import subprocess
import SCons.Util
from SCons.Script import *
def __expand_paths(target, source, env):
"""
Expand the paths to be used by the emitter
"""
configure_path = Dir('.')
if len(source) > 0:
configure_path = source[0]
# Calculating the directory in which to run the configure script.
if 'CONFIGURE_BUILDDIR' in env:
configure_builddir = configure_path.Dir(str(env['CONFIGURE_BUILDDIR']))
if 'CONFIGURE_BUILDVARIANT' in env:
if env['CONFIGURE_BUILDVARIANT']:
configure_builddir = configure_builddir.Dir(str(env['CONFIGURE_BUILDVARIANT']))
#print '__expand_paths(target,source,env) returns:'
#print 'configure_path = %s' % configure_path.abspath
#print 'configure_builddir = %s' % configure_builddir.abspath
return (configure_path, configure_builddir)
def emitter(target, source, env):
(configure_path,
configure_builddir) = __expand_paths(target, source, env)
# Make sure there's a directory to run configure in
if configure_path and not os.path.exists(configure_path.abspath):
print 'Path %s not found' % configure_path
new_targets = [configure_builddir.File('Makefile'),
configure_builddir.File('config.log'),
configure_builddir.File('config.status'),
configure_builddir.File('libtool')]
new_sources = [configure_path.File(env['CONFIGURE']),
configure_path.File('Makefile.in')]
env.Clean(new_targets,configure_builddir)
return (new_targets,
new_sources)
def parms(target, source, env):
"""
Assemble various ./configure parameters.
"""
# We now receive the transformed sources and targets calculated by the emitter.
# configure_cmd is the File node pointing to the configure script
configure_cmd = source[0]
# configure path is the parent folder of the configure script
configure_path = Dir(os.path.dirname(configure_cmd.abspath))
# Calculating the directory in which to run the configure script.
if 'CONFIGURE_BUILDDIR' in env:
configure_builddir = configure_path.Dir(str(env['CONFIGURE_BUILDDIR']))
if 'CONFIGURE_BUILDVARIANT' in env:
if env['CONFIGURE_BUILDVARIANT']:
configure_builddir = configure_builddir.Dir(str(env['CONFIGURE_BUILDVARIANT']))
configure_opts = None
if 'CONFIGURE_FLAGS' in env.Dictionary().keys():
configure_opts = env.subst(env['CONFIGURE_FLAGS'])
if 'CONFIGURE_PREFIX' in env.Dictionary().keys():
if env['CONFIGURE_PREFIX']:
configure_opts.append('--prefix=%s' % env.Dir(env['CONFIGURE_PREFIX']).abspath)
print 'params(target,source,env) returns:'
#print 'configure_builddir = %s' % configure_builddir.abspath
#print 'configure_cmd = %s' % configure_cmd.abspath
print 'configure_opts = %s' % configure_opts
return (configure_builddir.abspath, configure_cmd.abspath, configure_opts)
def message(target, source, env):
"""Return a pretty AutoConfig message."""
(configure_builddir,
configure_cmd,
configure_flags) = parms(target, source, env)
if 'CONFIGURE_COMSTR' in env.Dictionary().keys():
return env.subst(env['CONFIGURE_COMSTR'],
target = target, source = source, raw = 1)
msg = 'cd ' + configure_builddir + ' && CFLAGS="$_CPPINCFLAGS $CCFLAGS $CFLAGS" CXXFLAGS="$_CPPINCFLAGS $CCFLAGS $CXXFLAGS" LDFLAGS="$LINKFLAGS" ' + configure_cmd
if configure_flags is not None:
msg += ' ' + ' '.join(configure_flags)
return env.subst(msg)
def builder(target, source, env):
"""Run configure in a directory."""
(configure_builddir,
configure_cmd,
configure_opts) = parms(target, source, env)
# First create the builddir if it doesn't exist.
env.Execute(Mkdir(configure_builddir))
# Build up the command and its arguments in a list
fullcmd = [ configure_cmd ]
# Environment variables passed on to the script
env['ENV']['CFLAGS'] = env.subst("$_CPPINCFLAGS $CCFLAGS $CFLAGS")
env['ENV']['CXXFLAGS'] = env.subst("$_CPPINCFLAGS $CCFLAGS $CXXFLAGS")
env['ENV']['LDFLAGS'] = env.subst("$LINKFLAGS")
# Parameters behind the configure command
if configure_opts:
fullcmd.extend(configure_opts)
# Capture the configure command's output, unless we're verbose
# ./configure
configure = subprocess.Popen(fullcmd,
cwd = configure_builddir,
bufsize=0,
stdout = subprocess.PIPE,
stderr = subprocess.STDOUT,
env = env['ENV'])
# We need to read the generated output or the buffers will fill up and the process hangs
# Do not replace this with "for line in configure.stdout" due to buffering issues.
# Read more here:
# http://stackoverflow.com/questions/1183643/unbuffered-read-from-process-using-subprocess-in-python/1183654#1183654
line = configure.stdout.readline()
while line:
print line.rstrip()
line = configure.stdout.readline()
return configure.returncode
def generate(env, **kwargs):
env['BUILDERS']['AutoConfig'] = env.Builder(
action = env.Action(builder,message),
emitter = emitter,
source_factory = Dir)
env['CONFIGURE'] = 'configure'
env['CONFIGURE_FLAGS'] = SCons.Util.CLVar('')
env['CONFIGURE_BUILDDIR'] = 'build'
env['CONFIGURE_BUILDVARIANT'] = None
env['CONFIGURE_PREFIX'] = None
def exists(env):
return True
# thirdparty/site_scons/site_tools/Make.py
# Make Builder: Runs make.
#
# Parameters:
# MakePath -- SCons Dir node representing the directory in which to run make. REQUIRED.
# MakeCmd -- The 'make' executable to run.
# Default: make
# MakeEnv -- Dictionary of variables to set in the make execution environment.
# Default: none
# MakeOpts -- Options to pass on the make command line.
# Default: none
# MakeOneThread -- Don't pass any -j option to make.
# Default: False
# MakeTargets -- String of space-seperated targets to pass to make
# Default: ""
import subprocess
from SCons.Script import *
def parms(target, source, env):
"""Assemble various Make parameters."""
if 'MakePath' in env.Dictionary().keys():
make_path = env.subst(str(env['MakePath']))
else:
make_path = os.path.dirname(source[0].abspath)
make_cmd = 'make'
if 'MakeCmd' in env.Dictionary().keys():
make_cmd = env.subst(env['MakeCmd'])
elif 'MAKE' in env.Dictionary().keys():
make_cmd = env.subst(env['MAKE'])
make_env = None
if 'CROSS_BUILD' in env.Dictionary().keys():
make_env = env['CROSS_ENV']
if 'MakeEnv' in env.Dictionary().keys():
if make_env == None:
make_env = {}
else:
# We're appending to an existing dictionary, so create a copy
# instead of appending to the original env['CROSS_ENV']
make_env = env['CROSS_ENV'][:]
for (k,v) in env['MakeEnv'].items():
make_env[k] = v
make_opts = None
if 'MakeOpts' in env.Dictionary().keys():
make_opts = env.subst(env['MakeOpts'])
make_jobs = GetOption('num_jobs')
if 'MakeOneThread' in env.Dictionary().keys() and env['MakeOneThread']:
make_jobs = 1
make_targets = None
if 'MakeTargets' in env.Dictionary().keys():
make_targets = env.subst(env['MakeTargets'])
return (make_path, make_env, make_targets, make_cmd, make_jobs, make_opts)
def message(target, source, env):
"""Return a pretty Make message"""
(make_path,
make_env,
make_targets,
make_cmd,
make_jobs,
make_opts) = parms(target, source, env)
myenv = env.Clone()
# Want to use MakeTargets in the MAKECOMSTR, but make it pretty first.
if 'MakeTargets' in myenv.Dictionary().keys():
myenv['MakeTargets'] += ' '
else:
myenv['MakeTargets'] = ''
if 'MAKECOMSTR' in myenv.Dictionary().keys():
return myenv.subst(myenv['MAKECOMSTR'],
target = target, source = source, raw = 1)
msg = ''
if make_env != None:
for k, v in make_env.iteritems():
msg += ' ' + k + '=' + v
msg += ' ' + make_cmd
if make_path:
msg += ' -C ' + make_path
if make_jobs > 1:
msg += ' -j %d' % make_jobs
if make_opts != None:
msg += ' ' + ' '.join(make_opts)
if make_targets != None:
msg += ' ' + make_targets
return msg
def builder(target, source, env):
"""Run make in a directory."""
(make_path,
make_env,
make_targets,
make_cmd,
make_jobs,
make_opts) = parms(target, source, env)
# Make sure there's a directory to run make in
if make_path and not os.path.exists(make_path):
print 'Path %s not found' % make_path
# Build up the command and its arguments in a list
fullcmd = [ make_cmd ]
if make_jobs > 1:
fullcmd += [ '-j', str(make_jobs) ]
if make_opts:
fullcmd += make_opts
if make_targets:
fullcmd += make_targets.split()
# Capture the make command's output, unless we're verbose
real_stdout = subprocess.PIPE
if 'MAKECOMSTR' not in env.Dictionary().keys():
real_stdout = None
# Make!
make = subprocess.Popen(fullcmd,
cwd = make_path,
stdout = real_stdout,
env = make_env)
# Some subprocesses don't terminate unless we communicate with them
output = make.communicate()[0]
return make.returncode
def generate(env, **kwargs):
env['BUILDERS']['Make'] = env.Builder(
action = env.Action(builder, message))
def exists(env):
if env.WhereIs(env.subst('$MAKE')) != None:
return True
return False
thirdparty/gtest/SConscript
####################################################################################
# Build file for GoogleTest
#
# This file is supposed to be invoked as a subscript from the toplevel SConstruct
# file in 'thirdparty'.
####################################################################################
Import(['env', 'target'])
configureFlags = ['--disable-shared','--enable-static']
installDir = Dir('../target/%s' % target)
# Google Test: ./configure
google_test_makefile = env.AutoConfig(CONFIGURE_FLAGS = configureFlags,
CONFIGURE_BUILDVARIANT = target,
CONFIGURE_PREFIX = installDir.abspath)
# Google Test: make
google_test_library = env.Make([installDir.File('lib/libgtest.la'), installDir.File('lib/libgtest_main.la')],
google_test_makefile,
MakePath = Dir('build/%s' % target),
MakeTargets = 'install')
env.Clean(google_test_library, installDir.File('bin/gtest-config'))
env.Clean(google_test_library, installDir.Dir('include/gtest'))
env.Clean(google_test_library, env.Glob(installDir.File('lib/libgtest*').abspath))
env.Clean(google_test_library, installDir.File('share/aclocal/gtest.m4'))
#Alias('gtest', google_test_library)
Alias('gtest-%s' % target , google_test_library)
Alias(target,'gtest-%s' % target)
# thirdparty/SConstruct
################################################################################################
# Build file for all Third-Party code
#
# Every 3rdparty library in use is mapped onto a Mercurial subrepository.
################################################################################################
#-----------------------------------------------------------------------------------------------
# Load additional build tools in any of the environments.
# Since adding tools to the environment can't be done in site_init.py, it has to be done here
#-----------------------------------------------------------------------------------------------
base_environment.Tool('AutoConfig')
base_environment.Tool('Make')
debug_environment.Tool('AutoConfig')
debug_environment.Tool('Make')
production_environment.Tool('AutoConfig')
production_environment.Tool('Make')
#-----------------------------------------------------------------------------------------------
# Invoke the SConscript file of every 3rdparty library using every of the environments
#-----------------------------------------------------------------------------------------------
# Google Test
SConscript('gtest/SConscript', exports = { 'env' : debug_environment, 'target' : 'debug' })
SConscript('gtest/SConscript', exports = { 'env' : production_environment, 'target' : 'production' })
# thirdparty/site_scons/site_init.py
import platform
from SCons.Script import *
EnsurePythonVersion(2, 6)
EnsureSConsVersion(1,2,0)
################################################################################################
# Support for building with system packages or in Pylabs Sandbox
################################################################################################
AddOption('--in-pylabs',
dest='in_pylabs',
action='store_true',
help='Build against the Pylabs sandbox')
if GetOption('in_pylabs'):
base_environment = Environment(platform = 'pylabs')
base_environment.Tool('pylabslink')
else:
base_environment = Environment()
################################################################################################
# Definition of basic build environments
################################################################################################
# SCons definitions
# C/C++ flags: CCFLAGS
# C flags: CFLAGS
# C++ flags: CXXFLAGS
#
#-----------------------------------------------------------------------------------------------
# The base_environment contains settings that are applicable to every situation.
#-----------------------------------------------------------------------------------------------
# Specific Preprocessor settings
# base_environment.Append(CPPFLAGS = [])
# Overal custom C/C++ settings
base_environment.Append(CCFLAGS = ['-fPIC'])
# Specific C settings
# base_environment.Append(CFLAGS = [])
# Specific C++ settings
# base_environment.Append(CXXFLAGS = [])
# Architecture specific settings
ARCHFLAGS = {'ia32': [],
'x86_64': ['-DSSE_16']}
base_environment.Replace(ARCH = {'i686':'ia32',
'x86_64':'x86_64'}.get(platform.machine(),'ia32'))
base_environment.Append(CCFLAGS = ARCHFLAGS[base_environment['ARCH']])
# Path to 3rd party libraries
base_environment.Append(AMPLI_THIRD_PARTY = Dir('.').abspath)
#-----------------------------------------------------------------------------------------------
# The debug_environment contains settings that are applicable for debug building and testing.
#-----------------------------------------------------------------------------------------------
debug_environment = base_environment.Clone()
# Specific Preprocessor settings
# debug_environment.Append(CPPFLAGS = [])
# Overal custom C/C++ settings
# debug_environment.Append(CCFLAGS = ['-fPIC'])
# Specific C settings
# debug_environment.Append(CFLAGS = [])
# Specific C++ settings
# debug_environment.Append(CXXFLAGS = [])
#-----------------------------------------------------------------------------------------------
# The production_environment contains settings that are applicable for production building and testing.
#-----------------------------------------------------------------------------------------------
production_environment = base_environment.Clone()
# Specific Preprocessor settings
# production_environment.Append(CPPFLAGS = [])
# Overal custom C/C++ settings
# production_environment.Append(CCFLAGS = ['-fPIC'])
# Specific C settings
production_environment.Append(CFLAGS = [ '-DNDEBUG' ])
# Specific C++ settings
# production_environment.Append(CXXFLAGS = [])
#-----------------------------------------------------------------------------------------------
# The above environments can be used further on in any SConstruct or SConscript file
#-----------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------
# Default targets and the dependencies between them.
#-----------------------------------------------------------------------------------------------
Alias('production',[])
Alias('test-production','production')
Alias('debug',[])
Alias('test','debug')
Alias('coverage',[])
Alias('test-coverage',[])
Alias('all',['test','test-production','test-coverage'])
Default('test')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment