2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-19 16:32:16 +00:00

Add SCons-based build system.

This commit is contained in:
Stefan Seefeld
2015-12-23 13:12:04 -05:00
parent 5233f45da4
commit 57cd933240
14 changed files with 1005 additions and 0 deletions

34
SConscript Normal file
View File

@@ -0,0 +1,34 @@
# -*- python -*-
Import("env")
env.Append(CPPPATH = "#/include",CPPDEFINES = ["BOOST_ALL_NO_LIB=1"])
env.AppendUnique(CPPDEFINES = ["${LINK_DYNAMIC and 'BOOST_PYTHON_DYN_LINK=1' or []}"])
for variant in env["variant"]:
env["current_variant"] = variant
env.SetProperty(profile = False)
if variant == "release":
env.SetProperty(optimize = "speed", debug = False)
elif variant == "debug":
env.SetProperty(optimize = "no", debug = True)
elif variant == "profile":
env.SetProperty(optimize = "speed", profile = True, debug = True)
for linking in env["link"]:
env["linking"] = linking
if linking == "dynamic":
env["LINK_DYNAMIC"] = True
else:
env["LINK_DYNAMIC"] = False
for threading in env["threading"]:
env["current_threading"] = threading
env.SetProperty(threading = threading)
variant_dir=env.subst("$BOOST_CURRENT_VARIANT_DIR")
env.SConscript("src/SConscript", variant_dir=variant_dir + '/src',
exports = { "env" : env.Clone(BOOST_LIB = 'python') })
if GetOption("test"):
test_env = env.Clone(BOOST_LIB = 'python', BOOST_TEST = True)
test_env.BoostUseLib('python')
env.SConscript("test/SConscript", variant_dir=variant_dir + '/test',
exports = { "env" : test_env })

82
SConstruct Normal file
View File

@@ -0,0 +1,82 @@
# -*- python -*-
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import SCons.Script.Main
import config
import config.ui
import platform
import os
#
# We try to mimic the typical autotools-workflow.
#
# * In a 'configure' step all the essential build parameters are established
# (either by explicit command-line arguments or from configure checks)
# * A subsequent build step can then simply read the cached variables, so
# users don't have to memorize and re-issue the arguments on each subsequent
# invocation, and neither do the config checks need to be re-run.
#
# The essential part here is to define a 'config' target, which removes any
# caches that may still be lingering around, then runs the checks.
if 'config' in COMMAND_LINE_TARGETS:
# Clear the cache
try: os.remove('bin.SCons/config.py')
except: pass
if not os.path.exists('bin.SCons/'):
os.mkdir('bin.SCons/')
vars = Variables('bin.SCons/config.py', ARGUMENTS)
config.add_options(vars)
arch = ARGUMENTS.get('arch', platform.machine())
env = Environment(toolpath=['config/tools'],
tools=['default', 'libs', 'tests'],
variables=vars,
TARGET_ARCH=arch)
Help(config.ui.help(vars, env) + """
Variables are saved in bin.SCons/config.py and persist between scons invocations.
""")
if GetOption('help'):
Return()
build_dir = config.prepare_build_dir(env)
config_log = '{}/config.log'.format(build_dir)
# configure
SConsignFile('{}/.sconsign'.format(build_dir))
#env.Decider('MD5-timestamp')
env.Decider('timestamp-newer')
checks = config.get_checks()
if 'config' in COMMAND_LINE_TARGETS:
conf=env.Configure(custom_tests=checks, log_file=config_log, conf_dir=build_dir)
if False in (getattr(conf, c)() for c in checks):
Exit(1)
env = conf.Finish()
vars.Save('bin.SCons/config.py', env)
if not os.path.exists(config_log):
print('Please run `scons config` first. (See `scons -h` for available options.)')
Exit(1)
if not GetOption('verbose'):
config.ui.pretty_output(env)
# build
env['BPL_VERSION'] = '1.61'
for e in config.variants(env):
variant_dir=e.subst("$BOOST_CURRENT_VARIANT_DIR")
e.SConscript('src/SConscript', variant_dir=variant_dir + '/src',
exports = { 'env' : e.Clone(BOOST_LIB = 'python') })
if 'test' in COMMAND_LINE_TARGETS:
test_env = e.Clone(BOOST_LIB = 'python', BOOST_TEST = True)
test_env.BoostUseLib('python')
e.SConscript('test/SConscript', variant_dir=variant_dir + '/test',
exports = { 'env' : test_env })

