Skip to content

Commit d48ffea

Browse files
authored
Add custom build backend to support build args (#328)
This implements a custom build backend, inspired by the [solution used by Pillow.](python-pillow/Pillow#7171) install-option is deprecated and was removed with pip 23.1. The comonly accepted solution seems to be to define a custom build backend for now pypa/setuptools#2491 (comment) This commit changes the usage from `--install-option` to `--config-settings`.
1 parent db26d60 commit d48ffea

File tree

7 files changed

+74
-47
lines changed

7 files changed

+74
-47
lines changed

README.md

+13-18
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,27 @@ cd pycapnp
3838
pip install .
3939
```
4040

41-
If you wish to install using the latest upstream C++ Cap'n Proto:
41+
By default, the setup script will automatically use the locally installed Cap'n Proto.
42+
If Cap'n Proto is not installed, it will bundle and build the matching Cap'n Proto library.
43+
44+
To enforce bundling, the Cap'n Proto library:
4245

4346
```bash
44-
pip install \
45-
--install-option "--libcapnp-url" \
46-
--install-option "https://github.com/capnproto/capnproto/archive/master.tar.gz" \
47-
--install-option "--force-bundled-libcapnp" .
47+
pip install . -C force-bundled-libcapnp=True
4848
```
4949

50-
To force bundled python:
50+
If you wish to install using the latest upstream C++ Cap'n Proto:
5151

5252
```bash
53-
pip install --install-option "--force-bundled-libcapnp" .
53+
pip install . \
54+
-C force-bundled-libcapnp=True \
55+
-C libcapnp-url="https://github.com/capnproto/capnproto/archive/master.tar.gz"
5456
```
5557

56-
Slightly more prompt error messages using distutils rather than pip.
58+
To enforce using the installed Cap'n Proto from the system:
5759

5860
```bash
59-
python setup.py install --force-bundled-libcapnp
61+
pip install . -C force-system-libcapnp=True
6062
```
6163

6264
The bundling system isn't that smart so it might be necessary to clean up the bundled build when changing versions:
@@ -92,19 +94,12 @@ pipenv run pytest
9294

9395
### Binary Packages
9496

95-
Building a dumb binary distribution:
96-
97-
```bash
98-
python setup.py bdist_dumb
99-
```
100-
101-
Building a Python wheel distributiion:
97+
Building a Python wheel distributiion
10298

10399
```bash
104-
python setup.py bdist_wheel
100+
pip wheel .
105101
```
106102

107-
108103
## Documentation/Example
109104

110105
There is some basic documentation [here](http://capnproto.github.io/pycapnp/).

_custom_build/backend.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import sys
2+
3+
from setuptools.build_meta import * # noqa: F401, F403
4+
from setuptools.build_meta import build_wheel
5+
6+
backend_class = build_wheel.__self__.__class__
7+
8+
9+
class _CustomBuildMetaBackend(backend_class):
10+
def run_setup(self, setup_script="setup.py"):
11+
if self.config_settings:
12+
flags = []
13+
if self.config_settings.get("force-bundled-libcapnp"):
14+
flags.append("--force-bundled-libcapnp")
15+
if self.config_settings.get("force-system-libcapnp"):
16+
flags.append("--force-system-libcapnp")
17+
if self.config_settings.get("libcapnp-url"):
18+
flags.append("--libcapnp-url")
19+
flags.append(self.config_settings["libcapnp-url"])
20+
if flags:
21+
sys.argv = sys.argv[:1] + ["build_ext"] + flags + sys.argv[1:]
22+
return super().run_setup(setup_script)
23+
24+
def build_wheel(
25+
self, wheel_directory, config_settings=None, metadata_directory=None
26+
):
27+
self.config_settings = config_settings
28+
return super().build_wheel(wheel_directory, config_settings, metadata_directory)
29+
30+
31+
build_wheel = _CustomBuildMetaBackend().build_wheel

docs/install.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ To force rebuilding the pip package from source (you'll need requirments.txt or
1515

1616
To force bundling libcapnp (or force system libcapnp), just in case setup.py isn't doing the right thing::
1717

18-
pip install --no-binary :all: --install-option "--force-bundled-libcapnp"
19-
pip install --no-binary :all: --install-option "--force-system-libcapnp"
18+
pip install --no-binary :all: -C force-bundled-libcapnp=True
19+
pip install --no-binary :all: -C force-system-libcapnp=True
2020

2121
If you're using an older Linux distro (e.g. CentOS 6) you many need to set `LDFLAGS="-Wl,--no-as-needed -lrt"`::
2222

2323
LDFLAGS="-Wl,--no-as-needed -lrt" pip install --no-binary :all: pycapnp
2424

2525
It's also possible to specify the libcapnp url when bundling (this may not work, there be dragons)::
2626

27-
pip install --no-binary :all: --install-option "--force-bundled-libcapnp" --install-option "--libcapnp-url" --install-option "https://github.com/capnproto/capnproto/archive/master.tar.gz"
27+
pip install --no-binary :all: -C force-bundled-libcapnp=True -C libcapnp-url="https://github.com/capnproto/capnproto/archive/master.tar.gz"
2828

2929
From Source
3030
-----------

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[build-system]
22
requires = ["setuptools", "wheel", "pkgconfig", "cython<3"]
3+
build-backend = "backend"
4+
backend-path = ["_custom_build"]
35

46
[tool.pytest.ini_options]
57
asyncio_mode = "auto"

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[metadata]
2-
description-file = README.md
2+
description_file = README.md
33
license_files = LICENSE.md

setup.py

+22-24
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515

1616
from setuptools import setup, Extension
1717

18+
_this_dir = os.path.dirname(__file__)
19+
sys.path.insert(1, _this_dir)
20+
1821
from buildutils.build import build_libcapnp
1922
from buildutils.bundle import fetch_libcapnp
2023

21-
_this_dir = os.path.dirname(__file__)
2224

2325
MAJOR = 1
2426
MINOR = 3
@@ -85,26 +87,6 @@ def run(self):
8587
shutil.rmtree(x, ignore_errors=True)
8688

8789

88-
# hack to parse commandline arguments
89-
force_bundled_libcapnp = "--force-bundled-libcapnp" in sys.argv
90-
if force_bundled_libcapnp:
91-
sys.argv.remove("--force-bundled-libcapnp")
92-
force_system_libcapnp = "--force-system-libcapnp" in sys.argv
93-
if force_system_libcapnp:
94-
sys.argv.remove("--force-system-libcapnp")
95-
force_cython = "--force-cython" in sys.argv
96-
if force_cython:
97-
sys.argv.remove("--force-cython")
98-
# Always use cython, ignoring option
99-
libcapnp_url = None
100-
try:
101-
libcapnp_url_index = sys.argv.index("--libcapnp-url")
102-
libcapnp_url = sys.argv[libcapnp_url_index + 1]
103-
sys.argv.remove("--libcapnp-url")
104-
sys.argv.remove(libcapnp_url)
105-
except Exception:
106-
pass
107-
10890
from Cython.Distutils import build_ext as build_ext_c # noqa: E402
10991

11092

@@ -113,13 +95,29 @@ class build_libcapnp_ext(build_ext_c):
11395
Build capnproto library
11496
"""
11597

98+
user_options = build_ext_c.user_options + [
99+
("force-bundled-libcapnp", None, "Bundle capnp library into the installer"),
100+
("force-system-libcapnp", None, "Use system capnp library"),
101+
("libcapnp-url=", "u", "URL to download libcapnp from (only if bundled)"),
102+
]
103+
104+
def initialize_options(self):
105+
build_ext_c.initialize_options(self)
106+
self.force_bundled_libcapnp = None
107+
self.force_system_libcapnp = None
108+
self.libcapnp_url = None
109+
110+
def finalize_options(self):
111+
# print('The custom option for install is ', self.custom_option)
112+
build_ext_c.finalize_options(self)
113+
116114
def build_extension(self, ext):
117115
build_ext_c.build_extension(self, ext)
118116

119117
def run(self): # noqa: C901
120-
if force_bundled_libcapnp:
118+
if self.force_bundled_libcapnp:
121119
need_build = True
122-
elif force_system_libcapnp:
120+
elif self.force_system_libcapnp:
123121
need_build = False
124122
else:
125123
# Try to use capnp executable to find include and lib path
@@ -167,7 +165,7 @@ def run(self): # noqa: C901
167165

168166
if not os.path.exists(capnp_bin):
169167
# Not built, fetch and build
170-
fetch_libcapnp(bundle_dir, libcapnp_url)
168+
fetch_libcapnp(bundle_dir, self.libcapnp_url)
171169
build_libcapnp(bundle_dir, build_dir)
172170
else:
173171
print("capnproto already built at {}".format(build_dir))

tox.ini

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ deps=
77
pkgconfig
88
Jinja2
99
pytest
10+
pytest-asyncio
1011
cython<3
1112

1213
commands =
13-
python setup.py install
14+
pip install .
1415
py.test {posargs}
1516

1617
setenv =

0 commit comments

Comments
 (0)