From 20b00d6e5736295ca90d1cc599bcd8ee5c1f8f49 Mon Sep 17 00:00:00 2001 From: Omar Padron Date: Tue, 19 Jul 2016 14:19:31 -0400 Subject: [PATCH 1/4] add setuptools dependency --- docs/usage.rst | 4 +- requirements.txt | 3 +- skbuild/cmaker.py | 113 ++++++++++++++++++++++++--------- skbuild/command/bdist.py | 15 +++++ skbuild/command/bdist_wheel.py | 12 ++++ skbuild/command/build.py | 12 +++- skbuild/command/clean.py | 14 +++- skbuild/command/install.py | 12 +++- skbuild/distutils_wrap.py | 29 ++++++--- 9 files changed, 162 insertions(+), 52 deletions(-) create mode 100644 skbuild/command/bdist.py create mode 100644 skbuild/command/bdist_wheel.py diff --git a/docs/usage.rst b/docs/usage.rst index cc70647db..9e748cd36 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -4,8 +4,8 @@ Usage To use scikit-build in a project:: - # in your project's setup.py file, instead of from distutils import setup - from skbuild.distutils_wrap import setup + # in your project's setup.py file, instead of from setuptools import setup + from skbuild import setup TODO (scikit-build developer): need to provide small, self-contained setup function calls for (at least) 2 use cases: when a CMakeLists.txt file already diff --git a/requirements.txt b/requirements.txt index e1e8d9138..cc11ef8b5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -wheel==0.29.0 \ No newline at end of file +wheel==0.29.0 +setuptools==22.0.5 diff --git a/skbuild/cmaker.py b/skbuild/cmaker.py index 65ba5b918..4575e8680 100644 --- a/skbuild/cmaker.py +++ b/skbuild/cmaker.py @@ -20,7 +20,6 @@ RE_FILE_INSTALL = re.compile( r"""[ \t]*file\(INSTALL DESTINATION "([^"]+)".*"([^"]+)"\).*""") - def pop_arg(arg, a, default=None): """Pops an arg(ument) from an argument list a and returns the new list and the value of the argument if present and a default otherwise. @@ -50,7 +49,6 @@ def _remove_cwd_prefix(path): return result - def _touch_init(folder): init = os.path.join(folder, "__init__.py") if not os.path.exists(init): @@ -70,8 +68,7 @@ def __init__(self, **defines): self.platform = get_platform() def configure(self, clargs=(), generator_id=None): - """Calls cmake to generate the makefile (or VS solution, - or XCode project). + """Calls cmake to generate the Makefile/VS Solution/XCode project. Input: ------ @@ -110,17 +107,38 @@ def configure(self, clargs=(), generator_id=None): python_include_dir = CMaker.get_python_include_dir(python_version) python_library = CMaker.get_python_library(python_version) - cmd = ['cmake', os.getcwd(), '-G', generator_id, - '-DCMAKE_INSTALL_PREFIX:PATH={0}'.format( - os.path.join(os.getcwd(), CMAKE_INSTALL_DIR)), - '-DPYTHON_EXECUTABLE:FILEPATH=' + sys.executable, - '-DPYTHON_VERSION_STRING:STRING=' + sys.version.split(' ')[0], - '-DPYTHON_INCLUDE_DIR:PATH=' + python_include_dir, - '-DPYTHON_LIBRARY:FILEPATH=' + python_library, - '-DSKBUILD:BOOL=TRUE', - "-DCMAKE_MODULE_PATH:PATH={}".format( - os.path.dirname(__file__) + '/resources/cmake') - ] + cwd = os.getcwd() + cmd = ['cmake', cwd, '-G', generator_id] + cmd.extend( + "=".join((cmake_variable, value)) for cmake_variable, value in ( + ( + "-DCMAKE_INSTALL_PREFIX:PATH", + os.path.join(cwd, CMAKE_INSTALL_DIR) + ), ( + "-DPYTHON_EXECUTABLE:FILEPATH", + sys.executable + ), ( + "-DPYTHON_VERSION_STRING:STRING", + sys.version.split(' ')[0] + ), ( + "-DPYTHON_INCLUDE_DIR:PATH", + python_include_dir + ), ( + "-DPYTHON_LIBRARY:FILEPATH", + python_library + ), ( + "-DSKBUILD:BOOL", + "TRUE" + ), ( + "-DCMAKE_MODULE_PATH:PATH", + os.path.join( + os.path.dirname(__file__), + "resources", + "cmake" + ) + ) + ) + ) cmd.extend(clargs) @@ -158,20 +176,52 @@ def get_python_include_dir(python_version): # if Python.h not found (or python_include_dir is None), try to find a # suitable include dir - found_python_h = os.path.exists( - os.path.join(python_include_dir, 'Python.h')) - - if python_include_dir is None or not found_python_h: + found_python_h = ( + python_include_dir is not None or + os.path.exists(os.path.join(python_include_dir, 'Python.h')) + ) - candidate_prefixes = [ - os.path.dirname(sysconfig.get_config_var('INCLUDEPY')), - sysconfig.get_config_var('INCLUDEDIR'), - os.path.dirname(sysconfig.get_path('include')), - os.path.dirname(sysconfig.get_path('platinclude')), - os.path.join(sysconfig.get_python_inc(), - ".".join(map(str, sys.version_info[:2]))), - sysconfig.get_python_inc() - ] + if not found_python_h: + candidate_prefixes = [] + + # NOTE(opadron): these possible prefixes must be guarded against + # AttributeErrors and KeyErrors because they each can throw on + # different platforms or even different builds on the same platform. + try: + candidate_prefixes.append( + os.path.dirname(sysconfig.get_config_var('INCLUDEPY'))) + except (AttributeError, KeyError): + pass + + try: + candidate_prefixes.append( + sysconfig.get_config_var('INCLUDEDIR')) + except (AttributeError, KeyError): + pass + + try: + candidate_prefixes.append( + os.path.dirname(sysconfig.get_path('include'))) + except (AttributeError, KeyError): + pass + + try: + candidate_prefixes.append( + os.path.dirname(sysconfig.get_path('platinclude'))) + except (AttributeError, KeyError): + pass + + try: + candidate_prefixes.append( + os.path.join(sysconfig.get_python_inc(), + ".".join(map(str, sys.version_info[:2])))) + except (AttributeError, KeyError): + pass + + try: + candidate_prefixes.append(sysconfig.get_python_inc()) + except (AttributeError, KeyError): + pass candidate_prefixes = tuple(filter(bool, candidate_prefixes)) @@ -204,7 +254,7 @@ def get_python_library(python_version): # if static (or nonexistent), try to find a suitable dynamic libpython if (python_library is None or - os.path.splitext(python_library)[1][-2:] == '.a'): + os.path.splitext(python_library)[1][-2:] == '.a'): candidate_lib_prefixes = ['', 'lib'] @@ -322,7 +372,7 @@ def make(self, clargs=(), config="Release", source_dir="."): def install(self): """Returns a list of tuples of (install location, file list) to install - via distutils that is compatible with the data_files keyword argument. + via setuptools that is compatible with the data_files keyword argument. """ return self._parse_manifest() @@ -331,3 +381,6 @@ def _parse_manifest(self): "install_manifest.txt") with open(install_manifest_path, "r") as manifest: return [_remove_cwd_prefix(path) for path in manifest] + + return [] + diff --git a/skbuild/command/bdist.py b/skbuild/command/bdist.py new file mode 100644 index 000000000..b3e4432e4 --- /dev/null +++ b/skbuild/command/bdist.py @@ -0,0 +1,15 @@ + +try: + from setuptools.command.bdist import bdist as _bdist +except ImportError: + from distutils.command.bdist import bdist as _bdist + +class bdist(_bdist): + def finalize_options(self): + try: + if not self.build_base or self.build_base == 'build': + self.build_base = cmaker.DISTUTILS_INSTALL_DIR + except AttributeError: + pass + _bdist.finalize_options(self) + diff --git a/skbuild/command/bdist_wheel.py b/skbuild/command/bdist_wheel.py new file mode 100644 index 000000000..6e449f5a3 --- /dev/null +++ b/skbuild/command/bdist_wheel.py @@ -0,0 +1,12 @@ + +from wheel.bdist_wheel import bdist_wheel as _bdist_wheel + +class bdist_wheel(_bdist_wheel): + def finalize_options(self): + try: + if not self.build_base or self.build_base == 'build': + self.build_base = cmaker.DISTUTILS_INSTALL_DIR + except AttributeError: + pass + _bdist_wheel.finalize_options(self) + diff --git a/skbuild/command/build.py b/skbuild/command/build.py index 213cd28f1..69c7ff39b 100644 --- a/skbuild/command/build.py +++ b/skbuild/command/build.py @@ -1,11 +1,17 @@ -from distutils.command.build import build as _build +try: + from setuptools.command.build import build as _build +except ImportError: + from distutils.command.build import build as _build from .. import cmaker class build(_build): def finalize_options(self): - if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + try: + if not self.build_base or self.build_base == 'build': + self.build_base = cmaker.DISTUTILS_INSTALL_DIR + except AttributeError: + pass _build.finalize_options(self) diff --git a/skbuild/command/clean.py b/skbuild/command/clean.py index 5326e7a47..0f73f7edc 100644 --- a/skbuild/command/clean.py +++ b/skbuild/command/clean.py @@ -1,7 +1,11 @@ +try: + from setuptools.command.clean import clean as _clean +except ImportError: + from distutils.command.clean import clean as _clean + from shutil import rmtree -from distutils.command.clean import clean as _clean from distutils import log from .. import cmaker @@ -9,8 +13,12 @@ class clean(_clean): def finalize_options(self): - if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + try: + if not self.build_base or self.build_base == 'build': + self.build_base = cmaker.DISTUTILS_INSTALL_DIR + except AttributeError: + pass + _clean.finalize_options(self) def run(self): diff --git a/skbuild/command/install.py b/skbuild/command/install.py index 522ce8790..f3a80faf3 100644 --- a/skbuild/command/install.py +++ b/skbuild/command/install.py @@ -1,11 +1,17 @@ -from distutils.command.install import install as _install +try: + from setuptools.command.install import install as _install +except ImportError: + from distutils.command.install import install as _install from .. import cmaker class install(_install): def finalize_options(self): - if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + try: + if not self.build_base or self.build_base == 'build': + self.build_base = cmaker.DISTUTILS_INSTALL_DIR + except AttributeError: + pass _install.finalize_options(self) diff --git a/skbuild/distutils_wrap.py b/skbuild/distutils_wrap.py index a35333e28..7c41283de 100644 --- a/skbuild/distutils_wrap.py +++ b/skbuild/distutils_wrap.py @@ -1,17 +1,20 @@ -"""This module provides functionality for wrapping key components of the -distutils infrastructure. +"""This module provides functionality for wrapping key infrastructure components +from distutils and setuptools. """ import os import os.path import sys import argparse -import distutils.core from . import cmaker -from .command import build, install, clean +from .command import build, install, clean, bdist, bdist_wheel from .exceptions import SKBuildError +try: + from setuptools import setup as upstream_setup +except ImportError: + from distutils.core import setup as upstream_setup def move_arg(arg, a, b, newarg=None, f=lambda x: x, concatenate_value=False): """Moves an argument from a list to b list, possibly giving it a new name @@ -59,10 +62,12 @@ def parse_args(): concatenate_value=True) dutils, cmake = move_arg('-G', dutils, cmake) dutils, make = move_arg('-j', dutils, make) - op = os.path - - def absappend(x): - return op.join(op.dirname(op.abspath(sys.argv[0])), x) + absappend = ( + lambda x: os.path.join( + os.path.dirname(os.path.abspath(sys.argv[0])), + x + ) + ) dutils, dutils = move_arg('--egg-base', dutils, dutils, f=absappend) @@ -70,7 +75,7 @@ def absappend(x): def setup(*args, **kw): - """This function wraps distutils.core.setup() so that we can run cmake, make, + """This function wraps setup() so that we can run cmake, make, CMake build, then proceed as usual with a distutils, appending the CMake-generated output as necessary. """ @@ -226,6 +231,10 @@ def setup(*args, **kw): cmdclass['build'] = cmdclass.get('build', build.build) cmdclass['install'] = cmdclass.get('install', install.install) cmdclass['clean'] = cmdclass.get('clean', clean.clean) + cmdclass['bdist'] = cmdclass.get('bdist', bdist.bdist) + cmdclass['bdist_wheel'] = cmdclass.get( + 'bdist_wheel', bdist_wheel.bdist_wheel) kw['cmdclass'] = cmdclass - return distutils.core.setup(*args, **kw) + return upstream_setup(*args, **kw) + From 9b1e202322f7bfc8b2b01e3590098b7e032ea6c1 Mon Sep 17 00:00:00 2001 From: Omar Padron Date: Tue, 19 Jul 2016 14:29:37 -0400 Subject: [PATCH 2/4] rename distutils -> setuptools in several places --- README.rst | 2 +- skbuild/__init__.py | 3 ++- skbuild/cmaker.py | 6 +++--- skbuild/command/bdist.py | 2 +- skbuild/command/bdist_wheel.py | 2 +- skbuild/command/build.py | 2 +- skbuild/command/clean.py | 2 +- skbuild/command/install.py | 2 +- skbuild/{distutils_wrap.py => setuptools_wrap.py} | 2 +- 9 files changed, 12 insertions(+), 11 deletions(-) rename skbuild/{distutils_wrap.py => setuptools_wrap.py} (99%) diff --git a/README.rst b/README.rst index 56a0d9f50..04f7c257e 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ Improved build system generator for CPython C extensions. Better support is available for additional compilers, build systems, cross compilation, and locating dependencies and determining their build requirements. The **scikit-build** package is fundamentally just glue between -the `distutils` Python module and `CMake `_. Currently, +the `setuptools` Python module and `CMake `_. Currently, the package is available to perform builds in a `setup.py` file. In the future, the project aims to be a build tool option in the `currently developing pyproject.toml build system specification diff --git a/skbuild/__init__.py b/skbuild/__init__.py index aef4edb5c..4bf85042b 100644 --- a/skbuild/__init__.py +++ b/skbuild/__init__.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from .distutils_wrap import setup # noqa: F401: UnusedImport +from .setuptools_wrap import setup # noqa: F401: UnusedImport __author__ = 'The scikit-build team' __email__ = 'scikit-build@googlegroups.com' __version__ = '0.2.0' + diff --git a/skbuild/cmaker.py b/skbuild/cmaker.py index 4575e8680..3df3e1c59 100644 --- a/skbuild/cmaker.py +++ b/skbuild/cmaker.py @@ -15,7 +15,7 @@ SKBUILD_DIR = "_skbuild" CMAKE_BUILD_DIR = os.path.join(SKBUILD_DIR, "cmake-build") CMAKE_INSTALL_DIR = os.path.join(SKBUILD_DIR, "cmake-install") -DISTUTILS_INSTALL_DIR = os.path.join(SKBUILD_DIR, "distutils") +SETUPTOOLS_INSTALL_DIR = os.path.join(SKBUILD_DIR, "setuptools") RE_FILE_INSTALL = re.compile( r"""[ \t]*file\(INSTALL DESTINATION "([^"]+)".*"([^"]+)"\).*""") @@ -100,8 +100,8 @@ def configure(self, clargs=(), generator_id=None): if not os.path.exists(CMAKE_INSTALL_DIR): os.makedirs(CMAKE_INSTALL_DIR) - if not os.path.exists(DISTUTILS_INSTALL_DIR): - os.makedirs(DISTUTILS_INSTALL_DIR) + if not os.path.exists(SETUPTOOLS_INSTALL_DIR): + os.makedirs(SETUPTOOLS_INSTALL_DIR) python_version = CMaker.get_python_version() python_include_dir = CMaker.get_python_include_dir(python_version) diff --git a/skbuild/command/bdist.py b/skbuild/command/bdist.py index b3e4432e4..02188ad40 100644 --- a/skbuild/command/bdist.py +++ b/skbuild/command/bdist.py @@ -8,7 +8,7 @@ class bdist(_bdist): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR except AttributeError: pass _bdist.finalize_options(self) diff --git a/skbuild/command/bdist_wheel.py b/skbuild/command/bdist_wheel.py index 6e449f5a3..0f73125b4 100644 --- a/skbuild/command/bdist_wheel.py +++ b/skbuild/command/bdist_wheel.py @@ -5,7 +5,7 @@ class bdist_wheel(_bdist_wheel): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR except AttributeError: pass _bdist_wheel.finalize_options(self) diff --git a/skbuild/command/build.py b/skbuild/command/build.py index 69c7ff39b..b00c5933f 100644 --- a/skbuild/command/build.py +++ b/skbuild/command/build.py @@ -11,7 +11,7 @@ class build(_build): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR except AttributeError: pass _build.finalize_options(self) diff --git a/skbuild/command/clean.py b/skbuild/command/clean.py index 0f73f7edc..f16121586 100644 --- a/skbuild/command/clean.py +++ b/skbuild/command/clean.py @@ -15,7 +15,7 @@ class clean(_clean): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR except AttributeError: pass diff --git a/skbuild/command/install.py b/skbuild/command/install.py index f3a80faf3..d07cf1b3b 100644 --- a/skbuild/command/install.py +++ b/skbuild/command/install.py @@ -11,7 +11,7 @@ class install(_install): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.DISTUTILS_INSTALL_DIR + self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR except AttributeError: pass _install.finalize_options(self) diff --git a/skbuild/distutils_wrap.py b/skbuild/setuptools_wrap.py similarity index 99% rename from skbuild/distutils_wrap.py rename to skbuild/setuptools_wrap.py index 7c41283de..aa705900f 100644 --- a/skbuild/distutils_wrap.py +++ b/skbuild/setuptools_wrap.py @@ -76,7 +76,7 @@ def parse_args(): def setup(*args, **kw): """This function wraps setup() so that we can run cmake, make, - CMake build, then proceed as usual with a distutils, appending the + CMake build, then proceed as usual with setuptools, appending the CMake-generated output as necessary. """ sys.argv, cmake_args, make_args = parse_args() From e551643be34c4f92eb046ca6d9f2fcc87f99c300 Mon Sep 17 00:00:00 2001 From: Omar Padron Date: Wed, 20 Jul 2016 10:50:53 -0400 Subject: [PATCH 3/4] refactor test code --- skbuild/__init__.py | 1 - skbuild/cmaker.py | 9 ++- skbuild/command/bdist.py | 6 +- skbuild/command/bdist_wheel.py | 6 +- skbuild/setuptools_wrap.py | 2 +- tests/__init__.py | 103 +++++++++++++++++++++++++ tests/exec_2.py | 3 + tests/exec_3.py | 3 + tests/test_hello.py | 49 ++++-------- tests/test_hello_cython.py | 49 ++++-------- tests/test_outside_project_root.py | 28 ++----- tests/test_pen2.py | 59 +++++--------- tests/test_skbuild_variable.py | 31 +++----- tests/test_tower_of_babel.py | 120 +++++++++++++++-------------- 14 files changed, 251 insertions(+), 218 deletions(-) create mode 100644 tests/exec_2.py create mode 100644 tests/exec_3.py diff --git a/skbuild/__init__.py b/skbuild/__init__.py index 4bf85042b..e2604cd80 100644 --- a/skbuild/__init__.py +++ b/skbuild/__init__.py @@ -5,4 +5,3 @@ __author__ = 'The scikit-build team' __email__ = 'scikit-build@googlegroups.com' __version__ = '0.2.0' - diff --git a/skbuild/cmaker.py b/skbuild/cmaker.py index 3df3e1c59..bfacdd89d 100644 --- a/skbuild/cmaker.py +++ b/skbuild/cmaker.py @@ -20,6 +20,7 @@ RE_FILE_INSTALL = re.compile( r"""[ \t]*file\(INSTALL DESTINATION "([^"]+)".*"([^"]+)"\).*""") + def pop_arg(arg, a, default=None): """Pops an arg(ument) from an argument list a and returns the new list and the value of the argument if present and a default otherwise. @@ -49,6 +50,7 @@ def _remove_cwd_prefix(path): return result + def _touch_init(folder): init = os.path.join(folder, "__init__.py") if not os.path.exists(init): @@ -169,7 +171,9 @@ def get_python_version(): return python_version - @staticmethod + # NOTE(opadron): The try-excepts raise the cyclomatic complexity, but we + # need them for this function. + @staticmethod # noqa: C901: Complexity def get_python_include_dir(python_version): # determine python include dir python_include_dir = sysconfig.get_config_var('INCLUDEPY') @@ -254,7 +258,7 @@ def get_python_library(python_version): # if static (or nonexistent), try to find a suitable dynamic libpython if (python_library is None or - os.path.splitext(python_library)[1][-2:] == '.a'): + os.path.splitext(python_library)[1][-2:] == '.a'): candidate_lib_prefixes = ['', 'lib'] @@ -383,4 +387,3 @@ def _parse_manifest(self): return [_remove_cwd_prefix(path) for path in manifest] return [] - diff --git a/skbuild/command/bdist.py b/skbuild/command/bdist.py index 02188ad40..cc9db3d8a 100644 --- a/skbuild/command/bdist.py +++ b/skbuild/command/bdist.py @@ -4,12 +4,14 @@ except ImportError: from distutils.command.bdist import bdist as _bdist +from ..cmaker import SETUPTOOLS_INSTALL_DIR + + class bdist(_bdist): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR + self.build_base = SETUPTOOLS_INSTALL_DIR except AttributeError: pass _bdist.finalize_options(self) - diff --git a/skbuild/command/bdist_wheel.py b/skbuild/command/bdist_wheel.py index 0f73125b4..122c31535 100644 --- a/skbuild/command/bdist_wheel.py +++ b/skbuild/command/bdist_wheel.py @@ -1,12 +1,14 @@ from wheel.bdist_wheel import bdist_wheel as _bdist_wheel +from ..cmaker import SETUPTOOLS_INSTALL_DIR + + class bdist_wheel(_bdist_wheel): def finalize_options(self): try: if not self.build_base or self.build_base == 'build': - self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR + self.build_base = SETUPTOOLS_INSTALL_DIR except AttributeError: pass _bdist_wheel.finalize_options(self) - diff --git a/skbuild/setuptools_wrap.py b/skbuild/setuptools_wrap.py index aa705900f..e05fd75ef 100644 --- a/skbuild/setuptools_wrap.py +++ b/skbuild/setuptools_wrap.py @@ -16,6 +16,7 @@ except ImportError: from distutils.core import setup as upstream_setup + def move_arg(arg, a, b, newarg=None, f=lambda x: x, concatenate_value=False): """Moves an argument from a list to b list, possibly giving it a new name and/or performing a transformation on the value. Returns a and b. The arg @@ -237,4 +238,3 @@ def setup(*args, **kw): kw['cmdclass'] = cmdclass return upstream_setup(*args, **kw) - diff --git a/tests/__init__.py b/tests/__init__.py index 40a96afc6..4fc7e0986 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1,104 @@ # -*- coding: utf-8 -*- + +import inspect +import os +import os.path +import shutil +import sys + +from contextlib import contextmanager +from types import FunctionType + +from skbuild.cmaker import SKBUILD_DIR + +PYTHON2 = (sys.version_info < (3, 0)) + +# NOTE(opadron): we need to use separate files for the Python 2 and 3 cases +# because there's no way to write code that executes a file that parses +# successfully for both versions. +if PYTHON2: + from .exec_2 import execute +else: + from .exec_3 import execute + + +@contextmanager +def push_dir(dir): + old_cwd = os.getcwd() + os.chdir(dir) + yield + os.chdir(old_cwd) + + +@contextmanager +def push_argv(argv): + old_argv = sys.argv + sys.argv = argv + yield + sys.argv = old_argv + + +def _noop(): + pass + + +class project_test(): + def __init__(self, project): + self.project = project + + def __call__(self, func=_noop): + def result(*args, **kwargs): + dir = list(self.project) + dir.insert(0, os.path.dirname(os.path.abspath(__file__))) + dir = os.path.join(*dir) + + with push_dir(dir): + result2 = func(*args, **kwargs) + + return result2 + + return FunctionType( + result.__code__, + result.__globals__, + func.__name__, + result.__defaults__, + result.__closure__ + ) + + +class project_setup_py_test(): + def __init__(self, project, setup_args, clear_cache=False): + self.project = project + self.setup_args = setup_args + self.clear_cache = clear_cache + + f = inspect.currentframe().f_back + self.locals = f.f_locals + self.globals = f.f_globals + + def __call__(self, func=_noop): + @project_test(self.project) + def result(*args, **kwargs): + argv = ["setup.py"] + self.setup_args + with push_argv(argv): + if self.clear_cache and os.path.exists(SKBUILD_DIR): + shutil.rmtree(SKBUILD_DIR) + + setup_code = None + with open("setup.py", "r") as fp: + setup_code = compile(fp.read(), "setup.py", mode="exec") + + if setup_code is not None: + execute(setup_code, self.globals, self.locals) + + result2 = func(*args, **kwargs) + + return result2 + + return FunctionType( + result.__code__, + result.__globals__, + func.__name__, + result.__defaults__, + result.__closure__ + ) diff --git a/tests/exec_2.py b/tests/exec_2.py new file mode 100644 index 000000000..4e50db82a --- /dev/null +++ b/tests/exec_2.py @@ -0,0 +1,3 @@ + +def execute(code, _globals, _locals): + exec code in _globals, _locals # flake8: noqa: E901: SyntaxError diff --git a/tests/exec_3.py b/tests/exec_3.py new file mode 100644 index 000000000..9a24f3869 --- /dev/null +++ b/tests/exec_3.py @@ -0,0 +1,3 @@ + +def execute(code, _globals, _locals): + exec(code, _globals, _locals) diff --git a/tests/test_hello.py b/tests/test_hello.py index 3a1d93c9b..f5f1018cd 100644 --- a/tests/test_hello.py +++ b/tests/test_hello.py @@ -7,42 +7,19 @@ Tries to build and test the `hello` sample project. """ -import os -import os.path -import shutil -import subprocess -import sys - -from skbuild.cmaker import SKBUILD_DIR, CMAKE_BUILD_DIR +from . import project_setup_py_test +@project_setup_py_test(("samples", "hello"), ["build"], clear_cache=True) def test_hello_builds(): - old_argv = sys.argv - old_cwd = os.getcwd() - - sys.argv = ["setup.py", "build"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "hello")) - - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) - - try: - with open("setup.py", "r") as fp: - exec(fp.read()) - finally: - os.chdir(old_cwd) - sys.argv = old_argv - - -def test_hello_works(): - old_cwd = os.getcwd() - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "hello", CMAKE_BUILD_DIR)) - try: - subprocess.check_call( - ["ctest", "--build-config", - os.environ.get("SKBUILD_CMAKE_CONFIG", "Debug"), - "--output-on-failure"]) - finally: - os.chdir(old_cwd) + pass + + +# @project_setup_py_test(("samples", "hello"), ["test"]) +# def test_hello_works(): +# pass + + +@project_setup_py_test(("samples", "hello"), ["bdist_wheel"]) +def test_hello_wheel(): + pass diff --git a/tests/test_hello_cython.py b/tests/test_hello_cython.py index c23da2724..1d5d4da0c 100644 --- a/tests/test_hello_cython.py +++ b/tests/test_hello_cython.py @@ -7,42 +7,19 @@ Tries to build and test the `hello-cython` sample project. """ -import os -import os.path -import shutil -import subprocess -import sys - -from skbuild.cmaker import SKBUILD_DIR, CMAKE_BUILD_DIR +from . import project_setup_py_test +@project_setup_py_test(("samples", "hello-cython"), ["build"], clear_cache=True) def test_hello_cython_builds(): - old_argv = sys.argv - old_cwd = os.getcwd() - - sys.argv = ["setup.py", "build"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "hello-cython")) - - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) - - try: - with open("setup.py", "r") as fp: - exec(fp.read()) - finally: - os.chdir(old_cwd) - sys.argv = old_argv - - -def test_hello_cython_works(): - old_cwd = os.getcwd() - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "hello-cython", CMAKE_BUILD_DIR)) - try: - subprocess.check_call( - ["ctest", "--build-config", - os.environ.get("SKBUILD_CMAKE_CONFIG", "Debug"), - "--output-on-failure"]) - finally: - os.chdir(old_cwd) + pass + + +# @project_setup_py_test(("samples", "hello-cython"), ["test"]) +# def test_hello_cython_works(): +# pass + + +@project_setup_py_test(("samples", "hello-cython"), ["bdist_wheel"]) +def test_hello_cython_wheel(): + pass diff --git a/tests/test_outside_project_root.py b/tests/test_outside_project_root.py index 873d9a169..d99ae12b8 100644 --- a/tests/test_outside_project_root.py +++ b/tests/test_outside_project_root.py @@ -8,34 +8,22 @@ attempt fails with an SKBuildError exception. """ -import os -import os.path -import shutil -import sys - from skbuild.exceptions import SKBuildError -from skbuild.cmaker import SKBUILD_DIR - -def test_outside_project_root_installs(): - old_argv = sys.argv - old_cwd = os.getcwd() +from . import project_setup_py_test - sys.argv = ["setup.py", "install"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "fail-outside-project-root")) - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) +def test_outside_project_root_fails(): + @project_setup_py_test(("samples", "fail-outside-project-root"), + ["install"], + clear_cache=True) + def should_fail(): + pass exception_thrown = False try: - with open("setup.py", "r") as fp: - exec(fp.read()) + should_fail() except SKBuildError: exception_thrown = True - finally: - os.chdir(old_cwd) - sys.argv = old_argv assert exception_thrown diff --git a/tests/test_pen2.py b/tests/test_pen2.py index 3dcbfdbb9..22f03d24d 100644 --- a/tests/test_pen2.py +++ b/tests/test_pen2.py @@ -7,42 +7,23 @@ Tries to build and test the `pen2` sample project. """ -import os -import os.path -import shutil -import subprocess -import sys - -from skbuild.cmaker import SKBUILD_DIR, CMAKE_BUILD_DIR - - -def test_pen2_builds(): - old_argv = sys.argv - old_cwd = os.getcwd() - - sys.argv = ["setup.py", "build"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "pen2-cython")) - - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) - - try: - with open("setup.py", "r") as fp: - exec(fp.read()) - finally: - os.chdir(old_cwd) - sys.argv = old_argv - - -def test_pen2_works(): - old_cwd = os.getcwd() - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "pen2-cython", CMAKE_BUILD_DIR)) - try: - subprocess.check_call( - ["ctest", "-V", "--build-config", - os.environ.get("SKBUILD_CMAKE_CONFIG", "Debug"), - "--output-on-failure"]) - finally: - os.chdir(old_cwd) +# TODO(opadron): move this to scikit-build-sample-projects + +# from . import project_setup_py_test + + +# @project_setup_py_test(("samples", "pen2-cython"), +# ["build"], +# clear_cache=True) +# def test_pen2_builds(): +# pass + + +# @project_setup_py_test(("samples", "pen2-cython"), ["test"]) +# def test_pen2_works(): +# pass + + +# @project_setup_py_test(("samples", "pen2-cython"), ["bdist_wheel"]) +# def test_pen2_wheel(): +# pass diff --git a/tests/test_skbuild_variable.py b/tests/test_skbuild_variable.py index 791a671fc..1dab1bb54 100644 --- a/tests/test_skbuild_variable.py +++ b/tests/test_skbuild_variable.py @@ -8,28 +8,21 @@ "SKBUILD" must be set in order for the build to succeed. """ -import os -import os.path -import shutil -import sys +from . import project_setup_py_test -from skbuild.cmaker import SKBUILD_DIR +@project_setup_py_test(("samples", "fail-unless-skbuild-set"), + ["build"], + clear_cache=True) +def test_skbuild_variable_builds(): + pass -def test_fail_unless_skbuild_set_installs(): - old_argv = sys.argv - old_cwd = os.getcwd() - sys.argv = ["setup.py", "build"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "fail-unless-skbuild-set")) +# @project_setup_py_test(("samples", "fail-unless-skbuild-set"), ["test"]) +# def test_skbuild_variable_works(): +# pass - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) - try: - with open("setup.py", "r") as fp: - exec(fp.read()) - finally: - os.chdir(old_cwd) - sys.argv = old_argv +@project_setup_py_test(("samples", "fail-unless-skbuild-set"), ["bdist_wheel"]) +def test_skbuild_variable_wheel(): + pass diff --git a/tests/test_tower_of_babel.py b/tests/test_tower_of_babel.py index 0477ad54c..2e615bfd1 100644 --- a/tests/test_tower_of_babel.py +++ b/tests/test_tower_of_babel.py @@ -7,62 +7,64 @@ Tries to build and test the `tower-of-babel` sample project. """ -import os -import os.path -import shutil -import subprocess -import sys - -from skbuild.cmaker import SKBUILD_DIR, CMAKE_BUILD_DIR - - -def test_tbabel_builds(): - old_argv = sys.argv - old_cwd = os.getcwd() - - sys.argv = ["setup.py", "build"] - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir(os.path.join(cur_dir, "samples", "tower-of-babel")) - - if os.path.exists(SKBUILD_DIR): - shutil.rmtree(SKBUILD_DIR) - - try: - with open("setup.py", "r") as fp: - exec(fp.read()) - finally: - os.chdir(old_cwd) - sys.argv = old_argv - - -def test_tbabel_works(): - old_cwd = os.getcwd() - cur_dir = os.path.dirname(os.path.abspath(__file__)) - os.chdir( - os.path.join(cur_dir, "samples", "tower-of-babel", CMAKE_BUILD_DIR)) - - env = os.environ - pp = env.get("PYTHONPATH", []) - if pp: - pp = pp.split(os.pathsep) - - pyversion = "python" + ".".join(str(x) for x in sys.version_info[:2]) - pp.extend(( - os.path.join(sys.prefix, "lib", pyversion), - os.path.join(sys.prefix, "lib", pyversion, "site-packages"), - os.path.join(sys.prefix, "lib", pyversion, "lib-dynload"), - os.getcwd() - )) - - env["PYTHONPATH"] = os.pathsep.join(pp) - - env["PYTHONHOME"] = env.get("PYTHONHOME", sys.prefix) - - try: - subprocess.check_call( - ["ctest", "--build-config", - os.environ.get("SKBUILD_CMAKE_CONFIG", "Debug"), - "--output-on-failure"], - env=env) - finally: - os.chdir(old_cwd) +# TODO(opadron): move this to scikit-build-sample-projects + +# import os +# import os.path +# import shutil +# import subprocess +# import sys + +# from skbuild.cmaker import SKBUILD_DIR, CMAKE_BUILD_DIR + + +# def test_tbabel_builds(): +# old_argv = sys.argv +# old_cwd = os.getcwd() +# +# sys.argv = ["setup.py", "build"] +# cur_dir = os.path.dirname(os.path.abspath(__file__)) +# os.chdir(os.path.join(cur_dir, "samples", "tower-of-babel")) +# +# if os.path.exists(SKBUILD_DIR): +# shutil.rmtree(SKBUILD_DIR) +# +# try: +# with open("setup.py", "r") as fp: +# exec(fp.read()) +# finally: +# os.chdir(old_cwd) +# sys.argv = old_argv +# +# +# def test_tbabel_works(): +# old_cwd = os.getcwd() +# cur_dir = os.path.dirname(os.path.abspath(__file__)) +# os.chdir( +# os.path.join(cur_dir, "samples", "tower-of-babel", CMAKE_BUILD_DIR)) +# +# env = os.environ +# pp = env.get("PYTHONPATH", []) +# if pp: +# pp = pp.split(os.pathsep) +# +# pyversion = "python" + ".".join(str(x) for x in sys.version_info[:2]) +# pp.extend(( +# os.path.join(sys.prefix, "lib", pyversion), +# os.path.join(sys.prefix, "lib", pyversion, "site-packages"), +# os.path.join(sys.prefix, "lib", pyversion, "lib-dynload"), +# os.getcwd() +# )) +# +# env["PYTHONPATH"] = os.pathsep.join(pp) +# +# env["PYTHONHOME"] = env.get("PYTHONHOME", sys.prefix) +# +# try: +# subprocess.check_call( +# ["ctest", "--build-config", +# os.environ.get("SKBUILD_CMAKE_CONFIG", "Debug"), +# "--output-on-failure"], +# env=env) +# finally: +# os.chdir(old_cwd) From 82cb708b5bb2224808d271e9e415f595068434d3 Mon Sep 17 00:00:00 2001 From: Omar Padron Date: Thu, 21 Jul 2016 12:42:46 -0400 Subject: [PATCH 4/4] fix readability and style issues --- skbuild/cmaker.py | 102 +++++++++++++++++-------------------- skbuild/setuptools_wrap.py | 9 ++-- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/skbuild/cmaker.py b/skbuild/cmaker.py index bfacdd89d..9030c1eac 100644 --- a/skbuild/cmaker.py +++ b/skbuild/cmaker.py @@ -110,37 +110,23 @@ def configure(self, clargs=(), generator_id=None): python_library = CMaker.get_python_library(python_version) cwd = os.getcwd() - cmd = ['cmake', cwd, '-G', generator_id] - cmd.extend( - "=".join((cmake_variable, value)) for cmake_variable, value in ( - ( - "-DCMAKE_INSTALL_PREFIX:PATH", - os.path.join(cwd, CMAKE_INSTALL_DIR) - ), ( - "-DPYTHON_EXECUTABLE:FILEPATH", - sys.executable - ), ( - "-DPYTHON_VERSION_STRING:STRING", - sys.version.split(' ')[0] - ), ( - "-DPYTHON_INCLUDE_DIR:PATH", - python_include_dir - ), ( - "-DPYTHON_LIBRARY:FILEPATH", - python_library - ), ( - "-DSKBUILD:BOOL", - "TRUE" - ), ( - "-DCMAKE_MODULE_PATH:PATH", - os.path.join( - os.path.dirname(__file__), - "resources", - "cmake" - ) - ) - ) - ) + cmd = [ + 'cmake', cwd, '-G', generator_id, + ("-DCMAKE_INSTALL_PREFIX:PATH=" + + os.path.join(cwd, CMAKE_INSTALL_DIR)), + ("-DPYTHON_EXECUTABLE:FILEPATH=" + + sys.executable), + ("-DPYTHON_VERSION_STRING:STRING=" + + sys.version.split(' ')[0]), + ("-DPYTHON_INCLUDE_DIR:PATH=" + + python_include_dir), + ("-DPYTHON_LIBRARY:FILEPATH=" + + python_library), + ("-DSKBUILD:BOOL=" + + "TRUE"), + ("-DCMAKE_MODULE_PATH:PATH=" + + os.path.join(os.path.dirname(__file__), "resources", "cmake")) + ] cmd.extend(clargs) @@ -191,43 +177,46 @@ def get_python_include_dir(python_version): # NOTE(opadron): these possible prefixes must be guarded against # AttributeErrors and KeyErrors because they each can throw on # different platforms or even different builds on the same platform. - try: - candidate_prefixes.append( - os.path.dirname(sysconfig.get_config_var('INCLUDEPY'))) - except (AttributeError, KeyError): - pass + include_py = sysconfig.get_config_var('INCLUDEPY') + include_dir = sysconfig.get_config_var('INCLUDEDIR') + include = None + plat_include = None + python_inc = None + python_inc2 = None try: - candidate_prefixes.append( - sysconfig.get_config_var('INCLUDEDIR')) + include = sysconfig.get_path('include') except (AttributeError, KeyError): pass try: - candidate_prefixes.append( - os.path.dirname(sysconfig.get_path('include'))) + plat_include = sysconfig.get_path('platinclude') except (AttributeError, KeyError): pass try: - candidate_prefixes.append( - os.path.dirname(sysconfig.get_path('platinclude'))) - except (AttributeError, KeyError): + python_inc = sysconfig.get_python_inc() + except AttributeError: pass - try: - candidate_prefixes.append( - os.path.join(sysconfig.get_python_inc(), - ".".join(map(str, sys.version_info[:2])))) - except (AttributeError, KeyError): - pass - - try: - candidate_prefixes.append(sysconfig.get_python_inc()) - except (AttributeError, KeyError): - pass - - candidate_prefixes = tuple(filter(bool, candidate_prefixes)) + if include_py is not None: + include_py = os.path.dirname(include_py) + if include is not None: + include = os.path.dirname(include) + if plat_include is not None: + plat_include = os.path.dirname(plat_include) + if python_inc is not None: + python_inc2 = os.path.join( + python_inc, ".".join(map(str, sys.version_info[:2]))) + + candidate_prefixes = list(filter(bool, ( + include_py, + include_dir, + include, + plat_include, + python_inc, + python_inc2, + ))) candidate_versions = (python_version,) if python_version: @@ -248,6 +237,7 @@ def get_python_include_dir(python_version): break # TODO(opadron): what happens if we don't find an include directory? + # Throw SKBuildError? return python_include_dir diff --git a/skbuild/setuptools_wrap.py b/skbuild/setuptools_wrap.py index e05fd75ef..0fbd86faa 100644 --- a/skbuild/setuptools_wrap.py +++ b/skbuild/setuptools_wrap.py @@ -63,12 +63,9 @@ def parse_args(): concatenate_value=True) dutils, cmake = move_arg('-G', dutils, cmake) dutils, make = move_arg('-j', dutils, make) - absappend = ( - lambda x: os.path.join( - os.path.dirname(os.path.abspath(sys.argv[0])), - x - ) - ) + + def absappend(x): + return os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), x) dutils, dutils = move_arg('--egg-base', dutils, dutils, f=absappend)