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

[WIP] [PROPOSAL] use scikit-build for packaging #144

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
26 changes: 20 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.6)
project(python_wrapper)

find_package(PythonInterp REQUIRED)
find_package(PythonLibs REQUIRED)
find_package(PythonExtensions REQUIRED)
find_package(Cython REQUIRED)

set(CMAKE_PREFIX_PATH ${SymEngine_DIR} ${CMAKE_PREFIX_PATH})
find_package(SymEngine 0.3.0 REQUIRED CONFIG

find_package(SymEngine 0.3.0
REQUIRED CONFIG
PATH_SUFFIXES lib/cmake/symengine cmake/symengine CMake/)

message("SymEngine_DIR : " ${SymEngine_DIR})

set(CMAKE_BUILD_TYPE ${SYMENGINE_BUILD_TYPE})
set(CMAKE_CXX_FLAGS_RELEASE ${SYMENGINE_CXX_FLAGS_RELEASE})
set(CMAKE_CXX_FLAGS_DEBUG ${SYMENGINE_CXX_FLAGS_DEBUG})

include_directories(${SYMENGINE_INCLUDE_DIRS})

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
find_package(Python REQUIRED)
find_package(Cython REQUIRED)
# set(CMAKE_MODULE_PATH
# ${CMAKE_MODULE_PATH}
# "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")


include_directories(${PYTHON_INCLUDE_PATH})

Expand All @@ -22,7 +33,10 @@ if (MINGW AND ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8"))
endif()

if (MINGW AND (CMAKE_BUILD_TYPE STREQUAL "Release"))
try_compile(CHECK_PYTHON_HYPOT "${CMAKE_CURRENT_BINARY_DIR}/" "${CMAKE_SOURCE_DIR}/cmake/check_python_hypot.cpp")
try_compile(CHECK_PYTHON_HYPOT
"${CMAKE_CURRENT_BINARY_DIR}/"
"${CMAKE_SOURCE_DIR}/cmake/check_python_hypot.cpp")

if (NOT ${CHECK_PYTHON_HYPOT})
# include cmath before all headers
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -include cmath")
Expand Down
202 changes: 12 additions & 190 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,200 +3,27 @@
import os
import subprocess
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))
from skbuild import setup
Copy link
Contributor

Choose a reason for hiding this comment

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

try:
    from skbuild import setup
except ImportError:
    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')
    sys.exit(1)


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>'),
]


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.
if self.define:
defines = self.define.split(';')
self.define = [(s.strip(), None) if '=' not in s else
tuple(ss.strip() for ss in s.split('='))
for s in defines]
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.
if self.define:
defines = self.define.split(';')
self.define = [(s.strip(), None) if '=' not in s else
tuple(ss.strip() for ss in s.split('='))
for s in defines]
cmake_opts.extend(self.define)

cmake_build_type[0] = self.build_type
cmake_opts.extend([('PYTHON_INSTALL_PATH', self.install_platlib)])
cmake_opts.extend([('PYTHON_INSTALL_HEADER_PATH',
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()
# cmake_opts = [("PYTHON_BIN", sys.executable),
# ("CMAKE_INSTALL_RPATH_USE_LINK_PATH", "yes")]
# cmake_build_type = ["Release"]

## 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>'),
## ]

long_description = '''
SymEngine is a standalone fast C++ symbolic manipulation library.
Expand All @@ -212,11 +39,6 @@ def run(self):
author_email="[email protected]",
license="MIT",
url="https://github.com/symengine/symengine.py",
cmdclass={
'build': BuildWithCmake,
'build_ext': BuildExtWithCmake,
'install': InstallWithCmake,
},
classifiers=[
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
Expand Down
7 changes: 3 additions & 4 deletions symengine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
add_subdirectory(lib)
add_subdirectory(tests)

set(PY_PATH ${PYTHON_INSTALL_PATH}/symengine)
install(FILES __init__.py utilities.py compatibility.py sympy_compat.py
DESTINATION ${PY_PATH}
)
install(FILES
__init__.py utilities.py compatibility.py sympy_compat.py
DESTINATION ${PYTHON_RELATIVE_SITE_PACKAGES_DIR}/symengine)
31 changes: 13 additions & 18 deletions symengine/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
set(SRC
symengine_wrapper.cpp
pywrapper.cpp
)

set(SRC pywrapper.cpp)

configure_file(config.pxi.in config.pxi)

include_directories(BEFORE ${python_wrapper_BINARY_DIR}/symengine/lib)
add_subdirectory(symengine)

cython_add_module_pyx(symengine_wrapper symengine.pxd)
add_python_library(symengine_wrapper ${SRC})
add_cython_target(symengine_wrapper CXX OUTPUT_VAR symengine_wrapper_src)
add_library(symengine_wrapper MODULE ${symengine_wrapper_src} ${SRC})
target_link_libraries(symengine_wrapper ${SYMENGINE_LIBRARIES})
python_extension_module(symengine_wrapper)

if (CMAKE_CXX_COMPILER_ID MATCHES GNU|Clang)
# Must suppress strict aliasing for this file
set_source_files_properties(
symengine_wrapper.cpp
PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -Wno-unused-function"
)
PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -Wno-unused-function")
endif()

add_custom_target(cython
COMMAND cython symengine_wrapper.pyx
)
set(symengine_pkg ${PYTHON_RELATIVE_SITE_PACKAGES_DIR}/symengine)

set(PY_PATH ${PYTHON_INSTALL_PATH}/symengine/lib)
install(TARGETS symengine_wrapper
RUNTIME DESTINATION ${PY_PATH}
ARCHIVE DESTINATION ${PY_PATH}
LIBRARY DESTINATION ${PY_PATH}
)
RUNTIME DESTINATION ${symengine_pkg}/lib
ARCHIVE DESTINATION ${symengine_pkg}/lib
LIBRARY DESTINATION ${symengine_pkg}/lib)

install(FILES __init__.py
${CMAKE_CURRENT_BINARY_DIR}/config.pxi
symengine.pxd
symengine_wrapper.pxd
DESTINATION ${PY_PATH}
)
DESTINATION ${symengine_pkg}/lib)
5 changes: 1 addition & 4 deletions symengine/lib/symengine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
install(FILES
pywrapper.h
DESTINATION ${PYTHON_INSTALL_HEADER_PATH}
)
install(FILES pywrapper.h DESTINATION include)
3 changes: 1 addition & 2 deletions symengine/lib/symengine_wrapper.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ from symengine cimport RCP, pair, map_basic_basic, umap_int_basic, umap_int_basi
from libcpp cimport bool
from libcpp.string cimport string
from libcpp.vector cimport vector
from cpython cimport PyObject, Py_XINCREF, Py_XDECREF, \
PyObject_CallMethodObjArgs
from cpython cimport PyObject, Py_XINCREF, Py_XDECREF, PyObject_CallMethodObjArgs
from libc.string cimport memcpy
import cython
import itertools
Expand Down
4 changes: 1 addition & 3 deletions symengine/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
set(PY_PATH ${PYTHON_INSTALL_PATH}/symengine/tests)
install(FILES __init__.py
test_arit.py
test_dict_basic.py
Expand All @@ -17,5 +16,4 @@ install(FILES __init__.py
test_var.py
test_lambdify.py
test_sympy_compat.py
DESTINATION ${PY_PATH}
)
DESTINATION ${PYTHON_RELATIVE_SITE_PACKAGES_DIR}/symengine/tests)