116
config/__init__.py Normal file
View File

@@ -0,0 +1,116 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from SCons.Variables import *
from SCons.Script import AddOption
from collections import OrderedDict
import platform
from . import ui
from . import python
from . import boost
def add_options(vars):
ui.add_option('-V', '--verbose', dest='verbose', action='store_true', help='verbose mode: print full commands.')
python.add_options(vars)
boost.add_options(vars)
vars.Add('CPPPATH', converter=lambda v:v.split())
vars.Add('CCFLAGS', converter=lambda v:v.split())
vars.Add('LIBPATH', converter=lambda v:v.split())
vars.Add('LIBS', converter=lambda v:v.split())
vars.Add('PYTHON')
vars.Add('PYTHONLIBS')
ui.add_variable(vars, ("arch", "target architeture", platform.machine()))
ui.add_variable(vars, ("toolchain", "toolchain to use", 'gcc'))
ui.add_variable(vars, ListVariable("variant", "Build configuration", "release", ["release", "debug", "profile"]))
ui.add_variable(vars, ListVariable("link", "Library linking", "dynamic", ["static", "dynamic"]))
ui.add_variable(vars, ListVariable("threading", "Multi-threading support", "multi", ["single", "multi"]))
ui.add_variable(vars, EnumVariable("layout", "Layout of library names and header locations", "versioned", ["versioned", "system"]))
ui.add_variable(vars, PathVariable("stagedir", "If --stage is passed install only compiled library files in this location", "stage", PathVariable.PathAccept))
ui.add_variable(vars, PathVariable("prefix", "Install prefix", "/usr/local", PathVariable.PathAccept))
def get_checks():
checks = OrderedDict()
checks['python'] = python.check
checks['boost'] = boost.check
return checks
def set_property(env, **kw):
from toolchains.gcc import features as gcc_features
from toolchains.msvc import features as msvc_features
if 'gcc' in env['TOOLS']: features = gcc_features
elif 'msvc' in env['TOOLS']: features = msvc_features
else: raise Error('unknown toolchain')
features.init_once(env)
for (prop,value) in kw.items():
getattr(features, prop, lambda x, y : None)(env, value)
env[prop.upper()] = value
def boost_suffix(env):
suffix = str()
if env["layout"] == "versioned":
if "gcc" in env["TOOLS"]:
suffix += "-gcc" + "".join(env["CCVERSION"].split(".")[0:2])
if env["THREADING"] == "multi":
suffix += "-mt"
if env["DEBUG"]:
suffix += "-d"
if env["layout"] == "versioned":
suffix += "-" + "_".join(env["BPL_VERSION"].split("."))
return suffix
def prepare_build_dir(env):
vars = {}
env["boost_suffix"] = boost_suffix
build_dir="bin.SCons"
if "gcc" in env["TOOLS"]:
build_dir+="/gcc-%s"%env["CCVERSION"]
vars['CXXFLAGS'] = ['-ftemplate-depth-128', '-Wall']
elif "msvc" in env["TOOLS"]:
build_dir+="/msvc-%s"%env["MSVS_VERSION"]
vars['BOOST_BUILD_DIR'] = build_dir
vars['BOOST_SUFFIX'] = "${boost_suffix(__env__)}"
env.Replace(**vars)
return build_dir
def variants(env):
env.Append(CPPPATH = "#/include", CPPDEFINES = ["BOOST_ALL_NO_LIB=1"])
set_property(env, architecture = env['TARGET_ARCH'])
for variant in env["variant"]:
e = env.Clone()
e["current_variant"] = variant
set_property(env, profile = False)
if variant == "release":
set_property(e, optimize = "speed", debug = False)
elif variant == "debug":
set_property(e, optimize = "no", debug = True)
elif variant == "profile":
set_property(e, optimize = "speed", profile = True, debug = True)
for linking in env["link"]:
e["linking"] = linking
if linking == "dynamic":
e["LINK_DYNAMIC"] = True
else:
e["LINK_DYNAMIC"] = False
for threading in e["threading"]:
e["current_threading"] = threading
set_property(e, threading = threading)
yield e

