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

Switch SCons to use SConsChecks submodule for Windows support

This commit is contained in:
Christoph Lassner
2014-10-07 16:22:11 +02:00
committed by Jim Bosch
parent c509a3ab01
commit 73b8350e53
7 changed files with 72 additions and 229 deletions

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "SConsChecks"]
path = SConsChecks
url = https://github.com/ChrislS/SConsChecks.git
branch = master

6
README
View File

@@ -26,7 +26,11 @@ Linux and CMake on Windows.
Building with SCons should be as simple as running "scons" and "scons
install", but you may need to use the "--with-boost*" options (see
"scons --help") to specify where to find Boost. The Python that is
used by SCons will be the one built against.
used by SCons will be the one built against. Additionally, the
SConsChecks submodule must be initialized by git before building
by running
git submodule update --init
Please see libs/numpy/doc/cmakeBuild.rst for more information on
building with CMake.

1
SConsChecks Submodule

Submodule SConsChecks added at 9000598871

View File

@@ -6,192 +6,21 @@
# http://www.boost.org/LICENSE_1_0.txt)
# Big thanks to Mike Jarvis for help with the configuration prescriptions below.
# Integration of SConsChecks for platform independent building
# by Christoph Lassner.
from __future__ import print_function
import os
import sys
import subprocess
import sysconfig
from SCons.SConf import CheckContext
from SConsChecks import AddLibOptions, GetLibChecks
def setupPaths(env, prefix, include, lib):
if prefix is not None:
if include is None:
include = os.path.join(prefix, "include")
if lib is None:
lib = os.path.join(prefix, "lib")
if include:
env.PrependUnique(CPPPATH=[include])
if lib:
env.PrependUnique(LIBPATH=[lib])
AddMethod(Environment, setupPaths)
def checkLibs(context, try_libs, source_file):
init_libs = context.env.get('LIBS', [])
context.env.PrependUnique(LIBS=[try_libs])
result = context.TryLink(source_file, '.cpp')
if not result :
context.env.Replace(LIBS=init_libs)
return result
AddMethod(CheckContext, checkLibs)
def CheckPython(context):
python_source_file = """
#include "Python.h"
int main()
{
Py_Initialize();
Py_Finalize();
return 0;
}
"""
context.Message('Checking if we can build against Python... ')
try:
import distutils.sysconfig
except ImportError:
context.Result(0)
print('Failed to import distutils.sysconfig.')
return False
context.env.AppendUnique(CPPPATH=[distutils.sysconfig.get_python_inc()])
libDir = distutils.sysconfig.get_config_var("LIBDIR")
context.env.AppendUnique(LIBPATH=[libDir])
libfile = distutils.sysconfig.get_config_var("LIBRARY")
import re
match = re.search("(python.*)\.(a|so|dylib)", libfile)
if match:
context.env.AppendUnique(LIBS=[match.group(1)])
if match.group(2) == 'a':
flags = distutils.sysconfig.get_config_var('LINKFORSHARED')
if flags is not None:
context.env.AppendUnique(LINKFLAGS=flags.split())
flags = [f for f in " ".join(distutils.sysconfig.get_config_vars("MODLIBS", "SHLIBS")).split()
if f != "-L"]
context.env.MergeFlags(" ".join(flags))
result, output = context.TryRun(python_source_file,'.cpp')
if not result and context.env["PLATFORM"] == 'darwin':
# Sometimes we need some extra stuff on Mac OS
frameworkDir = libDir # 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, output = context.TryRun(python_source_file,'.cpp')
if not result:
context.Result(0)
print("Cannot run program built with Python.")
return False
if context.env["PLATFORM"] == "darwin":
context.env["LDMODULESUFFIX"] = ".so"
context.Result(1)
return True
def CheckNumPy(context):
numpy_source_file = """
#include "Python.h"
#include "numpy/arrayobject.h"
#if PY_MAJOR_VERSION == 2
static void wrap_import_array() {
import_array();
}
#else
static void * wrap_import_array() {
import_array();
}
#endif
void doImport() {
wrap_import_array();
}
int main()
{
int result = 0;
Py_Initialize();
doImport();
if (PyErr_Occurred()) {
result = 1;
} else {
npy_intp dims = 2;
PyObject * a = PyArray_SimpleNew(1, &dims, NPY_INT);
if (!a) result = 1;
Py_DECREF(a);
}
Py_Finalize();
return result;
}
"""
context.Message('Checking if we can build against NumPy... ')
try:
import numpy
except ImportError:
context.Result(0)
print('Failed to import numpy.')
print('Things to try:')
print('1) Check that the command line python (with which you probably installed numpy):')
print(' ', end=' ')
sys.stdout.flush()
subprocess.call('which python',shell=True)
print(' is the same as the one used by SCons:')
print(' ',sys.executable)
print(' If not, then you probably need to reinstall numpy with %s.' % sys.executable)
print(' Alternatively, you can reinstall SCons with your preferred python.')
print('2) Check that if you open a python session from the command line,')
print(' import numpy is successful there.')
return False
context.env.Append(CPPPATH=numpy.get_include())
result = context.checkLibs([''],numpy_source_file)
if not result:
context.Result(0)
print("Cannot build against NumPy.")
return False
result, output = context.TryRun(numpy_source_file,'.cpp')
if not result:
context.Result(0)
print("Cannot run program built with NumPy.")
return False
context.Result(1)
return True
def CheckBoostPython(context):
bp_source_file = """
#include "boost/python.hpp"
class Foo { public: Foo() {} };
int main()
{
Py_Initialize();
boost::python::object obj;
boost::python::class_< Foo >("Foo", boost::python::init<>());
Py_Finalize();
return 0;
}
"""
context.Message('Checking if we can build against Boost.Python... ')
context.env.setupPaths(
prefix = GetOption("boost_prefix"),
include = GetOption("boost_include"),
lib = GetOption("boost_lib")
)
boost_python_lib = GetOption ('boost_python_lib')
result = (
context.checkLibs([''], bp_source_file) or
context.checkLibs([boost_python_lib], bp_source_file) or
context.checkLibs([boost_python_lib+'_mt'], bp_source_file)
)
if not result:
context.Result(0)
print("Cannot build against Boost.Python.")
return False
result, output = context.TryRun(bp_source_file, '.cpp')
if not result:
context.Result(0)
print ("Cannot run program built against Boost.Python.")
return False
context.Result(1)
return True
_libs = ['boost.python',
'python',
'numpy']
_checks = GetLibChecks(_libs)
# Setup command-line options
def setupOptions():
@@ -205,7 +34,7 @@ def setupOptions():
metavar="DIR", default=os.environ.get("BOOST_DIR"),
help="prefix for Boost libraries; should have 'include' and 'lib' subdirectories")
AddOption("--with-boost-include", dest="boost_include", type="string", nargs=1, action="store",
metavar="DIR", help="location of Boost header files")
metavar="DIR", help="location of Boost header files", default=os.environ.get("BOOST_DIR"))
AddOption("--with-boost-lib", dest="boost_lib", type="string", nargs=1, action="store",
metavar="DIR", help="location of Boost libraries")
AddOption("--rpath", dest="custom_rpath", type="string", action="append",
@@ -213,7 +42,10 @@ def setupOptions():
AddOption("--boost-python-lib", dest="boost_python_lib", type="string", action="store",
help="name of boost_python_lib", default='boost_python')
variables = Variables()
variables.Add("CCFLAGS", default=os.environ.get("CCFLAGS", "-O2 -g"), help="compiler flags")
defaultflags = "-O2 -g"
if os.name == 'nt':
defaultflags = "/O2"
variables.Add("CCFLAGS", default=os.environ.get("CCFLAGS", defaultflags), help="compiler flags")
return variables
def makeEnvironment(variables):
@@ -227,15 +59,37 @@ def makeEnvironment(variables):
custom_rpath = GetOption("custom_rpath")
if custom_rpath is not None:
env.AppendUnique(RPATH=custom_rpath)
boost_lib = GetOption ('boost_lib')
if boost_lib is not None:
env.PrependUnique(LIBPATH=boost_lib)
if env['CC'] == 'cl':
# C++ exception handling,
# multithread-supporting, dynamically linked system libraries,
# generate debug information,
# dynamic link library.
env.AppendUnique(CPPFLAGS=['/EHsc', '/MD', '/Zi', '/LD'])
return env
def setupTargets(env, root="."):
lib = SConscript(os.path.join(root, "libs", "numpy", "src", "SConscript"), exports='env')
example = SConscript(os.path.join(root, "libs", "numpy", "example", "SConscript"), exports='env')
test = SConscript(os.path.join(root, "libs", "numpy", "test", "SConscript"), exports='env')
# Determine file extensions.
VERSION = sys.version_info.major
if os.name == 'nt':
EXT_SUFFIX = '.dll'
LIB_SUFFIX = '.lib'
PY_SUFFIX = '.pyd'
else:
EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX")
if VERSION == 2 and EXT_SUFFIX == 'None' or EXT_SUFFIX==None:
EXT_SUFFIX = '.so'
elif VERSION == 3 and EXT_SUFFIX == b'None' or EXT_SUFFIX==None:
EXT_SUFFIX = '.so'
LIB_SUFFIX = EXT_SUFFIX
PY_SUFFIX = EXT_SUFFIX
OBJ_SUFFIX = EXT_SUFFIX.replace ('.so', '.os')
lib = SConscript(os.path.join(root, "libs", "numpy", "src", "SConscript"),
exports=['env', 'EXT_SUFFIX', 'LIB_SUFFIX', 'OBJ_SUFFIX'])
example = SConscript(os.path.join(root, "libs", "numpy", "example", "SConscript"),
exports='env')
test = SConscript(os.path.join(root, "libs", "numpy", "test", "SConscript"),
exports=['env', 'lib', 'EXT_SUFFIX', 'LIB_SUFFIX', 'OBJ_SUFFIX', 'PY_SUFFIX'])
prefix = Dir(GetOption("prefix")).abspath
install_headers = GetOption('install_headers')
install_lib = GetOption('install_lib')
@@ -244,7 +98,7 @@ def setupTargets(env, root="."):
if not install_lib:
install_lib = os.path.join(prefix, "lib")
env.Alias("install", env.Install(install_lib, lib))
for header in ("dtype.hpp", "invoke_matching.hpp", "matrix.hpp",
for header in ("dtype.hpp", "invoke_matching.hpp", "matrix.hpp",
"ndarray.hpp", "numpy_object_mgr_traits.hpp",
"scalars.hpp", "ufunc.hpp",):
env.Alias("install", env.Install(os.path.join(install_headers, "boost", "numpy"),
@@ -252,6 +106,4 @@ def setupTargets(env, root="."):
env.Alias("install", env.Install(os.path.join(install_headers, "boost"),
os.path.join(root, "boost", "numpy.hpp")))
checks = {"CheckPython": CheckPython, "CheckNumPy": CheckNumPy, "CheckBoostPython": CheckBoostPython}
Return("setupOptions", "makeEnvironment", "setupTargets", "checks")
Return("setupOptions", "makeEnvironment", "setupTargets", "_checks", "_libs")

View File

@@ -5,7 +5,9 @@
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
setupOptions, makeEnvironment, setupTargets, checks = SConscript("SConscript")
from SConsChecks import GetLibChecks
setupOptions, makeEnvironment, setupTargets, checks, libnames = SConscript("SConscript")
variables = setupOptions()
@@ -14,8 +16,9 @@ env.AppendUnique(CPPPATH="#.")
if not GetOption("help") and not GetOption("clean"):
config = env.Configure(custom_tests=checks)
if not (config.CheckPython() and config.CheckNumPy() and config.CheckBoostPython()):
Exit(1)
checknames = GetLibChecks(libnames).keys()
if False in (config.__dict__[checkname]() for checkname in checknames):
Exit(1)
env = config.Finish()
setupTargets(env)

View File

@@ -7,32 +7,16 @@
import sys
import os
VERSION = sys.version_info.major
import sysconfig
if os.name == 'nt':
EXT_SUFFIX = '.dll'
LIB_SUFFIX = '.lib'
else:
EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX")
LIB_SUFFIX = EXT_SUFFIX
if VERSION == 2 and EXT_SUFFIX == 'None' or EXT_SUFFIX==None:
EXT_SUFFIX = '.so'
elif VERSION == 3 and EXT_SUFFIX == b'None' or EXT_SUFFIX==None:
EXT_SUFFIX = '.so'
print ('EXT:', EXT_SUFFIX)
OBJ_SUFFIX = EXT_SUFFIX.replace ('.so', '.os')
Import("env")
Import(['env', 'EXT_SUFFIX', 'LIB_SUFFIX', 'OBJ_SUFFIX'])
LIB_BOOST_NUMPY = ('boost_numpy' + LIB_SUFFIX)
mods = [g.name.replace('.cpp', '') for g in Glob("*.cpp")]
for m in mods:
env.SharedObject (target=m+OBJ_SUFFIX, source=m+'.cpp')
sourcefiles = Glob("*.cpp")
if os.name == 'nt':
lib = env.StaticLibrary(LIB_BOOST_NUMPY, source=[m+OBJ_SUFFIX for m in mods])
lib = env.StaticLibrary(LIB_BOOST_NUMPY, source=sourcefiles)
else:
mods = [g.name.replace('.cpp', '') for g in sourcefiles]
for m in mods:
env.SharedObject (target=m+OBJ_SUFFIX, source=m+'.cpp')
lib = env.SharedLibrary(LIB_BOOST_NUMPY, source=[m+OBJ_SUFFIX for m in mods])
Return("lib")

View File

@@ -5,7 +5,7 @@
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
Import("env")
Import(['env', 'lib', 'EXT_SUFFIX', 'LIB_SUFFIX', 'OBJ_SUFFIX', 'PY_SUFFIX'])
import os
import sys
@@ -15,17 +15,7 @@ lib_path = os.path.abspath(os.path.join("..", "src"))
test_env.Append(LIBPATH=[lib_path])
test_env.Append(RPATH=[lib_path])
test_env.Append(LINKFLAGS = ["$__RPATH"]) # workaround for SCons bug #1644t
import sys;
VERSION = sys.version_info.major
print (VERSION)
import sysconfig
EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX")
if VERSION == 2:
test_env.Append(LIBS=["boost_numpy"])
elif VERSION == 3:
test_env.Append(LIBS=["boost_numpy"+EXT_SUFFIX.replace('.so', '')])
test_env.AppendUnique(LIBS=lib)
test = []
def RunPythonUnitTest(target, source, env):
@@ -39,6 +29,11 @@ def PythonUnitTest(env, script, dependencies):
for name in ("dtype", "ufunc", "templates", "ndarray", "indexing", "shapes"):
mod = test_env.LoadableModule("%s_mod" % name, "%s_mod.cpp" % name, LDMODULEPREFIX="")
if os.name == 'nt':
# Move the module to have the correct name.
mod = env.Command(os.path.join(Dir('.').srcnode().abspath, "%s_mod.pyd" % (name)),
mod,
Move(os.path.join(Dir('.').srcnode().abspath, "%s_mod.pyd" % (name)), mod[0]))
test.extend(PythonUnitTest(test_env, "%s.py" % name, mod))
Return("test")