diff --git a/appveyor.yml b/.appveyor.yml similarity index 83% rename from appveyor.yml rename to .appveyor.yml index bc11c5b8..714a51bd 100644 --- a/appveyor.yml +++ b/.appveyor.yml @@ -4,7 +4,7 @@ environment: # /E:ON and /V:ON options are not enabled in the batch script intepreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\run_with_env.cmd" - BOOST_PREFIX: C:\Libraries\boost_1_60_0 + BOOST_PREFIX: C:\Libraries\boost_1_63_0 matrix: @@ -15,7 +15,8 @@ environment: - PYTHON: "C:\\Python27" PYTHON_VERSION: "2.7.x" # currently 2.7.9 PYTHON_ARCH: "32" - ARCH: "x86" + MSVC: "14.0" + ARCH: x86 #- PYTHON: "C:\\Python27-x64" # PYTHON_VERSION: "2.7.x" # currently 2.7.9 @@ -26,9 +27,11 @@ environment: # PYTHON_VERSION: "3.5.x" # currently 3.4.3 # PYTHON_ARCH: "32" - #- PYTHON: "C:\\Python35-x64" - # PYTHON_VERSION: "3.5.x" # currently 3.4.3 - # PYTHON_ARCH: "64" + - PYTHON: "C:\\Python36-x64" + PYTHON_VERSION: "3.6.x" + PYTHON_ARCH: "64" + MSVC: "12.0" + ARCH: x86_64 install: # If there is a newer build queued for the same PR, cancel this one. @@ -68,21 +71,22 @@ install: # compiled extensions and are not provided as pre-built wheel packages, # pip will build them from source using the MSVC compiler matching the # target Python version and architecture - - easy_install scons + - | + curl -LfsS -o faber.tar.gz https://github.com/stefanseefeld/faber/archive/release/0.2.tar.gz + tar xf faber.tar.gz + CD faber-release-0.2 + python setup.py install + CD .. + # report the available MSVC compilers + - python -m faber.tools.msvc - easy_install sphinx - pip install numpy - #- "%CMD_IN_ENV% pip install -r dev-requirements.txt" build_script: - # Build the compiled extension - #- "%CMD_IN_ENV% python setup.py build" - - scons config arch=%ARCH% --boost-include=%BOOST_PREFIX% - - scons arch=%ARCH% --verbose + - faber --with-boost-include=%BOOST_PREFIX% target.arch=%ARCH% msvc.version=%MSVC% test_script: - # Run the project tests - #- "%CMD_IN_ENV% python setup.py nosetests" - - scons test arch=%ARCH% --verbose + - faber --with-boost-include=%BOOST_PREFIX% test.report target.arch=%ARCH% msvc.version=%MSVC% after_test: # If tests are successful, create binary packages for the project. @@ -97,4 +101,4 @@ after_test: #on_success: # - TODO: upload the content of dist/*.whl to a public wheelhouse -# \ No newline at end of file +# diff --git a/.ci/faber b/.ci/faber new file mode 100644 index 00000000..b23e8a18 --- /dev/null +++ b/.ci/faber @@ -0,0 +1,3 @@ +from faber.tools.python import python + +p = python(command='$PYTHON') diff --git a/.travis.yml b/.travis.yml index 97a4fc8a..e9342193 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,12 +26,10 @@ matrix: - compiler: gcc env: CXX=g++ PYTHON=python3 CXXFLAGS=-std=c++11 - compiler: clang - # clang generates an 'illegal instruction' error in the NumPy check. - # Perhaps we need to upgrade clang to a newer version ? - env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++98 OPTIONS=--no-numpy + env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++98 - compiler: clang - env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++11 OPTIONS=--no-numpy - - env: PYTHON=python DOC=1 + env: CXX=clang++ PYTHON=python3 CXXFLAGS=-std=c++11 + #- env: PYTHON=python DOC=1 addons: @@ -77,16 +75,28 @@ install: ./bootstrap.sh ./b2 tools/bcp mkdir -p $HOME/Boost + # Install Boost.Python prerequisites, but not Boost.Python itself. dist/bin/bcp python tools/boostbook tools/quickbook $HOME/Boost &> /dev/null + rm -rf $HOME/Boost/boost/python* + popd + # Install Faber, the build tool. + date=2017-11-09 + #wget https://github.com/stefanseefeld/faber/archive/snapshot/$date.tar.gz + wget https://github.com/stefanseefeld/faber/archive/release/0.2.tar.gz + tar xf 0.2.tar.gz + pushd faber-release-0.2 + sudo python setup.py install popd fi before_script: -- scons --version +- sed -e "s/\$PYTHON/$PYTHON/g" .ci/faber > ~/.faber +- $PYTHON --version +- faber -h script: -- scons config --python=$PYTHON --boost-include=$HOME/Boost $OPTIONS -- if [ "$DOC" ]; then scons doc; else scons && scons test; fi +- faber --with-boost-include=$HOME/Boost test.report cxx.name=$CXX cxxflags=$CXXFLAGS +#- if [ "$DOC" ]; then scons doc; else scons && scons test; fi after_success: # Upload docs only when building upstream. diff --git a/fabscript b/fabscript new file mode 100644 index 00000000..59be9e18 --- /dev/null +++ b/fabscript @@ -0,0 +1,80 @@ +# -*- 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) + +from faber.feature import set +from faber.types import cxx +from faber.tools.compiler import cxxflags, define, include +from faber.tools.python import python +from faber.config import report, cxx_checks +from faber.config.try_run import try_run + +features += include('include') +features += define('BOOST_ALL_NO_LIB') # disable auto-linking +boost_include = options.get_with('boost-include') +if boost_include: + features += include(boost_include) +python = python.instance() +features |= set(python.include, python.linkpath, python.libs) + +class has_numpy(try_run): + + src = r""" +// If defined, enforces linking against PythonXXd.lib, which +// is usually not included in Python environments. +#undef _DEBUG +#include "Python.h" +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include "numpy/arrayobject.h" + +#if PY_VERSION_HEX >= 0x03000000 +void *initialize() { import_array();} +#else +void initialize() { import_array();} +#endif + +int main() +{ + int result = 0; + Py_Initialize(); + initialize(); + 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; +} +""" + def __init__(self, features=()): + + inc = '' + try: + inc = python.check_python('import numpy; print(numpy.get_include())') + features |= include(inc) + except Exception: + # ignore errors, the check will fail during compilation... + pass + try_run.__init__(self, 'has_numpy', has_numpy.src, cxx, features, + if_=(include(inc), define('HAS_NUMPY'))) + +checks = [cxx_checks.has_cxx11(features, define('HAS_CXX11')), + has_numpy(features)] +config = report('config', checks) + +src = module('src', features=config.use) +test = module('test', features=config.use) + +default = src.default diff --git a/src/fabscript b/src/fabscript new file mode 100644 index 00000000..d5dbf603 --- /dev/null +++ b/src/fabscript @@ -0,0 +1,58 @@ +# -*- 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) + +from faber.feature import set +from faber.artefacts.library import library +from faber.tools.compiler import define + +root = module('..') + +bpl = library('boost_python', + ['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'], + dependencies=root.config, + features=features + define('BOOST_PYTHON_SOURCE')) + +bnl = library('boost_numpy', + ['numpy/dtype.cpp', + 'numpy/matrix.cpp', + 'numpy/ndarray.cpp', + 'numpy/numpy.cpp', + 'numpy/scalars.cpp', + 'numpy/ufunc.cpp', + bpl], + dependencies=root.config, + features=features + define('BOOST_NUMPY_SOURCE'), + condition=set.define.contains('HAS_NUMPY')) +default = [bpl, bnl] diff --git a/test/fabscript b/test/fabscript new file mode 100644 index 00000000..1989cc0d --- /dev/null +++ b/test/fabscript @@ -0,0 +1,173 @@ +# -*- 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) + +from faber import platform +from faber.feature import set +from faber.tools.compiler import runpath +from faber.tools.python import python, pythonpath +from faber.artefacts.object import object +from faber.artefacts.binary import binary +from faber.artefacts.python import extension +from faber.test import test, report, fail + +src = module('..src') + +python_libs=python.instance().libs +features |= runpath(src.bpl.path, base='') + +def extension_test(name, ext=[], script=None, np=False, + features=features, condition=None): + """Create a Python extension test `name`. + Arguments: + * name: the name of the test. + * ext: extensions to be compiled, if none are given. + * script: the test script to execute, .py if none is given. + * np: if true, add boost_numpy to sources + * features: pre-defined features + * condition: any condition under which to run the test + Return: + * the test artefact""" + + features=features.copy() + extensions = [] + libs = [src.bnl, src.bpl] if np else [src.bpl] + for e in ext or [name]: + if type(e) is str: # build from a single source file + n = e if e != name else e + '_ext' + s = [e + '.cpp'] + else: # build from a list of source files + n = e[0] if e[0] != name else e[0] + '_ext' + s = [n + '.cpp' for n in e] + e = extension(n, s + libs, features=features) + features |= pythonpath(e.path, base='') + extensions.append(e) + if not script: + script = name+'.py' + return test(name, script, run=python.run, dependencies=extensions, + features=features, condition=condition) + +tests = [] +for t in [('injected',), + ('properties',), + ('return_arg',), + ('staticmethod',), + ('boost_shared_ptr',), + ('enable_shared_from_this',), + ('andreas_beyer',), + ('polymorphism',), + ('polymorphism2',), + ('wrapper_held_type',), + ('minimal',), + ('args',), + ('raw_ctor',), + ('exception_translator',), + ('test_enum', ['enum_ext']), + ('test_cltree', ['cltree']), + ('newtest', ['m1', 'm2']), + ('const_argument',), + ('keywords_test', ['keywords']), + ('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',), + ('iterator', ['iterator', 'input_iterator']), + ('stl_iterator',), + ('extract',), + ('crossmod_opaque', ['crossmod_opaque_a', 'crossmod_opaque_b']), + ('opaque',), + ('voidptr',), + ('pickle1',), + ('pickle2',), + ('pickle3',), + ('pickle4',), + ('nested',), + ('docstring',), + ('pytype_function',), + ('vector_indexing_suite',), + ('pointer_vector',), + ('builtin_converters', [], 'test_builtin_converters.py'), + ('map_indexing_suite', + [['map_indexing_suite', 'int_map_indexing_suite', 'a_map_indexing_suite']])]: + tests.append(extension_test(*t)) + +tests.append(extension_test('shared_ptr', + condition=set.define.contains('HAS_CXX11'))) +tests.append(extension_test('polymorphism2_auto_ptr', + condition=set.define.contains('HAS_CXX11').not_())) +tests.append(extension_test('auto_ptr', + condition=set.define.contains('HAS_CXX11'))) + +import_ = binary('import_', ['import_.cpp', src.bpl], features=features|python_libs) +if platform.os == 'Windows': + command = """set PATH=$(runpath);%PATH% +$(>[1]) $(>[2])""" +else: + command = 'LD_LIBRARY_PATH=$(runpath) $(>[1]) $(>[2])' + +tests.append(test('import', [import_, 'import_.py'], + run=action('run', command), + features=features)) + +tests.append(extension_test('calling_conventions', + condition=platform.os == 'Windows')) +tests.append(extension_test('calling_conventions_mf', + condition=platform.os == 'Windows')) + +for t in ['destroy_test', + 'pointer_type_id_test', + 'bases', + 'pointee', + 'if_else', + 'pointee', + 'result', + 'upcast', + 'select_from_python_test']: + tests.append(test(t, binary(t, [t + '.cpp', src.bpl], features=features), features=features, run=True)) +for t in ['indirect_traits_test', + 'string_literal', + 'borrowed', + 'object_manager', + 'copy_ctor_mutates_rhs', + 'select_holder', + 'select_arg_to_python_test']: + tests.append(test(t, object(t, [t + '.cpp'], features=features))) + +for t in ['raw_pyobject_fail1', + 'raw_pyobject_fail2', + 'as_to_python_function', + 'object_fail1']: + tests.append(test(t, object(t, [t + '.cpp'], features=features), expected=fail)) + +for t in ['numpy/dtype', + 'numpy/ufunc', + 'numpy/templates', + 'numpy/ndarray', + 'numpy/indexing', + 'numpy/shapes']: + tests.append(extension_test(t, np=True, + condition=set.define.contains('HAS_NUMPY'))) + +default = report('report', tests, fail_on_failures=True)