Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to scikit-build #219

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ symengine.egg-info/
# Temp files
*~
.eggs/

_skbuild
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ env:
# Release builds:
- PYTHON_VERSION="2.7" BUILD_SHARED_LIBS="yes"
- PYTHON_VERSION="2.7" WITH_MPFR="yes" INTEGER_CLASS="gmpxx" WITH_NUMPY="no"
- PYTHON_VERSION="3.3" WITH_MPC="yes"
- PYTHON_VERSION="3.4" WITH_MPFR="yes"
- PYTHON_VERSION="3.5" WITH_MPC="yes"
- PYTHON_VERSION="3.6" WITH_MPC="yes" INTEGER_CLASS="flint" WITH_FLINT="yes"
Expand Down
60 changes: 32 additions & 28 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ environment:
PLATFORMTOOLSET: "v140"

matrix:
- BUILD_TYPE: "Release"
COMPILER: MSVC15
PLATFORM: "Win32"
PYTHON_VERSION: 27
CONDA_INSTALL_LOCN: C:\\Miniconda35
WITH_MPFR: yes
WITH_MPC: yes
- BUILD_TYPE: "Release"
COMPILER: MSVC15
PLATFORM: "x64"
Expand All @@ -36,33 +29,39 @@ environment:
# COMPILER: MinGW
# PYTHON_VERSION: 35
- BUILD_TYPE: "Release"
COMPILER: MinGW-w64
PYTHON_VERSION: 27-x64
- BUILD_TYPE: "Debug"
COMPILER: MinGW-w64
PYTHON_VERSION: 35-x64
WITH_NUMPY: no
- BUILD_TYPE: "Debug"
COMPILER: MinGW-w64
PYTHON_VERSION: 36-x64
WITH_SYMPY: no
COMPILER: MSVC15
PLATFORM: "Win32"
PYTHON_VERSION: 27
CONDA_INSTALL_LOCN: C:\\Miniconda35
WITH_MPFR: yes
WITH_MPC: yes
# - BUILD_TYPE: "Debug"
# COMPILER: MinGW-w64
# PYTHON_VERSION: 35-x64
# WITH_NUMPY: no
# - BUILD_TYPE: "Debug"
# COMPILER: MinGW-w64
# PYTHON_VERSION: 36-x64
# WITH_SYMPY: no

install:
- set PYTHON_SOURCE_DIR=%CD%
- git clone https://github.com/sympy/symengine symengine-cpp

- if [%COMPILER%]==[MSVC15] call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
- if [%COMPILER%]==[MSVC15] conda update --yes --quiet conda

- if [%COMPILER%]==[MSVC15] conda config --add channels conda-forge
- if [%COMPILER%]==[MSVC15] if [%BUILD_TYPE%]==[Debug] conda config --add channels symengine/label/debug
- if [%COMPILER%]==[MSVC15] conda create -n test --yes mpir=3.0.0 vc=14
- if [%COMPILER%]==[MSVC15] call activate test
- if [%COMPILER%]==[MSVC15] if [%WITH_MPFR%]==[yes] conda install --yes mpfr=3.1.5
- if [%COMPILER%]==[MSVC15] if [%WITH_MPC%]==[yes] conda install --yes mpc=1.0.3
- if [%COMPILER%]==[MSVC15] if [%WITH_LLVM%]==[yes] conda install --yes llvmdev=3.9

- if [%COMPILER%]==[MSVC15] if [%WITH_MPFR%]==[yes] set MPFR_SPEC="mpfr=3.1.5"
- if [%COMPILER%]==[MSVC15] if [%WITH_MPC%]==[yes] set MPC_SPEC="mpc=1.0.3"
- if [%COMPILER%]==[MSVC15] if [%WITH_LLVM%]==[yes] set LLVM_SPEC="llvmdev=3.9"

- if [%COMPILER%]==[MSVC15] conda install --yes mpir=3.0.0 vc=14 ninja %MPFR_SPEC% %MPC_SPEC% %LLVM_SPEC%

- if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
- if [%COMPILER%]==[MinGW] mingw-get update

# workaround for https://github.com/appveyor/ci/issues/996
- if [%COMPILER%]==[MinGW] mingw-get upgrade mingw32-libstdc++
- if [%COMPILER%]==[MinGW] mingw-get install mingw32-gmp
Expand Down Expand Up @@ -92,23 +91,28 @@ install:
- mkdir build
- cd build

- if [%COMPILER%]==[MSVC15] if [%PLATFORM%]==[Win32] cmake -G "Visual Studio 14 2015" -DCMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library ..
- if [%COMPILER%]==[MSVC15] if [%PLATFORM%]==[x64] cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library ..
- if [%COMPILER%]==[MSVC15] if [%PLATFORM%]==[Win32] call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
- if [%COMPILER%]==[MSVC15] if [%PLATFORM%]==[x64] call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64

- if [%COMPILER%]==[MSVC15] cmake -G "Ninja" -DCMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ..

- if [%COMPILER%]==[MinGW] cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=C:\MinGW -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ..
- if [%COMPILER%]==[MinGW-w64] cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=C:\mingw64 -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ..

- if [%WITH_MPFR%]==[yes] cmake -DWITH_MPFR=yes ..
- if [%WITH_MPC%]==[yes] cmake -DWITH_MPC=yes ..
- if [%WITH_LLVM%]==[yes] cmake -DWITH_LLVM=yes -DMSVC_USE_MT=no ..

- cmake -DBUILD_SHARED_LIBS=yes -DBUILD_TESTS=no -DBUILD_BENCHMARKS=no -DCMAKE_INSTALL_PREFIX=C:\symengine ..
- cmake -DBUILD_SHARED_LIBS=yes -DBUILD_TESTS=no -DBUILD_BENCHMARKS=no -DCMAKE_INSTALL_PREFIX=C:\symengine ..

- cmake --build . --config %BUILD_TYPE% --target install
- cmake --build . --target install
- cd ../../

- python -m pip install scikit-build

build_script:
- set PATH=C:\symengine\bin\;%PATH%
- if [%COMPILER%]==[MSVC15] python setup.py install build_ext --compiler=msvc --build-type=%BUILD_TYPE%
- if [%COMPILER%]==[MSVC15] python -m pip install -vvv .
- if [%COMPILER%]==[MinGW] python setup.py install build_ext --compiler=mingw --inplace
- if [%COMPILER%]==[MinGW-w64] python setup.py install build_ext --compiler=mingw --inplace

Expand Down
4 changes: 3 additions & 1 deletion bin/test_travis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ set -e
set -x

# Build inplace so that nosetests can be run inside source directory
python setup.py install build_ext --inplace --symengine-dir=$our_install_dir
pip install -U pip
pip install -U cython scikit-build
pip install -vvv -e .

# Test python wrappers
nosetests -v
Expand Down
230 changes: 20 additions & 210 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,217 +1,28 @@
from __future__ import print_function
from os import getenv, path, makedirs

import os
import subprocess
import shlex
import sys
from distutils.command.build_ext import build_ext as _build_ext
from distutils.command.build import build as _build

# Make sure the system has the right Python version.
if sys.version_info[:2] < (2, 7):
print("SymEngine requires Python 2.7 or newer. "
"Python %d.%d detected" % sys.version_info[:2])
sys.exit(-1)

# use setuptools by default as per the official advice at:
# packaging.python.org/en/latest/current.html#packaging-tool-recommendations
use_setuptools = True
# set the environment variable USE_DISTUTILS=True to force the use of distutils
use_distutils = getenv('USE_DISTUTILS')
if use_distutils is not None:
if use_distutils.lower() == 'true':
use_setuptools = False
else:
print("Value {} for USE_DISTUTILS treated as False".
format(use_distutils))

if use_setuptools:
try:
from setuptools import setup
from setuptools.command.install import install as _install
except ImportError:
use_setuptools = False

if not use_setuptools:
from distutils.core import setup
from distutils.command.install import install as _install

cmake_opts = [("PYTHON_BIN", sys.executable),
("CMAKE_INSTALL_RPATH_USE_LINK_PATH", "yes")]
cmake_generator = [None]
cmake_build_type = ["Release"]


def process_opts(opts):
return ['-D'+'='.join(o) for o in opts]


def get_build_dir(dist):
source_dir = path.dirname(path.realpath(__file__))
build = dist.get_command_obj('build')
build_ext = dist.get_command_obj('build_ext')
return source_dir if build_ext.inplace else build.build_platlib


global_user_options = [
('symengine-dir=', None,
'path to symengine installation or build directory'),
('generator=', None, 'cmake build generator'),
('build-type=', None, 'build type: Release or Debug'),
('define=', 'D',
'options to cmake <var>:<type>=<value>'),
]

def _process_define(arg):
(defs, one), = getattr(arg, 'define', None) or [('', '1')]
assert one == '1'
defs = [df for df in defs.split(';') if df != '']
return [(s.strip(), None) if '=' not in s else
tuple(ss.strip() for ss in s.split('='))
for s in defs]


class BuildWithCmake(_build):
sub_commands = [('build_ext', None)]


class BuildExtWithCmake(_build_ext):
_build_opts = _build_ext.user_options
user_options = list(global_user_options)
user_options.extend(_build_opts)

def initialize_options(self):
_build_ext.initialize_options(self)
self.define = None
self.symengine_dir = None
self.generator = None
self.build_type = "Release"

def finalize_options(self):
_build_ext.finalize_options(self)
# The argument parsing will result in self.define being a string, but
# it has to be a list of 2-tuples.
# Multiple symbols can be separated with semi-colons.
self.define = _process_define(self)
cmake_opts.extend(self.define)
if self.symengine_dir:
cmake_opts.extend([('SymEngine_DIR', self.symengine_dir)])

if self.generator:
cmake_generator[0] = self.generator

cmake_build_type[0] = self.build_type

def cmake_build(self):
source_dir = path.dirname(path.realpath(__file__))
build_dir = get_build_dir(self.distribution)
if not path.exists(build_dir):
makedirs(build_dir)
if build_dir != source_dir and path.exists("CMakeCache.txt"):
os.remove("CMakeCache.txt")

