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

Add build script without setup.py support #45

Merged
merged 2 commits into from
Jul 23, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion poetry/core/masonry/builders/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def build(self, target_dir=None): # type: (Path) -> Path
else:
tar.addfile(tar_info) # Symlinks & ?

if self._poetry.package.build_config.get("generate-setup-file", True):
if self._poetry.package.build_should_generate_setup():
setup = self.build_setup()
tar_info = tarfile.TarInfo(pjoin(tar_dir, "setup.py"))
tar_info.size = len(setup)
Expand Down
64 changes: 42 additions & 22 deletions poetry/core/masonry/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,13 @@ def build(self):
with zipfile.ZipFile(
os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED
) as zip_file:
self._copy_module(zip_file)
self._build(zip_file)
if not self._poetry.package.build_should_generate_setup():
self._build(zip_file)
self._copy_module(zip_file)
else:
self._copy_module(zip_file)
self._build(zip_file)

self._write_metadata(zip_file)
self._write_record(zip_file)

Expand All @@ -91,44 +96,59 @@ def build(self):

def _build(self, wheel):
if self._package.build_script:
with SdistBuilder(poetry=self._poetry).setup_py() as setup:
# We need to place ourselves in the temporary
# directory in order to build the package
if not self._poetry.package.build_should_generate_setup():
# Since we have a build script but no setup.py generation is required,
# we assume that the build script will build and copy the files
# directly.
abn marked this conversation as resolved.
Show resolved Hide resolved
# That way they will be picked up when adding files to the wheel.
current_path = os.getcwd()
try:
os.chdir(str(self._path))
self._run_build_command(setup)
self._run_build_script(self._package.build_script)
finally:
os.chdir(current_path)
else:
with SdistBuilder(poetry=self._poetry).setup_py() as setup:
# We need to place ourselves in the temporary
# directory in order to build the package
current_path = os.getcwd()
try:
os.chdir(str(self._path))
self._run_build_command(setup)
finally:
os.chdir(current_path)

build_dir = self._path / "build"
lib = list(build_dir.glob("lib.*"))
if not lib:
# The result of building the extensions
# does not exist, this may due to conditional
# builds, so we assume that it's okay
return
build_dir = self._path / "build"
lib = list(build_dir.glob("lib.*"))
if not lib:
# The result of building the extensions
# does not exist, this may due to conditional
# builds, so we assume that it's okay
return

lib = lib[0]
lib = lib[0]

for pkg in lib.glob("**/*"):
if pkg.is_dir() or self.is_excluded(pkg):
continue
for pkg in lib.glob("**/*"):
if pkg.is_dir() or self.is_excluded(pkg):
continue

rel_path = str(pkg.relative_to(lib))
rel_path = str(pkg.relative_to(lib))

if rel_path in wheel.namelist():
continue
if rel_path in wheel.namelist():
continue

logger.debug(" - Adding: {}".format(rel_path))
logger.debug(" - Adding: {}".format(rel_path))

self._add_file(wheel, pkg, rel_path)
self._add_file(wheel, pkg, rel_path)

def _run_build_command(self, setup):
subprocess.check_call(
[sys.executable, str(setup), "build", "-b", str(self._path / "build")]
)

def _run_build_script(self, build_script):
subprocess.check_call([sys.executable, build_script])

def _copy_module(self, wheel): # type: (zipfile.ZipFile) -> None
to_add = self.find_files_to_add()

Expand Down
3 changes: 3 additions & 0 deletions poetry/core/packages/project_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ def urls(self):
urls.update(self.custom_urls)

return urls

def build_should_generate_setup(self): # type: () -> bool
return self.build_config.get("generate-setup-file", True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Module 1
========
27 changes: 27 additions & 0 deletions tests/masonry/builders/fixtures/extended_with_no_setup/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import os
import shutil
from distutils.command.build_ext import build_ext
from distutils.core import Distribution, Extension

extensions = [Extension("extended.extended", ["extended/extended.c"])]


def build():
distribution = Distribution({"name": "extended", "ext_modules": extensions})
distribution.package_dir = "extended"

cmd = build_ext(distribution)
cmd.ensure_finalized()
cmd.run()

# Copy built extensions back to the project
for output in cmd.get_outputs():
relative_extension = os.path.relpath(output, cmd.build_lib)
shutil.copyfile(output, relative_extension)
mode = os.stat(relative_extension).st_mode
mode |= (mode & 0o444) >> 2
os.chmod(relative_extension, mode)


if __name__ == "__main__":
build()
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[tool.poetry]
name = "extended"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <[email protected]>"
]
license = "MIT"

readme = "README.rst"

homepage = "https://python-poetry.org/"

[tool.poetry.build]
script = "build.py"
generate-setup-file = false
57 changes: 57 additions & 0 deletions tests/masonry/builders/test_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,63 @@ def test_wheel_c_extension():
zip.close()


@pytest.mark.skipif(
sys.platform == "win32"
and sys.version_info <= (3, 6)
or platform.python_implementation().lower() == "pypy",
reason="Disable test on Windows for Python <=3.6 and for PyPy",
)
def test_wheel_c_extension_with_no_setup():
module_path = fixtures_dir / "extended_with_no_setup"
builder = CompleteBuilder(Factory().create_poetry(module_path))
builder.build()

sdist = fixtures_dir / "extended_with_no_setup" / "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/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]\\dm?u?-.+
$""".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:
zip.close()


@pytest.mark.skipif(
sys.platform == "win32"
and sys.version_info <= (3, 6)
Expand Down