Skip to content

Commit 2755fa5

Browse files
authored
Merge pull request #101 from isuruf/setup
In-place and out-of-tree builds using setup.py
2 parents 1df763b + 5ef13d2 commit 2755fa5

File tree

4 files changed

+53
-42
lines changed

4 files changed

+53
-42
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ Python wrappers can be installed by,
3535

3636
Additional options to setup.py are
3737

38-
python setup.py install build
38+
python setup.py install build_ext
3939
--symengine-dir=/path/to/symengine/install/dir # Path to SymEngine install directory or build directory
4040
--compiler=mingw32|msvc|cygwin # Select the compiler for Windows
4141
--generator=cmake-generator # CMake Generator
4242
--build-type=Release|Debug # Set build-type for multi-configuration generators like MSVC
4343
--define="var1=value1;var2=value2" # Give options to CMake
44+
--inplace # Build the extension in source tree
4445

4546
Standard options to setup.py like `--user`, `--prefix` can be used to configure install location
4647

appveyor.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ install:
8383

8484
build_script:
8585

86-
- if [%COMPILER%]==[MSVC15] python setup.py install build --compiler=msvc --build-type=%BUILD_TYPE%
87-
- if [%COMPILER%]==[MinGW] python setup.py install build --compiler=mingw
88-
- if [%COMPILER%]==[MinGW-w64] python setup.py install build --compiler=mingw
86+
- if [%COMPILER%]==[MSVC15] python setup.py install build_ext --compiler=msvc --build-type=%BUILD_TYPE%
87+
- if [%COMPILER%]==[MinGW] python setup.py install build_ext --compiler=mingw --inplace
88+
- if [%COMPILER%]==[MinGW-w64] python setup.py install build_ext --compiler=mingw --inplace
8989

9090
test_script:
9191
- if not [%COMPILER%]==[MSVC15] nosetests

bin/test_travis.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ set -e
55
# Echo each command
66
set -x
77

8-
# Build and install python wrappers
9-
python setup.py install --symengine-dir=$our_install_dir
8+
# Build inplace so that nosetests can be run inside source directory
9+
python setup.py install build_ext --inplace --symengine-dir=$our_install_dir
1010

1111
# Test python wrappers
1212
if [[ "${WITH_SAGE}" != "yes" ]]; then

setup.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import print_function
2-
from os import getenv, path
2+
from os import getenv, path, makedirs
3+
import os
34
import subprocess
45
import sys
56

@@ -20,6 +21,7 @@
2021
print("Value {} for USE_DISTUTILS treated as False".\
2122
format(use_distutils))
2223

24+
from distutils.command.build_ext import build_ext as _build_ext
2325
from distutils.command.build import build as _build
2426

2527
if use_setuptools:
@@ -40,26 +42,37 @@
4042
def process_opts(opts):
4143
return ['-D'+'='.join(o) for o in opts]
4244

45+
def get_build_dir(dist):
46+
source_dir = path.dirname(path.realpath(__file__))
47+
build = dist.get_command_obj('build')
48+
build_ext = dist.get_command_obj('build_ext')
49+
return source_dir if build_ext.inplace else build.build_platlib
50+
51+
global_user_options = [
52+
('symengine-dir=', None, 'path to symengine installation or build directory'),
53+
('generator=', None, 'cmake build generator'),
54+
('build-type=', None, 'build type: Release or Debug'),
55+
('define=', 'D',
56+
'options to cmake <var>:<type>=<value>'),
57+
]
58+
4359
class BuildWithCmake(_build):
44-
_build_opts = _build.user_options
45-
user_options = [
46-
('symengine-dir=', None, 'path to symengine installation or build directory'),
47-
('generator=', None, 'cmake build generator'),
48-
('build-type=', None, 'build type: Release or Debug'),
49-
('define=', 'D',
50-
'options to cmake <var>:<type>=<value>')
51-
]
60+
sub_commands = [('build_ext', None)]
61+
62+
class BuildExtWithCmake(_build_ext):
63+
_build_opts = _build_ext.user_options
64+
user_options = list(global_user_options)
5265
user_options.extend(_build_opts)
5366