40
config/boost.py Normal file
View File

@@ -0,0 +1,40 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from . import ui
import os
def add_options(vars):
ui.add_option("--boost-prefix", dest="boost_prefix", type="string", nargs=1, action="store",
metavar="DIR", default=os.environ.get("BOOST_DIR"),
help="prefix for Boost libraries; should have 'include' and 'lib' subdirectories, 'boost' and 'stage\\lib' subdirectories on Windows")
ui.add_option("--boost-include", dest="boost_include", type="string", nargs=1, action="store",
metavar="DIR", help="location of Boost header files")
def check(context):
boost_source_file = r"#include <boost/config.hpp>"
context.Message('Checking for Boost...')
boost_prefix = context.env.GetOption('boost_prefix')
boost_include = context.env.GetOption('boost_include')
incpath=None
if boost_include:
incpath=boost_include
elif boost_prefix:
incpath=boost_prefix
if incpath:
context.env.AppendUnique(CPPPATH=[incpath])
if not context.TryCompile(boost_source_file, '.cpp'):
context.Result(0)
return False
context.Result(1)
return True

98
config/python.py Normal file
View File

@@ -0,0 +1,98 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from . import ui
def add_options(vars):
ui.add_option('--python', help='the python executable')
def check(context):
python_source_file = r"""
// If defined, enforces linking againg PythonXXd.lib, which
// is usually not included in Python environments.
#undef _DEBUG
#include "Python.h"
int main()
{
Py_Initialize();
Py_Finalize();
return 0;
}
"""
import platform
import subprocess
import re, os
def check_python(cmd):
return subprocess.check_output([python, '-c', cmd]).strip()
def check_sysconfig(cmd):
r = check_python('import distutils.sysconfig as c; print(c.%s)'%cmd)
return r if r != 'None' else ''
context.Message('Checking for Python...')
python = context.env.GetOption('python') or 'python'
context.env['PYTHON'] = python
incpath = check_sysconfig('get_python_inc()')
context.env.AppendUnique(CPPPATH=[incpath])
if platform.system() == 'Windows':
version = check_python('import sys; print("%d%d"%sys.version_info[0:2])')
prefix = check_python('import sys; print(sys.prefix)')
libfile = os.path.join(prefix, 'libs', 'python%s.lib'%version)
libpath = os.path.join(prefix, 'libs')
lib = 'python%s'%version
context.env.AppendUnique(LIBS=[lib])
else:
libpath = check_sysconfig('get_config_var("LIBDIR")')
libfile = check_sysconfig('get_config_var("LIBRARY")')
match = re.search('(python.*)\.(a|so|dylib)', libfile)
lib = None
if match:
lib = match.group(1)
context.env.AppendUnique(PYTHONLIBS=[lib])
if match.group(2) == 'a':
flags = check_sysconfig('get_config_var("LINKFORSHARED")')
if flags is not None:
context.env.AppendUnique(LINKFLAGS=flags.split())
context.env.AppendUnique(LIBPATH=[libpath])
oldlibs = context.AppendLIBS([lib])
flags = check_sysconfig('get_config_var("MODLIBS")')
flags += ' ' + check_sysconfig('get_config_var("SHLIBS")')
flags = [f[2:] for f in flags.strip().split() if f.startswith('-l')]
if flags:
context.AppendLIBS([flags])
result = context.TryLink(python_source_file,'.cpp')
if not result and context.env['PLATFORM'] == 'darwin':
# Sometimes we need some extra stuff on Mac OS
frameworkDir = libpath # search up the libDir tree for the proper home for frameworks
while frameworkDir and frameworkDir != "/":
frameworkDir, d2 = os.path.split(frameworkDir)
if d2 == "Python.framework":
if not "Python" in os.listdir(os.path.join(frameworkDir, d2)):
context.Result(0)
print((
"Expected to find Python in framework directory %s, but it isn't there"
% frameworkDir))
return False
break
context.env.AppendUnique(LINKFLAGS="-F%s" % frameworkDir)
result = context.TryLink(python_source_file,'.cpp')
if not result:
context.Result(0)
print("Cannot link program with Python.")
return False
if context.env['PLATFORM'] == 'darwin':
context.env['LDMODULESUFFIX'] = '.so'
context.Result(1)
context.SetLIBS(oldlibs)
context.env.AppendUnique(PYTHONLIBS=[lib] + flags)
return True

