Commit e8e79736 by Sébastien Eustace

Fix building of wheels with extensions and src layout

parent 7911793e
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
- Fixed reading of some `setup.py` files. - Fixed reading of some `setup.py` files.
- Fixed a `KeyError` when getting information for packages which require reading setup files. - Fixed a `KeyError` when getting information for packages which require reading setup files.
- Fixed the building of wheels with C extensions and an `src` layout.
## [0.12.6] - 2018-11-05 ## [0.12.6] - 2018-11-05
......
...@@ -14,7 +14,6 @@ from io import StringIO ...@@ -14,7 +14,6 @@ from io import StringIO
from poetry.__version__ import __version__ from poetry.__version__ import __version__
from poetry.semver import parse_constraint from poetry.semver import parse_constraint
from poetry.utils._compat import Path
from ..utils.helpers import normalize_file_permissions from ..utils.helpers import normalize_file_permissions
from ..utils.package_include import PackageInclude from ..utils.package_include import PackageInclude
...@@ -67,8 +66,8 @@ class WheelBuilder(Builder): ...@@ -67,8 +66,8 @@ class WheelBuilder(Builder):
with zipfile.ZipFile( with zipfile.ZipFile(
os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED
) as zip_file: ) as zip_file:
self._build()
self._copy_module(zip_file) self._copy_module(zip_file)
self._build(zip_file)
self._write_metadata(zip_file) self._write_metadata(zip_file)
self._write_record(zip_file) self._write_record(zip_file)
...@@ -79,7 +78,7 @@ class WheelBuilder(Builder): ...@@ -79,7 +78,7 @@ class WheelBuilder(Builder):
self._io.writeln(" - Built <fg=cyan>{}</>".format(self.wheel_filename)) self._io.writeln(" - Built <fg=cyan>{}</>".format(self.wheel_filename))
def _build(self): def _build(self, wheel):
if self._package.build: if self._package.build:
setup = self._path / "setup.py" setup = self._path / "setup.py"
...@@ -103,9 +102,22 @@ class WheelBuilder(Builder): ...@@ -103,9 +102,22 @@ class WheelBuilder(Builder):
return return
lib = lib[0] lib = lib[0]
for pkg in lib.glob("*"): excluded = self.find_excluded_files()
shutil.rmtree(str(self._path / pkg.name)) for pkg in lib.glob("**/*"):
shutil.copytree(str(pkg), str(self._path / pkg.name)) if pkg.is_dir() or pkg in excluded:
continue
rel_path = str(pkg.relative_to(lib))
if rel_path in wheel.namelist():
continue
self._io.writeln(
" - Adding: <comment>{}</comment>".format(rel_path),
verbosity=self._io.VERBOSITY_VERY_VERBOSE,
)
self._add_file(wheel, pkg, rel_path)
def _copy_module(self, wheel): def _copy_module(self, wheel):
excluded = self.find_excluded_files() excluded = self.find_excluded_files()
......
from distutils.core import Extension
extensions = [Extension("extended.extended", ["src/extended/extended.c"])]
def build(setup_kwargs):
setup_kwargs.update({"ext_modules": extensions})
[tool.poetry]
name = "extended"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
build = "build.py"
#include <Python.h>
static PyObject *hello(PyObject *self) {
return PyUnicode_FromString("Hello");
}
static PyMethodDef module_methods[] = {
{
"hello",
(PyCFunction) hello,
NULL,
PyDoc_STR("Say hello.")
},
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"extended",
NULL,
-1,
module_methods,
NULL,
NULL,
NULL,
NULL,
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_extended(void)
#else
init_extended(void)
#endif
{
PyObject *module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule3("extended", module_methods, NULL);
#endif
if (module == NULL)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
...@@ -84,6 +84,67 @@ $""".format( ...@@ -84,6 +84,67 @@ $""".format(
) )
is not None is not None
) )
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended\..+\.(so|pyd)", records) is not None
finally:
zip.close()
@pytest.mark.skipif(
sys.platform == "win32" and sys.version_info <= (3, 4),
reason="Disable test on Windows for Python <=3.4",
)
def test_wheel_c_extension_src_layout():
module_path = fixtures_dir / "src_extended"
builder = CompleteBuilder(
Poetry.create(module_path), NullEnv(execute=True), NullIO()
)
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
whl = list((module_path / "dist").glob("extended-0.1-cp*-cp*-*.whl"))[0]
assert whl.exists()
zip = zipfile.ZipFile(str(whl))
has_compiled_extension = False
for name in zip.namelist():
if name.startswith("extended/extended") and name.endswith((".so", ".pyd")):
has_compiled_extension = True
assert has_compiled_extension
try:
wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL"))
assert (
re.match(
"""(?m)^\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: false
Tag: cp[23]\\d-cp[23]\\dmu?-.+
$""".format(
__version__
),
wheel_data,
)
is not None
)
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended\..+\.(so|pyd)", records) is not None
finally: finally:
zip.close() zip.close()
......
...@@ -309,6 +309,21 @@ def test_with_c_extensions(): ...@@ -309,6 +309,21 @@ def test_with_c_extensions():
assert "extended-0.1/extended/extended.c" in tar.getnames() assert "extended-0.1/extended/extended.c" in tar.getnames()
def test_with_c_extensions_src_layout():
poetry = Poetry.create(project("src_extended"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
def test_with_src_module_file(): def test_with_src_module_file():
poetry = Poetry.create(project("source_file")) poetry = Poetry.create(project("source_file"))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment