Skip to content

Commit

Permalink
Merge pull request #83 from frostming/feature/build
Browse files Browse the repository at this point in the history
support build script
  • Loading branch information
frostming authored Mar 20, 2020
2 parents dd3a34e + 5f440c1 commit d044ebc
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 1 deletion.
26 changes: 26 additions & 0 deletions docs/docs/pyproject.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
```
1 change: 1 addition & 0 deletions news/23.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support specifying build script for C extensions.
13 changes: 13 additions & 0 deletions pdm/builders/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ def _find_files_iter(self, include_build: bool = False) -> Iterator[str]:
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):
Expand Down Expand Up @@ -229,6 +232,16 @@ def format_setup_py(self) -> str:
"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"]))
Expand Down
26 changes: 25 additions & 1 deletion pdm/builders/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import shutil
import stat
import subprocess
import tempfile
import zipfile
from base64 import urlsafe_b64encode
Expand Down Expand Up @@ -178,7 +179,30 @@ def _build(self, wheel):
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():
Expand Down

0 comments on commit d044ebc

Please sign in to comment.