@@ -8,6 +8,24 @@ parent: Topical Guides
88
99{% include toc.html %}
1010
11+ <!-- [[[cog
12+ from cog_helpers import code_fence, render_cookie, TOMLMatcher
13+ with render_cookie(backend="skbuild", vcs=False) as skbuild:
14+ skbuild_cmakelists_txt = skbuild.joinpath("CMakeLists.txt").read_text(encoding="utf-8").strip()
15+ skbuild_src_main_cpp = skbuild.joinpath("src/main.cpp").read_text(encoding="utf-8").strip()
16+ skbuild_pyproject = TOMLMatcher.from_file(skbuild / "pyproject.toml")
17+ with render_cookie(backend="mesonpy", vcs=False) as mesonpy:
18+ mesonpy_meson_build = mesonpy.joinpath("meson.build").read_text(encoding="utf-8").strip()
19+ mesonpy_src_main_cpp = mesonpy.joinpath("src/main.cpp").read_text(encoding="utf-8").strip()
20+ mesonpy_pyproject = TOMLMatcher.from_file(mesonpy / "pyproject.toml")
21+ assert "tool.meson-python" not in mesonpy_pyproject
22+ with render_cookie(backend="maturin", vcs=False) as maturin:
23+ maturin_cargo_toml = maturin.joinpath("Cargo.toml").read_text(encoding="utf-8").strip()
24+ maturin_src_lib_rs = maturin.joinpath("src/lib.rs").read_text(encoding="utf-8").strip()
25+ maturin_pyproject = TOMLMatcher.from_file(maturin / "pyproject.toml")
26+ ]]] -->
27+ <!-- [[[end]]] -->
28+
1129# Packaging Compiled Projects
1230
1331There are a variety of ways to package compiled projects. In the past, the only
@@ -52,27 +70,48 @@ selects the backend:
5270
5371{% tabs %} {% tab skbc Scikit-build-core %}
5472
73+ <!-- [[[cog
74+ with code_fence("toml"):
75+ print(skbuild_pyproject.get_source("build-system"))
76+ ]]] -->
77+ <!-- prettier-ignore-start -->
5578``` toml
5679[build-system ]
57- requires = [" scikit-build-core" ]
80+ requires = [" pybind11 " , " scikit-build-core>=0.11 " ]
5881build-backend = " scikit_build_core.build"
5982```
83+ <!-- prettier-ignore-end -->
84+ <!-- [[[end]]] -->
6085
6186{% endtab %} {% tab meson Meson-python %}
6287
88+ <!-- [[[cog
89+ with code_fence("toml"):
90+ print(mesonpy_pyproject.get_source("build-system"))
91+ ]]] -->
92+ <!-- prettier-ignore-start -->
6393``` toml
6494[build-system ]
65- requires = [" meson-python" ]
95+ requires = [" meson-python>=0.18 " , " pybind11 " ]
6696build-backend = " mesonpy"
6797```
98+ <!-- prettier-ignore-end -->
99+ <!-- [[[end]]] -->
68100
69101{% endtab %} {% tab maturin Maturin %}
70102
103+ <!-- [[[cog
104+ with code_fence("toml"):
105+ print(maturin_pyproject.get_source("build-system"))
106+ ]]] -->
107+ <!-- prettier-ignore-start -->
71108``` toml
72109[build-system ]
73- requires = [" maturin" ]
110+ requires = [" maturin>=1.9,<2 " ]
74111build-backend = " maturin"
75112```
113+ <!-- prettier-ignore-end -->
114+ <!-- [[[end]]] -->
76115
77116{% endtab %} {% endtabs %}
78117
@@ -83,20 +122,48 @@ build-backend = "maturin"
83122These tools all read the project table. They also have extra configuration
84123options in ` tool.* ` settings.
85124
125+ {% tabs %} {% tab skbc Scikit-build-core %}
126+
86127<!-- [[[cog
87- from cog_helpers import code_fence, render_cookie
88- with render_cookie(backend="skbuild") as skbuild:
89- skbuild_cmakelists_txt = skbuild.joinpath("CMakeLists.txt").read_text(encoding="utf-8").strip()
90- skbuild_src_main_cpp = skbuild.joinpath("src/main.cpp").read_text(encoding="utf-8").strip()
91- with render_cookie(backend="mesonpy") as mesonpy:
92- mesonpy_meson_build = mesonpy.joinpath("meson.build").read_text(encoding="utf-8").strip()
93- mesonpy_src_main_cpp = mesonpy.joinpath("src/main.cpp").read_text(encoding="utf-8").strip()
94- with render_cookie(backend="maturin") as maturin:
95- maturin_cargo_toml = maturin.joinpath("Cargo.toml").read_text(encoding="utf-8").strip()
96- maturin_src_lib_rs = maturin.joinpath("src/lib.rs").read_text(encoding="utf-8").strip()
128+ with code_fence("toml"):
129+ print(skbuild_pyproject.get_source("tool.scikit-build"))
97130]]] -->
131+ <!-- prettier-ignore-start -->
132+ ``` toml
133+ [tool .scikit-build ]
134+ minimum-version = " build-system.requires"
135+ build-dir = " build/{wheel_tag}"
136+ ```
137+ <!-- prettier-ignore-end -->
98138<!-- [[[end]]] -->
99139
140+ These options are not required, but can improve your experience.
141+
142+ {% endtab %} {% tab meson Meson-python %}
143+
144+ No ` tool.meson-python ` configuration required for this example.
145+
146+ {% endtab %} {% tab maturin Maturin %}
147+
148+ <!-- [[[cog
149+ with code_fence("toml"):
150+ print(maturin_pyproject.get_source("tool.maturin"))
151+ ]]] -->
152+ <!-- prettier-ignore-start -->
153+ ``` toml
154+ [tool .maturin ]
155+ module-name = " package._core"
156+ python-source = " src"
157+ sdist-generator = " git" # default is cargo
158+ ```
159+ <!-- prettier-ignore-end -->
160+ <!-- [[[end]]] -->
161+
162+ Maturin assumes you follow Rust's package structure, so we need a little bit of
163+ configuration here to follow the convention of the other tools here.
164+
165+ {% endtab %} {% endtabs %}
166+
100167## Backend specific files
101168
102169{% tabs %} {% tab skbc Scikit-build-core %}
@@ -180,18 +247,18 @@ with code_fence("toml"):
180247[package ]
181248name = " package"
182249version = " 0.1.0"
183- edition = " 2018 "
250+ edition = " 2021 "
184251
185252[lib ]
186253name = " _core"
187254# "cdylib" is necessary to produce a shared library for Python to import from.
188255crate-type = [" cdylib" ]
189256
190257[dependencies ]
191- rand = " 0.8.3 "
258+ rand = " 0.9.2 "
192259
193260[dependencies .pyo3 ]
194- version = " 0.19.1 "
261+ version = " 0.27.2 "
195262# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so)
196263# "abi3-py310" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.10
197264features = [" extension-module" , " abi3-py310" ]
@@ -203,6 +270,11 @@ features = ["extension-module", "abi3-py310"]
203270
204271## Example compiled file
205272
273+ This example will make a ` _core ` extension inside your package; this pattern
274+ allows you to easily provide both Python files and compiled extensions, and
275+ keeping the details of your compiled extension private. You can select whatever
276+ name you wish, though, or even make your compiled extension a top level module.
277+
206278{% tabs %} {% tab skbc Scikit-build-core %}
207279
208280Example ` src/main.cpp ` file:
@@ -297,26 +369,29 @@ with code_fence("rs"):
297369``` rs
298370use pyo3 :: prelude :: * ;
299371
300- #[pyfunction]
301- fn add (x : i64 , y : i64 ) -> i64 {
302- x + y
303- }
304-
305- #[pyfunction]
306- fn subtract (x : i64 , y : i64 ) -> i64 {
307- x - y
308- }
309-
310372/// A Python module implemented in Rust. The name of this function must match
311373/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to
312374/// import the module.
313375#[pymodule]
314- fn _core (_py : Python , m : & PyModule ) -> PyResult <()> {
315- m . add_function (wrap_pyfunction! (add , m )? )? ;
316- m . add_function (wrap_pyfunction! (subtract , m )? )? ;
317- m . add (" __version__" , env! (" CARGO_PKG_VERSION" ))? ;
376+ mod _core {
377+ use super :: * ;
378+
379+ #[pyfunction]
380+ fn add (x : i64 , y : i64 ) -> i64 {
381+ x + y
382+ }
383+
384+ #[pyfunction]
385+ fn subtract (x : i64 , y : i64 ) -> i64 {
386+ x - y
387+ }
388+
318389
319- Ok (())
390+ #[pymodule_init]
391+ fn pymodule_init (m : & Bound <'_ , PyModule >) -> PyResult <()> {
392+ m . add (" __version__" , env! (" CARGO_PKG_VERSION" ))? ;
393+ Ok (())
394+ }
320395}
321396```
322397<!-- prettier-ignore-end -->
@@ -327,7 +402,8 @@ fn _core(_py: Python, m: &PyModule) -> PyResult<()> {
327402## Package structure
328403
329404The recommendation (followed above) is to have source code in ` /src ` , and the
330- Python package files in ` /src/<package> ` .
405+ Python package files in ` /src/<package> ` . The compiled files also can go in
406+ ` /src ` .
331407
332408## Versioning
333409
@@ -343,25 +419,26 @@ though the defaults are reasonable.
343419
344420Unlike pure Python, you'll need to build redistributable wheels for each
345421platform and supported Python version if you want to avoid compilation on the
346- user's system. See [ the CI page on wheels] [ gha_wheels ] for a suggested workflow.
422+ user's system using cibuildwheel. See [ the CI page on wheels] [ gha_wheels ] for a
423+ suggested workflow.
347424
348425## Special considerations
349426
350427### NumPy
351428
352429Modern versions of NumPy (1.25+) allow you to target older versions when
353- building, which is _ highly_ recommended, and this will become required in NumPy
354- 2.0. Now you add:
430+ building, which is _ highly_ recommended, and this became required in NumPy 2.0.
431+ Now you add:
355432
356433``` cpp
357434#define NPY_TARGET_VERSION NPY_1_22_API_VERSION
358435```
359436
360437(Where that number is whatever version you support as a minimum) then make sure
361- you build with NumPy 1.25+ (or 2.0+ when it comes out ). Before 1.25, it was
362- necessary to actually pin the oldest NumPy you supported (the
363- `oldest-supported-numpy` package is the easiest method). If you support Python <
364- 3.9, you'll have to use the old method for those versions.
438+ you build with NumPy 1.25+ (or 2.0+). Before 1.25, it was necessary to actually
439+ pin the oldest NumPy you supported (the `oldest-supported-numpy` package is the
440+ easiest method). If you support Python < 3.9, you'll have to use the old method
441+ for those versions.
365442
366443If using pybind11, you don't need NumPy at build-time in the first place.
367444
0 commit comments