diff --git a/aksetup_helper.py b/aksetup_helper.py index 36422892cbf07f30736c2bd2d9733bf312545c6b..e86f4cd8e8e193d014462b57b4b0a79357558698 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"), - ] + BaseBuildExtCommand.build_extension(self, 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) + + +# {{{ (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 + + 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 + + NumpyBuildExtCommand.build_extensions(self) + +# }}} + +# }}} + +# vim: foldmethod=marker diff --git a/setup.py b/setup.py index 775141b0d91db62fafd716cb66e9124fa10e628f..a52f8299d99834946b4700113138dff4f202113c 100644 --- a/setup.py +++ b/setup.py @@ -22,9 +22,9 @@ def search_on_path(filenames): def get_config_schema(): - from aksetup_helper import ConfigSchema, Option, \ - IncludeDir, LibraryDir, Libraries, BoostLibraries, \ - Switch, StringListOption, make_boost_base_options + from aksetup_helper import (ConfigSchema, Option, + IncludeDir, LibraryDir, Libraries, BoostLibraries, + Switch, StringListOption, make_boost_base_options) nvcc_path = search_on_path(["nvcc", "nvcc.exe"]) if nvcc_path is None: @@ -102,8 +102,8 @@ def main(): import sys from aksetup_helper import (hack_distutils, get_config, setup, - NumpyExtension, set_up_shipped_boost_if_requested, - check_git_submodules) + ExtensionUsingNumpy, set_up_shipped_boost_if_requested, + check_git_submodules, NumpyBuildExtCommand) check_git_submodules() @@ -216,7 +216,7 @@ def main(): ext_package="pycuda", ext_modules=[ - NumpyExtension("_driver", + ExtensionUsingNumpy("_driver", [ "src/cpp/cuda.cpp", "src/cpp/bitlog.cpp", @@ -230,13 +230,13 @@ def main(): extra_compile_args=conf["CXXFLAGS"], extra_link_args=conf["LDFLAGS"], ), - NumpyExtension("_pvt_struct", + ExtensionUsingNumpy("_pvt_struct", [pvt_struct_source], extra_compile_args=conf["CXXFLAGS"], extra_link_args=conf["LDFLAGS"], ), ], - + cmdclass={'build_ext': NumpyBuildExtCommand}, include_package_data=True, package_data={ "pycuda": [