View File

@@ -0,0 +1,18 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import traceback
def append_feature_flag(env, **kw):
stack = traceback.extract_stack(limit = 3)
feature = stack[0][2].upper()
for (key, val) in kw.items():
feature_var = feature + "_" + key
env.AppendUnique(**{ key : "$" + feature_var })
env[feature_var] = val

55
config/toolchains/gcc.py Normal file
View File

@@ -0,0 +1,55 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from . import append_feature_flag
class features:
@classmethod
def init_once(cls, env):
pass
@staticmethod
def architecture(env, arch):
if arch:
flag = {'x86' : '-m32',
'x86_64' : '-m64',}.get(arch)
if flag:
append_feature_flag(env, CCFLAGS = flag)
@staticmethod
def optimize(env, optimize):
if not optimize or optimize == "no":
append_feature_flag(env, CCFLAGS = "-O0 -fno-inline")
elif optimize == "speed":
append_feature_flag(env, CCFLAGS = "-O3 -finline-functions -Wno-inline")
elif optimize == "space":
append_feature_flag(env, CCFLAGS = "-Os")
else:
append_feature_flag(env, CCFLAGS = "")
@staticmethod
def profile(env, profile):
if profile:
append_feature_flag(env, CCFLAGS = "-pg", LINKFLAGS = "-pg")
else:
append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "")
@staticmethod
def threading(env, threading):
if threading == "multi":
append_feature_flag(env, CCFLAGS = "-pthread", LINKFLAGS = "-pthread")
else:
append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "")
@staticmethod
def debug(env, debug):
if debug:
append_feature_flag(env, CCFLAGS = "-g", CPPDEFINES = [])
else:
append_feature_flag(env, CCFLAGS = "", CPPDEFINES = "NDEBUG")

57
config/toolchains/msvc.py Normal file
View File

@@ -0,0 +1,57 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from . import append_feature_flag
class features:
@classmethod
def init_once(cls, env):
env.AppendUnique(CCFLAGS = ['-TP', '/Z7', '/W3' ,'/GR', '/MDd', '/Zc:forScope', '/Zc:wchar_t', '/wd4675', '/EHs'])
env.AppendUnique(LINKFLAGS = ['/subsystem:console'])
@staticmethod
def architecture(env, arch):
if arch:
flag = {'x86' : '/MACHINE:X32',
'x86_64' : '/MACHINE:X64',}.get(arch)
if flag:
append_feature_flag(env, LINKFLAGS = flag)
@staticmethod
def optimize(env, optimize):
#if not optimize or optimize == "no":
# append_feature_flag(env, CCFLAGS = "-O0 -fno-inline")
#elif optimize == "speed":
# append_feature_flag(env, CCFLAGS = "-O3 -finline-functions -Wno-inline")
#elif optimize == "space":
# append_feature_flag(env, CCFLAGS = "-Os")
#else:
append_feature_flag(env, CCFLAGS = "")
@staticmethod
def profile(env, profile):
#if profile:
# append_feature_flag(env, CCFLAGS = "-pg", LINKFLAGS = "-pg")
#else:
append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "")
@staticmethod
def threading(env, threading):
#if threading == "multi":
# append_feature_flag(env, CCFLAGS = "/MT")
#else:
# append_feature_flag(env, CCFLAGS = "", LINKFLAGS = "")
pass
@staticmethod
def debug(env, debug):
#if debug:
# append_feature_flag(env, CCFLAGS = "-g", CPPDEFINES = [])
#else:
append_feature_flag(env, CCFLAGS = "", CPPDEFINES = "NDEBUG")