5467
def initialize_options(self):
55-
_build.initialize_options(self)
68+
_build_ext.initialize_options(self)
5669
self.define = None
5770
self.symengine_dir = None
5871
self.generator = None
5972
self.build_type = "Release"
6073

6174
def finalize_options(self):
62-
_build.finalize_options(self)
75+
_build_ext.finalize_options(self)
6376
# The argument parsing will result in self.define being a string, but
6477
# it has to be a list of 2-tuples.
6578
# Multiple symbols can be separated with semi-colons.
@@ -78,15 +91,22 @@ def finalize_options(self):
7891
cmake_build_type[0] = self.build_type
7992

8093
def cmake_build(self):
81-
dir = path.dirname(path.realpath(__file__))
82-
cmake_cmd = ["cmake", dir, "-DCMAKE_BUILD_TYPE=" + cmake_build_type[0]]
94+
source_dir = path.dirname(path.realpath(__file__))
95+
build_dir = get_build_dir(self.distribution)
96+
if not path.exists(build_dir):
97+
makedirs(build_dir)
98+
if build_dir != source_dir and path.exists("CMakeCache.txt"):
99+
os.remove("CMakeCache.txt")
100+
101+
cmake_cmd = ["cmake", source_dir, "-DCMAKE_BUILD_TYPE=" + cmake_build_type[0]]
83102
cmake_cmd.extend(process_opts(cmake_opts))
84-
if not path.exists("CMakeCache.txt"):
103+
if not path.exists(path.join(build_dir, "CMakeCache.txt")):
85104
cmake_cmd.extend(self.get_generator())
86-
if subprocess.call(cmake_cmd) != 0:
105+
if subprocess.call(cmake_cmd, cwd=build_dir) != 0:
87106
raise EnvironmentError("error calling cmake")
88107

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

92112
def get_generator(self):
@@ -108,25 +128,17 @@ def get_generator(self):
108128

109129
def run(self):
110130
self.cmake_build()
111-
# can't use super() here because _build is an old style class in 2.7
112-
_build.run(self)
131+
# can't use super() here because _build_ext is an old style class in 2.7
132+
_build_ext.run(self)
113133

114134
class InstallWithCmake(_install):
115135
_install_opts = _install.user_options
116-
user_options = [
117-
('symengine-dir=', None, 'path to symengine installation or build directory'),
118-
('generator=', None, 'cmake build generator'),
119-
('build-type=', None, 'build type: Release or Debug'),
120-
('define=', 'D',
121-
'options to cmake <var>:<type>=<value>')
122-
]
136+
user_options = list(global_user_options)
123137
user_options.extend(_install_opts)
124138

125139
def initialize_options(self):
126140
_install.initialize_options(self)
127141
self.define = None
128-
self.symengine_dir = None
129-
self.generator = None
130142
self.build_type = "Release"
131143

132144
def finalize_options(self):
@@ -140,27 +152,24 @@ def finalize_options(self):
140152
tuple(ss.strip() for ss in s.split('='))
141153
for s in defines]
142154
cmake_opts.extend(self.define)
143-
if self.symengine_dir:
144-
cmake_opts.extend([('SymEngine_DIR', self.symengine_dir)])
145-
146-
if self.generator:
147-
cmake_generator[0] = self.generator
148155

149156
cmake_build_type[0] = self.build_type
150157
cmake_opts.extend([('PYTHON_INSTALL_PATH', self.install_platlib)])
151158
cmake_opts.extend([('PYTHON_INSTALL_HEADER_PATH', self.install_headers)])
152159

153160
def cmake_install(self):
154-
dir = path.dirname(path.realpath(__file__))
155-
cmake_cmd = ["cmake", dir]
161+
source_dir = path.dirname(path.realpath(__file__))
162+
build_dir = get_build_dir(self.distribution)
163+
cmake_cmd = ["cmake", source_dir]
156164
cmake_cmd.extend(process_opts(cmake_opts))
157165

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

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

166175
import compileall
@@ -187,6 +196,7 @@ def run(self):
187196
url = "https://github.com/symengine/symengine.py",
188197
cmdclass={
189198
'build' : BuildWithCmake,
199+
'build_ext' : BuildExtWithCmake,
190200
'install' : InstallWithCmake,
191201
},
192202
classifiers=[

0 commit comments

Comments
 (0)