diff --git a/skbuild/command/egg_info.py b/skbuild/command/egg_info.py index 3944aa3b..405d1525 100644 --- a/skbuild/command/egg_info.py +++ b/skbuild/command/egg_info.py @@ -2,6 +2,7 @@ import os from setuptools.command.egg_info import egg_info as _egg_info +from setuptools.command.egg_info import manifest_maker as _manifest_maker class egg_info(_egg_info): @@ -12,3 +13,25 @@ def finalize_options(self): self.egg_base = os.path.join(script_dir, self.egg_base) _egg_info.finalize_options(self) + + def find_sources(self): + """Generate SOURCES.txt manifest file""" + manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") + mm = manifest_maker(self.distribution) + mm.manifest = manifest_filename + mm.run() + self.filelist = mm.filelist + +class manifest_maker(_manifest_maker): + + def add_defaults(self): + import ptpdb; ptpdb.set_trace() + _manifest_maker.add_defaults(self) + old_include_package_data = self.distribution.include_package_data + # self.distribution.include_package_data = True + build_py = self.get_finalized_command('build_py') + df = build_py.data_files + for _, src_dir, _, filenames in build_py.data_files: + self.filelist.extend([os.path.join(src_dir, filename) + for filename in filenames]) + self.distribution.include_package_data = old_include_package_data diff --git a/skbuild/setuptools_wrap.py b/skbuild/setuptools_wrap.py index 9a45d509..a6fcc16a 100644 --- a/skbuild/setuptools_wrap.py +++ b/skbuild/setuptools_wrap.py @@ -11,10 +11,7 @@ from .command import build, install, clean, bdist, bdist_wheel, egg_info 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 create_skbuild_argparser(): @@ -153,25 +150,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) @@ -179,7 +221,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 @@ -194,6 +236,8 @@ def setup(*args, **kw): for parent_dir, file_set in data_files.items() ] + # kw['include_package_data'] = True + # work around https://bugs.python.org/issue1011113 # (patches provided, but no updates since 2014) cmdclass = kw.get('cmdclass', {}) @@ -203,6 +247,7 @@ def setup(*args, **kw): cmdclass['bdist'] = cmdclass.get('bdist', bdist.bdist) cmdclass['bdist_wheel'] = cmdclass.get( 'bdist_wheel', bdist_wheel.bdist_wheel) + #import ptpdb; ptpdb.set_trace() cmdclass['egg_info'] = cmdclass.get('egg_info', egg_info.egg_info) kw['cmdclass'] = cmdclass diff --git a/tests/test_root_package.py b/tests/test_root_package.py new file mode 100644 index 00000000..8a92f22f --- /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 00000000..f50a9a45 --- /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 00000000..20d8b894 --- /dev/null +++ b/tests/unit/root-package/lib/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(_spam MODULE _spam.c) +python_extension_module(_spam) + +add_test(NAME spam + COMMAND ${PYTHON_EXECUTABLE} -m spam + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}) + +install(TARGETS _spam LIBRARY DESTINATION "lib") +install(FILES spam.py DESTINATION "lib") diff --git a/tests/unit/root-package/lib/_spam.c b/tests/unit/root-package/lib/_spam.c new file mode 100644 index 00000000..f195bbf2 --- /dev/null +++ b/tests/unit/root-package/lib/_spam.c @@ -0,0 +1,41 @@ +#include + +static PyObject * +spam_system(PyObject *self, PyObject *args) +{ + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + return PyLong_FromLong(sts); +} + + +static PyMethodDef spam_methods[] = { + {"system", spam_system, METH_VARARGS, + "Execute a shell command."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +#if PY_MAJOR_VERSION < 3 +PyMODINIT_FUNC init_spam(void) +{ + (void) Py_InitModule("_spam", spam_methods); +} +#else /* PY_MAJOR_VERSION >= 3 */ +static struct PyModuleDef spam_module_def = { + PyModuleDef_HEAD_INIT, + "_spam", + "Internal \"_spam\" module", + -1, + spam_methods +}; + +PyMODINIT_FUNC PyInit__spam(void) +{ + return PyModule_Create(&spam_module_def); +} +#endif /* PY_MAJOR_VERSION >= 3 */ diff --git a/tests/unit/root-package/lib/spam.py b/tests/unit/root-package/lib/spam.py new file mode 100644 index 00000000..bd0b0ec6 --- /dev/null +++ b/tests/unit/root-package/lib/spam.py @@ -0,0 +1 @@ +from ._spam import spam # noqa: F401 diff --git a/tests/unit/root-package/setup.py b/tests/unit/root-package/setup.py new file mode 100644 index 00000000..e0fb8784 --- /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=['spam'] +)