44
config/tools/clang.py Normal file
View File

@@ -0,0 +1,44 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# Based on SCons/Tool/gcc.py
import os
import re
import subprocess
import SCons.Util
import SCons.Tool.cc
compilers = ['clang']
def generate(env):
"""Add Builders and construction variables for clang to an Environment."""
SCons.Tool.cc.generate(env)
env['CC'] = env.Detect(compilers) or 'clang'
if env['PLATFORM'] in ['cygwin', 'win32']:
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
else:
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS -fPIC')
# determine compiler version
if env['CC']:
#pipe = SCons.Action._subproc(env, [env['CC'], '-dumpversion'],
pipe = SCons.Action._subproc(env, [env['CC'], '--version'],
stdin = 'devnull',
stderr = 'devnull',
stdout = subprocess.PIPE)
if pipe.wait() != 0: return
# clang -dumpversion is of no use
line = pipe.stdout.readline()
match = re.search(r'clang +version +([0-9]+(?:\.[0-9]+)+)', line)
if match:
env['CCVERSION'] = match.group(1)
def exists(env):
return env.Detect(compilers)

84
config/tools/libs.py Normal file
View File

@@ -0,0 +1,84 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import distutils.sysconfig
from SCons.Script import AddOption, COMMAND_LINE_TARGETS, BUILD_TARGETS
def BoostLibrary(env, lib, sources, make_aliases = True, **kw):
if env["LINK_DYNAMIC"]:
lib_node = env.SharedLibrary("boost_" + lib + env["BOOST_SUFFIX"], sources, **kw)
else:
lib_node = env.StaticLibrary("boost_" + lib + env["BOOST_SUFFIX"], sources, **kw)
if make_aliases:
if env.GetOption("stage"):
env.Alias(lib, env.Install(env.Dir("$stagedir", "#"), lib_node))
env.Default(env.Alias(lib, lib_node))
if env.GetOption("install"):
env.Alias(lib, env.Install("$prefix/lib", lib_node))
return lib_node
def BoostUseLib(env, lib):
env.AppendUnique(
LIBPATH = [env.Dir("$BOOST_CURRENT_VARIANT_DIR/src")],
LIBS = ["boost_" + lib + env["BOOST_SUFFIX"]]
)
if env.get("BOOST_TEST"):
env.AppendUnique(RPATH = [env.Dir("$BOOST_CURRENT_VARIANT_DIR/src")])
def PythonExtension(env, lib, sources, **kw):
if env["LINK_DYNAMIC"]:
#env.AppendUnique(CPPDEFINES = ['BOOST_PYTHON_DYN_LINK=1'])
ext = env.SharedLibrary(lib, sources, SHLIBPREFIX='', SHLIBSUFFIX=distutils.sysconfig.get_config_var("SO"), **kw)
#env.Alias(lib, ext)
return ext
def boost_copy_func(dest, source, env):
import os, stat, shutil
if os.path.isdir(source):
if os.path.exists(dest):
if not os.path.isdir(dest):
raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))
else:
os.makedirs(dest)
for file in os.listdir(source):
if file == ".svn": continue
boost_copy_func(os.path.join(dest, file), os.path.join(source, file), env)
else:
shutil.copy2(source, dest)
st = os.stat(source)
os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
return 0
def exists(env):
return True
def generate(env):
env.AddMethod(BoostLibrary)
env.AddMethod(BoostUseLib)
env.AddMethod(PythonExtension)
env.Replace(
INSTALL = boost_copy_func,
BOOST_CURRENT_VARIANT_DIR = "$BOOST_BUILD_DIR/$current_variant/$linking/threading-$current_threading"
)
AddOption('--stage', dest='stage', action="store_true")
AddOption('--install', dest='install', action="store_true")
#if env.GetOption("install"):
# BUILD_TARGETS.extend(env.Alias("install-headers"))

