From 5f440c157bf16f351a8e0abf63b1be206a6bd561 Mon Sep 17 00:00:00 2001 From: frostming Date: Fri, 20 Mar 2020 10:01:03 +0800 Subject: [PATCH] support build script --- docs/docs/pyproject.md | 26 ++++++++++++++++++++++++++ news/23.feature | 1 + pdm/builders/base.py | 13 +++++++++++++ pdm/builders/wheel.py | 26 +++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 news/23.feature diff --git a/docs/docs/pyproject.md b/docs/docs/pyproject.md index b8b671bf..9bd41130 100644 --- a/docs/docs/pyproject.md +++ b/docs/docs/pyproject.md @@ -94,3 +94,29 @@ format of `[tool.pdm.cli]` format: [tool.pdm.entry_points.pytest11] myplugin = "mypackage.plugin:pytest_plugin" ``` + +## Build C extensions + +Currently building C extensions still rely on `setuptools`. You should write a python script which contains +a function named `build` and accepts the arguments dictionary of `setup()` as the only parameter. +In the function, update the dictionary with your `ext_modules` settings. + +Here is an example taken from `MarkupSafe`: + +```python +# build.py +from setuptools import Extension + +ext_modules = [Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])] + +def build(setup_kwargs): + setup_kwargs.update(ext_modules=ext_modules) +``` + +Now, specify the build script path via `build` in the `pyproject.toml`: + +```toml +# pyproject.toml +[tool.pdm] +build = "build.py" +``` diff --git a/news/23.feature b/news/23.feature new file mode 100644 index 00000000..475b76a5 --- /dev/null +++ b/news/23.feature @@ -0,0 +1 @@ +Support specifying build script for C extensions. diff --git a/pdm/builders/base.py b/pdm/builders/base.py index 1fdbf43a..c2eef5d9 100644 --- a/pdm/builders/base.py +++ b/pdm/builders/base.py @@ -195,6 +195,9 @@ class Builder: if not include_build: return + if self.meta.build and os.path.isfile(self.meta.build): + yield self.meta.build + for pat in ("COPYING", "LICENSE"): for path in glob.glob(pat + "*"): if os.path.isfile(path): @@ -229,6 +232,16 @@ class Builder: "url": meta.homepage, } + if meta.build: + # The build script must contain a `build(setup_kwargs)`, we just import + # and execute it. + after.extend( + [ + "from {} import build\n".format(meta.build.split(".")[0]), + "build(setup_kwargs)\n", + ] + ) + package_paths = meta.convert_package_paths() if package_paths["packages"]: extra.append(" 'packages': {!r},\n".format(package_paths["packages"])) diff --git a/pdm/builders/wheel.py b/pdm/builders/wheel.py index 7deb73b3..cc124a76 100644 --- a/pdm/builders/wheel.py +++ b/pdm/builders/wheel.py @@ -4,6 +4,7 @@ import hashlib import os import shutil import stat +import subprocess import tempfile import zipfile from base64 import urlsafe_b64encode @@ -178,7 +179,30 @@ class WheelBuilder(Builder): if not self.meta.build: return self.ensure_setup_py() - # TODO: C extension build + setup_py = self.ireq.setup_py_path + build_args = [ + self.project.environment.python_executable, + setup_py, + "build", + "-b", + str(self.project.root / "build"), + ] + subprocess.run( + build_args, capture_output=stream.verbosity >= stream.DETAIL, check=True + ) + build_dir = self.project.root / "build" + lib_dir = next(build_dir.glob("lib.*"), None) + if not lib_dir: + return + for pkg in lib_dir.glob("**/*"): + if pkg.is_dir(): + continue + + rel_path = str(pkg.relative_to(lib_dir)) + + if rel_path in wheel.namelist(): + continue + self._add_file(wheel, pkg, rel_path) def _copy_module(self, wheel): for path in self.find_files_to_add(): -- GitLab