From 3d31449724a9c8aa72380fddd8dee18bdbfe487e Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 3 Jan 2019 10:50:03 -0500 Subject: [PATCH 1/5] Use pybind11 setup widgetry from aksetup --- aksetup_helper.py | 158 ++++++++++++++++++++++++++++++++++++++-------- setup.py | 101 ++--------------------------- 2 files changed, 137 insertions(+), 122 deletions(-) diff --git a/aksetup_helper.py b/aksetup_helper.py index 3642289..0b53185 100644 --- a/aksetup_helper.py +++ b/aksetup_helper.py @@ -1,6 +1,8 @@ import setuptools # noqa from setuptools import Extension import sys +from setuptools.command.build_ext import ( # noqa: N812 + build_ext as BaseBuildExtCommand) def count_down_delay(delay): @@ -32,6 +34,14 @@ def setup(*args, **kwargs): raise +def get_numpy_incpath(): + from imp import find_module + # avoid actually importing numpy, it screws up distutils + file, pathname, descr = find_module("numpy") + from os.path import join + return join(pathname, "core", "include") + + class NumpyExtension(Extension): # nicked from # http://mail.python.org/pipermail/distutils-sig/2007-September/008253.html @@ -41,15 +51,8 @@ class NumpyExtension(Extension): self._include_dirs = self.include_dirs del self.include_dirs # restore overwritten property - def get_numpy_incpath(self): - from imp import find_module - # avoid actually importing numpy, it screws up distutils - file, pathname, descr = find_module("numpy") - from os.path import join - return join(pathname, "core", "include") - def get_additional_include_dirs(self): - return [self.get_numpy_incpath()] + return [get_numpy_incpath()] def get_include_dirs(self): return self._include_dirs + self.get_additional_include_dirs() @@ -63,35 +66,44 @@ class NumpyExtension(Extension): include_dirs = property(get_include_dirs, set_include_dirs, del_include_dirs) -class PyUblasExtension(NumpyExtension): - def get_module_include_path(self, name): - from pkg_resources import Requirement, resource_filename - return resource_filename(Requirement.parse(name), "%s/include" % name) +class ExtensionUsingNumpy(Extension): + """Unlike :class:`NumpyExtension`, this class does not require numpy to be + importable upon extension module creation, allowing ``setup_requires=["numpy"]`` + to work. On the other hand, it requires the use of:: - def get_additional_include_dirs(self): - return (NumpyExtension.get_additional_include_dirs(self) - + [self.get_module_include_path("pyublas")]) + setup(..., + cmdclass={'build_ext': NumpyBuildExtCommand}) + + or + setup(..., + cmdclass={'build_ext': PybindBuildExtCommand}) + """ + + +class NumpyBuildExtCommand(BaseBuildExtCommand): + def build_extension(self, extension): + # We add the numpy include dir right before building the + # extension, in order to avoid having to import numpy when + # the setup script is imported, which would prevent + # installation before manual installation of numpy. + if isinstance(extension, ExtensionUsingNumpy): + numpy_incpath = get_numpy_incpath() + if numpy_incpath not in extension.include_dirs: + extension.include_dirs.append(numpy_incpath) -class HedgeExtension(PyUblasExtension): - @property - def include_dirs(self): - return self._include_dirs + [ - self.get_numpy_incpath(), - self.get_module_include_path("pyublas"), - self.get_module_include_path("hedge"), - ] + super(NumpyBuildExtCommand, self).build_extension(extension) # {{{ tools -def flatten(list): +def flatten(lst): """For an iterable of sub-iterables, generate each member of each sub-iterable in turn, i.e. a flattened version of that super-iterable. Example: Turn [[a,b,c],[d,e,f]] into [a,b,c,d,e,f]. """ - for sublist in list: + for sublist in lst: for j in sublist: yield j @@ -504,6 +516,10 @@ class Libraries(StringListOption): help=help or ("Library names for %s (without lib or .so)" % (human_name or humanize(lib_name)))) +# }}} + + +# {{{ configure options for specific software class BoostLibraries(Libraries): def __init__(self, lib_base_name, default_lib_name=None): @@ -628,6 +644,10 @@ def make_boost_base_options(): help="The compiler with which Boost C++ was compiled, e.g. gcc43"), ] +# }}} + + +# {{{ configure frontend def configure_frontend(): from optparse import OptionParser @@ -679,6 +699,8 @@ def configure_frontend(): substitute(substs, "Makefile") +# }}} + def substitute(substitutions, fname): import re @@ -723,6 +745,8 @@ def substitute(substitutions, fname): chmod(fname, infile_stat_res.st_mode) +# {{{ git bits + def _run_git_command(cmd): git_error = None from subprocess import Popen, PIPE @@ -828,6 +852,10 @@ def check_git_submodules(): print("git submodules initialized successfully") print(DASH_SEPARATOR) +# }}} + + +# {{{ pybind11 def check_pybind11(): try: @@ -848,3 +876,83 @@ def check_pybind11(): from aksetup_helper import count_down_delay count_down_delay(delay=10) + + +# {{{ boilerplate from https://github.com/pybind/python_example/blob/2ed5a68759cd6ff5d2e5992a91f08616ef457b5c/setup.py # noqa + +class get_pybind_include(object): # noqa: N801 + """Helper class to determine the pybind11 include path + + The purpose of this class is to postpone importing pybind11 + until it is actually installed, so that the ``get_include()`` + method can be invoked. """ + + def __init__(self, user=False): + self.user = user + + def __str__(self): + import pybind11 + return pybind11.get_include(self.user) + + +# As of Python 3.6, CCompiler has a `has_flag` method. +# cf http://bugs.python.org/issue26689 +def has_flag(compiler, flagname): + """Return a boolean indicating whether a flag name is supported on + the specified compiler. + """ + import tempfile + with tempfile.NamedTemporaryFile('w', suffix='.cpp', delete=False) as f: + f.write('int main (int argc, char **argv) { return 0; }') + fname = f.name + try: + compiler.compile([fname], extra_postargs=[flagname]) + except setuptools.distutils.errors.CompileError: + return False + return True + + +def cpp_flag(compiler): + """Return the -std=c++[11/14] compiler flag. + + The c++14 is prefered over c++11 (when it is available). + """ + if has_flag(compiler, '-std=c++14'): + return '-std=c++14' + elif has_flag(compiler, '-std=c++11'): + return '-std=c++11' + else: + raise RuntimeError('Unsupported compiler -- at least C++11 support ' + 'is needed!') + + +class PybindBuildExtCommand(NumpyBuildExtCommand): + """A custom build extension for adding compiler-specific options.""" + c_opts = { + 'msvc': ['/EHsc'], + 'unix': [], + } + + if sys.platform == 'darwin': + c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7'] + + def build_extensions(self): + ct = self.compiler.compiler_type + opts = self.c_opts.get(ct, []) + if ct in ['unix', 'mingw32']: + opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) + opts.append(cpp_flag(self.compiler)) + if has_flag(self.compiler, '-fvisibility=hidden'): + opts.append('-fvisibility=hidden') + elif ct == 'msvc': + opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) + for ext in self.extensions: + ext.extra_compile_args = ext.extra_compile_args + opts + + super(PybindBuildExtCommand, self).build_extensions() + +# }}} + +# }}} + +# vim: foldmethod=marker diff --git a/setup.py b/setup.py index 354b542..eaab4dc 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,5 @@ #!/usr/bin/env python -import os -import sys -import setuptools -from setuptools import Extension -from setuptools.command.build_ext import build_ext - def get_config_schema(): from aksetup_helper import (ConfigSchema, StringListOption) @@ -18,98 +12,11 @@ def get_config_schema(): ]) -# {{{ pybind11 gunk - -class get_pybind_include(object): # noqa - """Helper class to determine the pybind11 include path - - The purpose of this class is to postpone importing pybind11 - until it is actually installed, so that the ``get_include()`` - method can be invoked. """ - - def __init__(self, user=False): - self.user = user - - def __str__(self): - import pybind11 - return pybind11.get_include(self.user) - - -ext_modules = [ - Extension( - 'python_example', - ['src/main.cpp'], - include_dirs=[ - # Path to pybind11 headers - get_pybind_include(), - get_pybind_include(user=True) - ], - language='c++' - ), -] - - -# As of Python 3.6, CCompiler has a `has_flag` method. -# cf http://bugs.python.org/issue26689 -def has_flag(compiler, flagname): - """Return a boolean indicating whether a flag name is supported on - the specified compiler. - """ - import tempfile - with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f: - f.write('int main (int argc, char **argv) { return 0; }') - try: - compiler.compile([f.name], extra_postargs=[flagname]) - except setuptools.distutils.errors.CompileError: - return False - return True - - -def cpp_flag(compiler): - """Return the -std=c++[11/14] compiler flag. - - The c++14 is prefered over c++11 (when it is available). - """ - if has_flag(compiler, '-std=c++14'): - return '-std=c++14' - elif has_flag(compiler, '-std=c++11'): - return '-std=c++11' - else: - raise RuntimeError('Unsupported compiler -- at least C++11 support ' - 'is needed!') - - -class BuildExt(build_ext): - """A custom build extension for adding compiler-specific options.""" - c_opts = { - 'msvc': ['/EHsc'], - 'unix': [], - } - - if sys.platform == 'darwin': - c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7'] - os.environ['CC'] = 'clang++' - - def build_extensions(self): - ct = self.compiler.compiler_type - opts = self.c_opts.get(ct, []) - if ct == 'unix': - opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) - opts.append(cpp_flag(self.compiler)) - if has_flag(self.compiler, '-fvisibility=hidden'): - opts.append('-fvisibility=hidden') - elif ct == 'msvc': - opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) - for ext in self.extensions: - ext.extra_compile_args = opts - build_ext.build_extensions(self) - -# }}} - - def main(): from aksetup_helper import (hack_distutils, - check_pybind11, get_config, setup, check_git_submodules) + check_pybind11, get_config, setup, check_git_submodules, + Extension, + get_pybind_include, PybindBuildExtCommand) check_pybind11() check_git_submodules() @@ -201,7 +108,7 @@ def main(): extra_link_args=conf["LDFLAGS"], ), ], - cmdclass={'build_ext': BuildExt}, + cmdclass={'build_ext': PybindBuildExtCommand}, zip_safe=False, ) -- GitLab From 9a60b75676f787ce6fa16517e6bc770f30081e1e Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 3 Jan 2019 11:01:42 -0500 Subject: [PATCH 2/5] Update pybind aksetup for Py2 compat --- aksetup_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aksetup_helper.py b/aksetup_helper.py index 0b53185..e86f4cd 100644 --- a/aksetup_helper.py +++ b/aksetup_helper.py @@ -92,7 +92,7 @@ class NumpyBuildExtCommand(BaseBuildExtCommand): if numpy_incpath not in extension.include_dirs: extension.include_dirs.append(numpy_incpath) - super(NumpyBuildExtCommand, self).build_extension(extension) + BaseBuildExtCommand.build_extension(self, extension) # {{{ tools @@ -878,7 +878,7 @@ def check_pybind11(): count_down_delay(delay=10) -# {{{ boilerplate from https://github.com/pybind/python_example/blob/2ed5a68759cd6ff5d2e5992a91f08616ef457b5c/setup.py # noqa +# {{{ (modified) boilerplate from https://github.com/pybind/python_example/blob/2ed5a68759cd6ff5d2e5992a91f08616ef457b5c/setup.py # noqa class get_pybind_include(object): # noqa: N801 """Helper class to determine the pybind11 include path @@ -949,7 +949,7 @@ class PybindBuildExtCommand(NumpyBuildExtCommand): for ext in self.extensions: ext.extra_compile_args = ext.extra_compile_args + opts - super(PybindBuildExtCommand, self).build_extensions() + NumpyBuildExtCommand.build_extensions(self) # }}} -- GitLab From b1c8507413ffa9c0b6650560fb59e53816aea042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kl=C3=B6ckner?= Date: Thu, 3 Jan 2019 17:09:39 +0100 Subject: [PATCH 3/5] Bump py3.5 -> py3.6 for CI --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35ef09a..0c6dd78 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,14 +13,14 @@ Python 2.7: junit: test/pytest.xml -Python 3.5: +Python 3.6: script: - - py_version=3.5 + - py_version=3.6 - EXTRA_INSTALL="numpy pybind11" - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - ". ./build-and-test-py-project.sh" tags: - - python3.5 + - python3.6 except: - tags artifacts: -- GitLab From 9f360f37403b419239bbc869f5ad5a13ead5f3c1 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 3 Jan 2019 16:40:34 -0500 Subject: [PATCH 4/5] Try to force C++ compilation to make -std= flag OK --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index eaab4dc..6108086 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,7 @@ def main(): define_macros=triangle_macros + tetgen_macros, extra_compile_args=conf["CXXFLAGS"], extra_link_args=conf["LDFLAGS"], + language="c++", ), ], cmdclass={'build_ext': PybindBuildExtCommand}, -- GitLab From c3ca162006071948d89f2ef5a08f340910d2d2d0 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner Date: Thu, 3 Jan 2019 16:47:56 -0500 Subject: [PATCH 5/5] Rename triangle source file to C++ to make -std= option OK --- setup.py | 3 +-- src/cpp/{triangle.c => triangle.cpp} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename src/cpp/{triangle.c => triangle.cpp} (100%) diff --git a/setup.py b/setup.py index 6108086..9a92461 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ def main(): "src/cpp/wrapper.cpp", "src/cpp/wrap_triangle.cpp", - "src/cpp/triangle.c", + "src/cpp/triangle.cpp", "src/cpp/wrap_tetgen.cpp", "src/cpp/tetgen.cpp", @@ -106,7 +106,6 @@ def main(): define_macros=triangle_macros + tetgen_macros, extra_compile_args=conf["CXXFLAGS"], extra_link_args=conf["LDFLAGS"], - language="c++", ), ], cmdclass={'build_ext': PybindBuildExtCommand}, diff --git a/src/cpp/triangle.c b/src/cpp/triangle.cpp similarity index 100% rename from src/cpp/triangle.c rename to src/cpp/triangle.cpp -- GitLab