diff --git a/skbuild/setuptools_wrap.py b/skbuild/setuptools_wrap.py index 54efdb367..e0478b031 100644 --- a/skbuild/setuptools_wrap.py +++ b/skbuild/setuptools_wrap.py @@ -11,10 +11,7 @@ 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 +from setuptools import setup as upstream_setup def move_arg(arg, a, b, newarg=None, f=lambda x: x, concatenate_value=False): @@ -122,25 +119,70 @@ def setup(*args, **kw): # the data files on the bottom would have been mapped to # "top.not_a_subpackage" instead of "top", proper -- had such a package been # specified. - package_prefixes = list(sorted( - ( - (package_dir[package].replace('.', '/'), package) - for package in packages - ), - key=lambda tup: len(tup[0]), - reverse=True - )) + def get_package_prefix(package, package_dir): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any). + + Modified from Python 2.7.12/3.5.2 + distutils.command.build_py:get_package_dir""" + + path = package.split('.') + + if not package_dir: + if path: + return os.path.join(*path) + else: + return '' + else: + tail = [] + while path: + try: + pdir = package_dir['.'.join(path)] + except KeyError: + tail.insert(0, path[-1]) + del path[-1] + else: + tail.insert(0, pdir) + return os.path.join(*tail) + else: + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + + if tail: + return os.path.join(*tail) + else: + return '' + + package_prefixes = [] + for package in packages: + prefix = (get_package_prefix(package, package_dir), package) + package_prefixes.append(prefix) + # Add the root (nameless) package + prefix = (get_package_prefix('', package_dir), '') + package_prefixes.append(prefix) + package_prefixes = sorted(package_prefixes, + key=lambda tup: len(tup[0]), + reverse=True) try: cmkr = cmaker.CMaker() cmkr.configure(cmake_args) cmkr.make(make_args) - except SKBuildError as e: + except SKBuildError as error: import traceback print("Traceback (most recent call last):") traceback.print_tb(sys.exc_info()[2]) print() - sys.exit(e) + sys.exit(error) _classify_files(cmkr.install(), package_data, package_prefixes, py_modules, scripts, new_scripts, data_files) @@ -148,7 +190,7 @@ def setup(*args, **kw): kw['package_data'] = package_data kw['package_dir'] = { package: os.path.join(cmaker.CMAKE_INSTALL_DIR, prefix) - for prefix, package in package_prefixes + for package, prefix in package_dir.items() } kw['py_modules'] = py_modules @@ -178,7 +220,7 @@ def setup(*args, **kw): def _classify_files(install_paths, package_data, package_prefixes, py_modules, - scripts, new_scripts, data_files): + scripts, new_scripts, data_files): install_root = os.path.join(os.getcwd(), cmaker.CMAKE_INSTALL_DIR) for path in install_paths: found_package = False @@ -194,7 +236,7 @@ def _classify_files(install_paths, package_data, package_prefixes, py_modules, " Project Root : {}\n" " Violating File: {}\n").format(install_root, test_path)) - # peel off the 'skbuild' prefix + # peel off the 'skbuild' prefix path = os.path.relpath(path, cmaker.CMAKE_INSTALL_DIR) # check to see if path is part of a package diff --git a/tests/test_root_package.py b/tests/test_root_package.py new file mode 100644 index 000000000..8a92f22f6 --- /dev/null +++ b/tests/test_root_package.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""test_root_package +---------------------------------- + +Ensure that skbuild supports the root package, i.e. the nameless top level +"package". +""" + +from . import project_setup_py_test + + +@project_setup_py_test(("unit", "root-package"), ["build"], clear_cache=True) +def test_hello_builds(): + pass diff --git a/tests/unit/root-package/CMakeLists.txt b/tests/unit/root-package/CMakeLists.txt new file mode 100644 index 000000000..f50a9a458 --- /dev/null +++ b/tests/unit/root-package/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.5.0) + +project(root-package) + +enable_testing() + +find_package(PythonInterp REQUIRED) +find_package(PythonLibs REQUIRED) +find_package(PythonExtensions REQUIRED) + +add_subdirectory(lib) diff --git a/tests/unit/root-package/lib/CMakeLists.txt b/tests/unit/root-package/lib/CMakeLists.txt new file mode 100644 index 000000000..84b2ece8c --- /dev/null +++ b/tests/unit/root-package/lib/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(_hello MODULE _hello.cxx) +python_extension_module(_hello) + +add_test(NAME hello + COMMAND ${PYTHON_EXECUTABLE} -m hello + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}) + +install(TARGETS _hello LIBRARY DESTINATION "lib") +install(FILES hello.py DESTINATION "lib") diff --git a/tests/unit/root-package/lib/_hello.cxx b/tests/unit/root-package/lib/_hello.cxx new file mode 100644 index 000000000..75e744b97 --- /dev/null +++ b/tests/unit/root-package/lib/_hello.cxx @@ -0,0 +1,65 @@ + +// Python includes +#include + +// STD includes +#include + +//----------------------------------------------------------------------------- +static PyObject *hello_example(PyObject *self, PyObject *args) +{ + // Unpack a string from the arguments + const char *strArg; + if (!PyArg_ParseTuple(args, "s", &strArg)) + return NULL; + + // Print message and return None + PySys_WriteStdout("Hello, %s! :)\n", strArg); + Py_RETURN_NONE; +} + +//----------------------------------------------------------------------------- +static PyObject *elevation_example(PyObject *self, PyObject *args) +{ + // Return an integer + return PyLong_FromLong(21463L); +} + +//----------------------------------------------------------------------------- +static PyMethodDef hello_methods[] = { + { + "hello", + hello_example, + METH_VARARGS, + "Prints back 'Hello ', for example example: hello.hello('you')" + }, + + { + "size", + elevation_example, + METH_VARARGS, + "Returns elevation of Nevado Sajama." + }, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +//----------------------------------------------------------------------------- +#if PY_MAJOR_VERSION < 3 +PyMODINIT_FUNC init_hello(void) +{ + (void) Py_InitModule("_hello", hello_methods); +} +#else /* PY_MAJOR_VERSION >= 3 */ +static struct PyModuleDef hello_module_def = { + PyModuleDef_HEAD_INIT, + "_hello", + "Internal \"_hello\" module", + -1, + hello_methods +}; + +PyMODINIT_FUNC PyInit__hello(void) +{ + return PyModule_Create(&hello_module_def); +} +#endif /* PY_MAJOR_VERSION >= 3 */ diff --git a/tests/unit/root-package/lib/hello.py b/tests/unit/root-package/lib/hello.py new file mode 100644 index 000000000..60c961bcd --- /dev/null +++ b/tests/unit/root-package/lib/hello.py @@ -0,0 +1 @@ +from ._hello import hello diff --git a/tests/unit/root-package/setup.py b/tests/unit/root-package/setup.py new file mode 100644 index 000000000..816cd8535 --- /dev/null +++ b/tests/unit/root-package/setup.py @@ -0,0 +1,11 @@ +from skbuild import setup + +setup( + name="root-package", + version="1.2.3", + description="a package that populates the root (nameless) package", + author='The scikit-build team', + license="MIT", + package_dir={'': 'lib'}, + py_modules=['hello'] +)