105
config/tools/tests.py Normal file
View File

@@ -0,0 +1,105 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from SCons.Script import AddOption, Flatten
from SCons.Script import Builder
from SCons.Action import Action
from subprocess import check_output, STDOUT, CalledProcessError
import sys
def BoostCompileTest(env, test, source = None, **kw):
obj = env.Object(test, source if source is not None else test + '.cpp')
return obj
def BoostRun(env, prog, target, command = '$SOURCE'):
def call(target, source, env=env):
cmd = env.subst(command, target=target, source=source)
result_file = env.subst('$TARGET', target=target)
output=''
try:
output=check_output(cmd, stderr=STDOUT, shell=True)
success=True
except CalledProcessError as e:
output=e.output
success=False
with open(result_file, 'w+') as result:
result.write('Result: {}\n'.format(success and 'pass' or 'fail'))
result.write('Output: {}\n'.format(output))
if sys.stdout.isatty():
env['RESULT']=success and '\033[92mPASS\033[0m' or '\033[91mFAIL\033[0m'
else:
env['RESULT']=success and 'PASS' or 'FAIL'
testcomstr = env.get('TESTCOMSTR')
if testcomstr:
run = env.Command(target, prog, Action(call, cmdstr=testcomstr))
else:
run = env.Command(target, prog, Action(call, cmdstr=command))
env.AddPostAction(target, Action('@echo $RESULT'))
return run
def BoostRunPythonScript(env, script):
return env.BoostRun(env.File(script), script.replace('.py', '.result'), '"${PYTHON}" $SOURCE')
def BoostRunTest(env, test, source = None, command = '$SOURCE', command_sources = [], **kw):
test_prog = env.Program(test, (source is None) and (test + ".cpp") or source, **kw)
command += '> $TARGET'
run = env.BoostRun([test_prog, command_sources], test + '.result', command)
return run
def BoostRunTests(env, tests, **kw):
run = []
for test in Flatten(tests):
run += env.BoostRunTest(test, **kw)
return run
def BoostCompileTests(env, tests, **kw):
comp = []
for test in Flatten(tests):
comp += env.BoostCompileTest(test, **kw)
return comp
def BoostTestSummary(env, tests, **kw):
def print_summary(target, source, **kw):
results = tests
failures = [r for r in results
if r.get_path().endswith('.result') and not 'Result: pass' in r.get_contents()]
print('%s tests; %s pass; %s fails'%(len(results), len(results)-len(failures), len(failures)))
for f in failures:
print('%s\n%s'%(f.get_path(), f.get_contents()))
testsumcomstr = env.get('TESTSUMCOMSTR')
if testsumcomstr:
run = env.Command('summary', tests, Action(print_summary, cmdstr=testsumcomstr))
else:
run = env.Command('summary', tests, print_summary, cmdstr='')
def exists(env):
return True
def generate(env):
AddOption('--test', dest='test', action="store_true")
env.AddMethod(BoostCompileTest)
env.AddMethod(BoostRun)
env.AddMethod(BoostRunPythonScript)
env.AddMethod(BoostRunTest)
env.AddMethod(BoostRunTests)
env.AddMethod(BoostCompileTests)
env.AddMethod(BoostTestSummary)

96
config/ui.py Normal file
View File