cmake_cmd = ["cmake", source_dir,
"-DCMAKE_BUILD_TYPE=" + cmake_build_type[0]]
cmake_cmd.extend(process_opts(cmake_opts))
if not path.exists(path.join(build_dir, "CMakeCache.txt")):
cmake_cmd.extend(self.get_generator())
if subprocess.call(cmake_cmd, cwd=build_dir) != 0:
raise EnvironmentError("error calling cmake")

if subprocess.call(["cmake", "--build", ".",
"--config", cmake_build_type[0]],
cwd=build_dir) != 0:
raise EnvironmentError("error building project")

def get_generator(self):
if cmake_generator[0]:
return ["-G", cmake_generator[0]]
else:
import platform
import sys
if (platform.system() == "Windows"):
compiler = str(self.compiler).lower()
if ("msys" in compiler):
return ["-G", "MSYS Makefiles"]
elif ("mingw" in compiler):
return ["-G", "MinGW Makefiles"]
elif sys.maxsize > 2**32:
return ["-G", "Visual Studio 14 2015 Win64"]
else:
return ["-G", "Visual Studio 14 2015"]
return []

def run(self):
self.cmake_build()
# can't use super() here because
# _build_ext is an old style class in 2.7
_build_ext.run(self)


class InstallWithCmake(_install):
_install_opts = _install.user_options
user_options = list(global_user_options)
user_options.extend(_install_opts)

def initialize_options(self):
_install.initialize_options(self)
self.define = None
self.symengine_dir = None
self.generator = None
self.build_type = "Release"

def finalize_options(self):
_install.finalize_options(self)
# The argument parsing will result in self.define being a string, but
# it has to be a list of 2-tuples.
# Multiple symbols can be separated with semi-colons.
self.define = _process_define(self)
cmake_opts.extend(self.define)
cmake_build_type[0] = self.build_type
cmake_opts.extend([('PYTHON_INSTALL_PATH', path.join(os.getcwd(), self.install_platlib))])
#cmake_opts.extend([('PYTHON_INSTALL_HEADER_PATH',
# path.join(os.getcwd(), self.install_headers))])

def cmake_install(self):
source_dir = path.dirname(path.realpath(__file__))
build_dir = get_build_dir(self.distribution)
cmake_cmd = ["cmake", source_dir]
cmake_cmd.extend(process_opts(cmake_opts))

# CMake has to be called here to update PYTHON_INSTALL_PATH
# if build and install were called separately by the user
if subprocess.call(cmake_cmd, cwd=build_dir) != 0:
raise EnvironmentError("error calling cmake")

if subprocess.call(["cmake", "--build", ".",
"--config", cmake_build_type[0],
"--target", "install"],
cwd=build_dir) != 0:
raise EnvironmentError("error installing")

import compileall
compileall.compile_dir(path.join(self.install_platlib, "symengine"))

def run(self):
# can't use super() here because _install is an old style class in 2.7
_install.run(self)
self.cmake_install()

cmdclass={
'build': BuildWithCmake,
'build_ext': BuildExtWithCmake,
'install': InstallWithCmake,
}

try:
from wheel.bdist_wheel import bdist_wheel
class BdistWheelWithCmake(bdist_wheel):
def finalize_options(self):
bdist_wheel.finalize_options(self)
self.root_is_pure = False
cmdclass["bdist_wheel"] = BdistWheelWithCmake
from skbuild import setup
except ImportError:
pass

print('scikit-build is required to build from source.', file=sys.stderr)
print('Please run:', file=sys.stderr)
print('', file=sys.stderr)
print(' python -m pip install scikit-build')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the command just be pip install scikit-build?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both ways work but the advantage of python -m pip ... is that it's easier to tell which version of Python the package is being installed in (this SO thread discusses it some more). The Python 3 documentation recommends this way of invoking pip (also see this Python issue that implemented the change in the docs).

sys.exit(1)

from setuptools import find_packages

# BEGIN
# TODO: remove hack when py27 is dropped
from skbuild.platform_specifics import windows

if sys.version_info < (3, 0):
windows._get_msvc_compiler_env = lambda _ : {}
# END
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be incorporated into scikit-build itself?


long_description = '''
SymEngine is a standalone fast C++ symbolic manipulation library.
Optional thin Python wrappers (SymEngine) allow easy usage from Python and
Expand All @@ -225,13 +36,12 @@ def finalize_options(self):
setup(name="symengine",
version="0.3.0",
description="Python library providing wrappers to SymEngine",
setup_requires=['cython>=0.19.1'],
long_description=long_description,
packages=find_packages(),
author="SymEngine development team",
author_email="symengine@googlegroups.com",
license="MIT",
url="https://github.com/symengine/symengine.py",
cmdclass = cmdclass,
classifiers=[
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
Expand Down
2 changes: 1 addition & 1 deletion symengine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
add_subdirectory(lib)
add_subdirectory(tests)

set(PY_PATH ${PYTHON_INSTALL_PATH}/symengine)
set(PY_PATH symengine)
install(FILES __init__.py utilities.py compatibility.py sympy_compat.py functions.py printing.py
DESTINATION ${PY_PATH}
)
Loading