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

Switch to setuptools #89

Merged
merged 4 commits into from Jul 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Expand Up @@ -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 <https://cmake.org/>`_. Currently,
the `setuptools` Python module and `CMake <https://cmake.org/>`_. 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
Expand Down
4 changes: 2 additions & 2 deletions docs/usage.rst
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
@@ -1 +1,2 @@
wheel==0.29.0
wheel==0.29.0
setuptools==22.0.5
2 changes: 1 addition & 1 deletion skbuild/__init__.py
@@ -1,6 +1,6 @@
# -*- 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'
Expand Down
112 changes: 79 additions & 33 deletions skbuild/cmaker.py
Expand Up @@ -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 "([^"]+)".*"([^"]+)"\).*""")
Expand Down Expand Up @@ -70,8 +70,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:
------
Expand Down Expand Up @@ -103,24 +102,31 @@ 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)
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,
("-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)

Expand Down Expand Up @@ -151,29 +157,66 @@ 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')

# 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:

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()
]
found_python_h = (
python_include_dir is not None or
os.path.exists(os.path.join(python_include_dir, 'Python.h'))
)

candidate_prefixes = tuple(filter(bool, candidate_prefixes))
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the documentation indicate None is returned, could you revert these changes ?

On Linux, no error:

$ python -c "import sysconfig; print(sysconfig.get_config_var('PYTHONPATH'))"
:plat-x86_64-linux-gnu:lib-tk:lib-old

$ python -c "import sysconfig; print(sysconfig.get_config_var('Invalid'))"
None

On MacOSx, no error:

$ python -c "import sysconfig; print(sysconfig.get_config_var('Invalid'))"
None
$ python -c "import sysconfig; print(sysconfig.get_config_var('PYTHONPATH'))"
:plat-darwin:plat-mac:plat-mac/lib-scriptpackages:../../Extras/lib/python:lib-tk:lib-old

On windows, I expect the same.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, that particular piece does not need to be guarded.

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:
include = sysconfig.get_path('include')
except (AttributeError, KeyError):
pass

try:
plat_include = sysconfig.get_path('platinclude')
except (AttributeError, KeyError):
pass

try:
python_inc = sysconfig.get_python_inc()
except AttributeError:
pass

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:
Expand All @@ -194,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

Expand Down Expand Up @@ -322,7 +366,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()

Expand All @@ -331,3 +375,5 @@ 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 []
17 changes: 17 additions & 0 deletions skbuild/command/bdist.py
@@ -0,0 +1,17 @@

try:
from setuptools.command.bdist import bdist as _bdist
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 = SETUPTOOLS_INSTALL_DIR
except AttributeError:
pass
_bdist.finalize_options(self)
14 changes: 14 additions & 0 deletions skbuild/command/bdist_wheel.py
@@ -0,0 +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 = SETUPTOOLS_INSTALL_DIR
except AttributeError:
pass
_bdist_wheel.finalize_options(self)
12 changes: 9 additions & 3 deletions 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.SETUPTOOLS_INSTALL_DIR
except AttributeError:
pass
_build.finalize_options(self)
14 changes: 11 additions & 3 deletions skbuild/command/clean.py
@@ -1,16 +1,24 @@

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


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.SETUPTOOLS_INSTALL_DIR
except AttributeError:
pass

_clean.finalize_options(self)

def run(self):
Expand Down
12 changes: 9 additions & 3 deletions 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.SETUPTOOLS_INSTALL_DIR
except AttributeError:
pass
_install.finalize_options(self)
24 changes: 15 additions & 9 deletions skbuild/distutils_wrap.py → skbuild/setuptools_wrap.py
@@ -1,17 +1,21 @@
"""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
Expand Down Expand Up @@ -59,19 +63,18 @@ 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)
return os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), x)

dutils, dutils = move_arg('--egg-base', dutils, dutils, f=absappend)

return dutils, cmake, make


def setup(*args, **kw):
"""This function wraps distutils.core.setup() so that we can run cmake, make,
CMake build, then proceed as usual with a distutils, appending the
"""This function wraps setup() so that we can run cmake, make,
CMake build, then proceed as usual with setuptools, appending the
CMake-generated output as necessary.
"""
sys.argv, cmake_args, make_args = parse_args()
Expand Down Expand Up @@ -226,6 +229,9 @@ 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)