@@ -0,0 +1,96 @@
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
from SCons.Script import AddOption
import sys
variables=[] # remember 'public' variables
options=[]
def add_option(*args, **kwds):
"""Capture the help messages so we can produce a helpful usage text."""
options.append('{:25} {}'.format(', '.join(args), kwds.get('help', '')))
AddOption(*args, **kwds)
def add_variable(vars, var):
variables.append(var[0])
vars.Add(var)
def options_help(env):
return '\n '.join(options)
def variables_help(vars, env):
"""This is cloned from SCons' Variables.GenerateHelpText, to only report 'public' variables."""
opts = [o for o in vars.options if o.key in variables]
def format(opt):
if opt.key in env:
actual = env.subst('${%s}' % opt.key)
else:
actual = None
return vars.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
text = ''.join([f for f in map(format, opts) if f])
lines = [' %s'%l for l in text.split('\n')] # Add some indentation
return '\n'.join(lines)
def help(vars, env):
return """Usage: scons [--option...] [variable=value...] [target...]
available options:
{}
available variables:
{}
""".format(options_help(env), variables_help(vars, env))
def pretty_output(env):
colors = {}
colors['red'] = '\033[31m'
colors['green'] = '\033[32m'
colors['blue'] = '\033[34m'
colors['yellow'] = '\033[93m'
colors['Red'] = '\033[91m'
colors['Green'] = '\033[92m'
colors['Blue'] = '\033[94m'
colors['Purple'] = '\033[95m'
colors['Cyan'] = '\033[96m'
colors['end'] = '\033[0m'
#If the output is not a terminal, remove the colors
if not sys.stdout.isatty():
for key, value in colors.iteritems():
colors[key] = ''
compile_source_message = '{green}Compiling $TARGET{end}'.format(**colors)
compile_shared_source_message = '{green}Compiling $TARGET{end}'.format(**colors)
link_program_message = '{blue}Linking $TARGET{end}'.format(**colors)
link_library_message = '{blue}Linking $TARGET{end}'.format(**colors)
ranlib_library_message = '{blue}Ranlib $TARGET{end}'.format(**colors)
link_shared_library_message = '{blue}Linking $TARGET{end}'.format(**colors)
test_message = '{blue}Testing $SOURCE{end}'.format(**colors)
testsum_message = '{Blue}Test Summary{end}'.format(**colors)
env.Replace(CXXCOMSTR = compile_source_message,
CCCOMSTR = compile_source_message,
SHCCCOMSTR = compile_shared_source_message,
SHCXXCOMSTR = compile_shared_source_message,
ARCOMSTR = link_library_message,
RANLIBCOMSTR = ranlib_library_message,
SHLINKCOMSTR = link_shared_library_message,
LINKCOMSTR = link_program_message,
TESTCOMSTR = test_message,
TESTSUMCOMSTR = testsum_message)

44
src/SConscript Normal file
View File

@@ -0,0 +1,44 @@
# -*- python -*-
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
Import('env')
env.AppendUnique(CPPDEFINES = ["${LINK_DYNAMIC and 'BOOST_PYTHON_DYN_LINK=1' or ''}"])
env.AppendUnique(CPPDEFINES = ['BOOST_PYTHON_SOURCE'])
env.BoostLibrary(
'python',
['numeric.cpp',
'list.cpp',
'long.cpp',
'dict.cpp',
'tuple.cpp',
'str.cpp',
'slice.cpp',
'converter/from_python.cpp',
'converter/registry.cpp',
'converter/type_id.cpp',
'object/enum.cpp',
'object/class.cpp',
'object/function.cpp',
'object/inheritance.cpp',
'object/life_support.cpp',
'object/pickle_support.cpp',
'errors.cpp',
'module.cpp',
'converter/builtin_converters.cpp',
'converter/arg_to_python_base.cpp',
'object/iterator.cpp',
'object/stl_iterator.cpp',
'object_protocol.cpp',
'object_operators.cpp',
'wrapper.cpp',
'import.cpp',
'exec.cpp',
'object/function_doc_signature.cpp'])

132
test/SConscript Normal file
View File

@@ -0,0 +1,132 @@
# -*- python -*-
#
# Copyright (c) 2016 Stefan Seefeld
# All rights reserved.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
Import('env')
# libs needed for embedding
ELIBS=env['LIBS'] + env['PYTHONLIBS']
def BPLTest(env, name, sources = None, script = None):
run = env.BoostRunPythonScript(name + '.py')
if sources:
for source in sources:
Depends(run,
env.PythonExtension(source != name and source or (source + '_ext'), source + '.cpp')
)
else:
Depends(run, env.PythonExtension(name + '_ext', name + '.cpp'))
return run
env.AddMethod(BPLTest)
env.AppendENVPath('PYTHONPATH', Dir('.').path)
tests=[]
tests+=env.BPLTest('crossmod_exception', ['crossmod_exception_a', 'crossmod_exception_b'])
for test in ['injected',
'properties',
'return_arg',
'staticmethod',
'shared_ptr',
'enable_shared_from_this',
'andreas_beyer',
'polymorphism',
'polymorphism2',
'wrapper_held_type',
'polymorphism2_auto_ptr',
'auto_ptr',
'minimal',
'args',
'raw_ctor',
#'numpy',
'enum',
'exception_translator']:
tests+=env.BPLTest(test)
tests+=env.BPLTest('test_cltree', ['cltree'])
tests+=env.BPLTest('newtest', ['m1', 'm2'])
tests+=env.BPLTest('const_argument')
tests+=env.BPLTest('keywords_test', ['keywords'])
Depends(
env.BoostRunPythonScript('test_builtin_converters.py'),
env.PythonExtension('builtin_converters_ext', 'test_builtin_converters.cpp')
)
for test in ['test_pointer_adoption',
'operators',
'operators_wrapper',
'callbacks',
'defaults',
'object',
'list',
'long',
'dict',
'tuple',
'str',
'slice',
'virtual_functions',
'back_reference',
'implicit',
'data_members',
'ben_scott1',
'bienstman1',
'bienstman2',
'bienstman3',
'multi_arg_constructor']:
tests+=env.BPLTest(test)
tests+=env.BPLTest('iterator', ['iterator', 'input_iterator'])
tests+=env.BPLTest('stl_iterator')
tests+=env.BPLTest('extract')
tests+=env.BPLTest('crossmod_opaque', ['crossmod_opaque_a', 'crossmod_opaque_b'])
for test in ['opaque',
'voidptr',
'pickle1',
'pickle2',
'pickle3',
'pickle4',
'nested',
'docstring',
'pytype_function',
'vector_indexing_suite',
'pointer_vector']:
tests+=env.BPLTest(test)
Depends(
env.BoostRunPythonScript('map_indexing_suite.py'),
env.PythonExtension('map_indexing_suite_ext', [
'map_indexing_suite.cpp',
'int_map_indexing_suite.cpp',
'a_map_indexing_suite.cpp'])
)
tests+=env.BoostRunTest('import_', 'import_.cpp', '${SOURCES[0]} ${SOURCES[1]}', 'import_.py', LIBS=ELIBS)
tests+=env.BoostCompileTest('indirect_traits_test')
tests+=env.BoostRunTests(['destroy_test',
'pointer_type_id_test',
'bases',
'if_else',
'pointee',
'result'], LIBS=ELIBS)
tests+=env.BoostCompileTests(['string_literal',
'borrowed',
'object_manager',
'copy_ctor_mutates_rhs'])
tests+=env.BoostRunTest('upcast', LIBS=ELIBS)
tests+=env.BoostCompileTest('select_holder')
tests+=env.BoostRunTest('select_from_python_test', LIBS=ELIBS)
tests+=env.BoostCompileTest('select_arg_to_python_test')
env.BoostTestSummary(tests)
AlwaysBuild(tests)