ksp-builder 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kivy School
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: ksp-builder
3
+ Version: 0.0.0
4
+ Summary: PEP 517 build backend for KSProject-based packages
5
+ Author: ksp-builder contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/kivy-school/ksp-builder
8
+ Project-URL: Repository, https://github.com/kivy-school/ksp-builder
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: Software Development :: Build Tools
14
+ Requires-Python: >=3.13
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: compilekv
18
+ Requires-Dist: cython>=3.3.0
19
+ Requires-Dist: pyjnius-builder
20
+ Requires-Dist: setuptools>=84.0.0
21
+ Dynamic: license-file
22
+
23
+ # ksp-builder
24
+ A PEP 517 build backend for KSProject-based packages that combines Cython
25
+ compilation, Kivy KV compilation, Java source injection
26
+ ([pyjnius-builder](https://github.com/kivy-school/pyjnius-builder) convention),
27
+ Swift build support
28
+ ([pyswiftkit-builder](https://github.com/Py-Swift/pyswiftkit-builder)), and
29
+ Android Gradle configuration injection into a single backend.
30
+
31
+ **Requires Python ≥ 3.13** (uses `tomllib` from the standard library and aligns
32
+ with the minimum version requirements of pyswiftkit-builder and pyjnius-builder).
33
+
34
+ ## Usage
35
+
36
+ ```toml
37
+ [build-system]
38
+ requires = ["ksp-builder"]
39
+ build-backend = "ksp_builder"
40
+ ```
41
+
42
+ ## Configuration
43
+
44
+ ### Build scripts — `[tool.ksp-builder]`
45
+
46
+ Run your own scripts around the build. `before_build` runs before the artifact
47
+ is assembled, `after_build` runs once it exists on disk. Both keys are optional
48
+ and paths are relative to the project root.
49
+
50
+ ```toml
51
+ [tool.ksp-builder]
52
+ before_build = "scripts/before_build.py"
53
+ after_build = "scripts/sign_wheel.sh"
54
+ ```
55
+
56
+ The scripts do not have to be Python. A `.py` path is run with the interpreter
57
+ running the build; any other path is executed directly, so it needs its
58
+ executable bit set (`chmod +x`) and a shebang naming its interpreter:
59
+
60
+ ```sh
61
+ #!/usr/bin/env bash
62
+ set -e
63
+ codesign --sign "$SIGNING_IDENTITY" "$KSP_BUILD_ARTIFACT"
64
+ ```
65
+
66
+ Scripts run with the project root as their working directory. `after_build`
67
+ receives the artifact path as its first argument. Both are given the
68
+ environment variables `KSP_BUILD_PROJECT_ROOT`, `KSP_BUILD_TARGET`
69
+ (`wheel`, `sdist` or `editable`), and — for `after_build` —
70
+ `KSP_BUILD_ARTIFACT`. A script that exits non-zero fails the build.
71
+
72
+ ### Cython compilation — `[tool.ksp-builder]`
73
+
74
+ Compiles your package sources into extension modules, replacing the
75
+ `cythonized_app` `setup.py` (there is no `setup.py` with a PEP 517 backend, so
76
+ `ksp-builder` injects the `ext_modules` itself).
77
+
78
+ ```toml
79
+ [tool.ksp-builder]
80
+ cythonize = true
81
+ py_to_pyx = true
82
+ ```
83
+
84
+ The two switches are separate on purpose:
85
+
86
+ | Setting | Effect |
87
+ | --- | --- |
88
+ | `cythonize = true` | Compiles the `.pyx` sources already in your packages. `.py` modules are untouched. |
89
+ | `py_to_pyx = true` | Also copies each `.py` module to `.dist_src/` as a `.pyx` and compiles it. **Requires `cythonize`** — on its own it does nothing. |
90
+
91
+ `__init__.py` and `__main__.py` are never converted — the first keeps the
92
+ package importable, the second keeps `python -m yourpackage` working, since
93
+ runpy needs a code object that a compiled extension cannot provide. Both are
94
+ still shipped, just as `.py`. Top-level modules (`py-modules`) are left alone
95
+ too. A converted module ships only as its
96
+ compiled extension: the original `.py` is dropped from the wheel. Generated
97
+ `.pyx` and C files are written to `.dist_src/` at the project root, which you
98
+ can add to `.gitignore`. Nothing is ever written back into your package: a
99
+ build leaves the source tree exactly as it found it.
100
+
101
+ #### `.kv` files and other assets
102
+
103
+ Plain setuptools ships only Python modules, so a cythonized Kivy app would build
104
+ fine and then fail at runtime with its `.kv` files missing. Whenever it is
105
+ compiling something (`cythonize` or `compile_kv`), `ksp-builder` therefore
106
+ ships **every file in your packages** as package data —
107
+ `.kv`, images, fonts, JSON, whatever is there — including files in nested data
108
+ directories such as `assets/images/`. No configuration needed.
109
+
110
+ ```
111
+ kvapp/__init__.py -> kvapp/__init__.py
112
+ kvapp/app.py -> kvapp/app.cpython-313-darwin.so
113
+ kvapp/app.kv -> kvapp/app.kv
114
+ kvapp/data/images/logo.png -> kvapp/data/images/logo.png
115
+ kvapp/fonts/Roboto.ttf -> kvapp/fonts/Roboto.ttf
116
+ ```
117
+
118
+ Assets land next to the compiled extension, so `Path(__file__).parent` still
119
+ finds them. Directories containing an `__init__.py` are skipped here and
120
+ collected as the packages they are. (With `compile_kv = true` the `.kv` files
121
+ are the exception: they are compiled into their modules instead of shipped —
122
+ see below.)
123
+
124
+ Sources and build output are the one exception — `.py`, `.pyx`, `.pxd`, `.c`,
125
+ `.h`, `.o`, `.so`, `.pyd`, `.pyc` and `__pycache__` are never shipped as assets,
126
+ since that would hand back the very sources the compilation just removed. To
127
+ ship one of those deliberately (a prebuilt binary, say), name it explicitly in
128
+ `[tool.setuptools.package-data]`, which is merged with what is found here.
129
+
130
+ Set `include_assets = false` to turn the whole behaviour off and go back to
131
+ declaring package data yourself.
132
+
133
+ Package layout still comes from `[tool.setuptools]` — `packages`, `package-dir`
134
+ and `packages.find` are read as-is (with the same src/flat auto-discovery
135
+ setuptools does), so nothing is duplicated in this section. The remaining keys
136
+ only tune the Cython step:
137
+
138
+ ```toml
139
+ [tool.ksp-builder]
140
+ cythonize = true
141
+ py_to_pyx = true
142
+ cythonize_exclude = ["main.py", "**/legacy/*.py"] # keep these as .py
143
+ cythonize_keep_py = true # ship the .py sources too
144
+ include_assets = false # stop auto-shipping assets
145
+
146
+ [tool.ksp-builder.cythonize_directives]
147
+ language_level = "3" # the default
148
+ boundscheck = false
149
+ ```
150
+
151
+ A pattern in `cythonize_exclude` without a `/` matches that file name at any
152
+ depth; a pattern with one is matched against the whole project-relative path.
153
+
154
+ Editable installs (`pip install -e .`) compile real `.pyx` sources but never
155
+ convert `.py`, so your edits keep taking effect during development. Sdists are
156
+ never cythonized — compilation happens when a wheel is built from the sdist.
157
+ Note that setuptools does not add `.pyx` files to an sdist on its own, so if you
158
+ ship hand-written Cython sources, include them (`MANIFEST.in` with
159
+ `recursive-include <pkg> *.pyx *.pxd`) or a wheel built from the sdist will have
160
+ nothing to compile.
161
+
162
+ ### Kivy KV compilation — `[tool.ksp-builder]`
163
+
164
+ ```toml
165
+ [tool.ksp-builder]
166
+ compile_kv = true
167
+ ```
168
+
169
+ Turns every `.kv` file in your packages into a Python module using the
170
+ [`compilekv`](https://github.com/kivy-school/compilekv) library: the KV rules
171
+ become real widget classes, so nothing has to be parsed at startup and the
172
+ result can be compiled like any other module.
173
+
174
+ Each `.kv` is compiled together with the `.py` of the same name beside it, and
175
+ the module the two produce replaces that `.py` in the wheel. A `.kv` with no
176
+ `.py` beside it simply becomes a module of its own. The `.kv` itself is *not*
177
+ shipped — it has been compiled into the module, and shipping it too would only
178
+ invite a second, conflicting set of rules at runtime.
179
+
180
+ ```
181
+ kvapp/app.py + kvapp/app.kv -> kvapp/app.py (the two, merged)
182
+ kvapp/theme.kv -> kvapp/theme.py (no .py of its own)
183
+ ```
184
+
185
+ Only `.kv` files sitting **directly in a package** are compiled, since a
186
+ compiled KV file becomes an importable module and there is no module name for
187
+ one in a plain data directory. A `.kv` under `kvapp/ui/` stays ordinary
188
+ package data.
189
+
190
+ #### All three switches together
191
+
192
+ `compile_kv` composes with the Cython switches. With all three on, the module
193
+ compiled out of the `.kv` is staged as a `.pyx` and compiled to an extension,
194
+ so neither the `.kv` nor the `.py` that fed it reaches the wheel:
195
+
196
+ ```toml
197
+ [tool.ksp-builder]
198
+ cythonize = true
199
+ py_to_pyx = true
200
+ compile_kv = true
201
+ ```
202
+
203
+ ```
204
+ kvapp/__init__.py -> kvapp/__init__.py
205
+ kvapp/__main__.py -> kvapp/__main__.py
206
+ kvapp/app.py + kvapp/app.kv -> kvapp/app.cpython-313-darwin.so
207
+ kvapp/theme.kv -> kvapp/theme.cpython-313-darwin.so
208
+ kvapp/assets/logo.png -> kvapp/assets/logo.png
209
+ ```
210
+
211
+ A module named by `cythonize_exclude` is still compiled out of its KV file, it
212
+ just stays a plain `.py`. `__init__` and `__main__` are never turned into
213
+ extensions here either.
214
+
215
+ Editable installs (`pip install -e .`) leave `.kv` files alone — the source
216
+ tree is what gets imported, so a generated module would never be used and your
217
+ `.kv` edits keep taking effect during development.
218
+
219
+ ### Android Gradle config — `[tool.kivy-school.android]`
220
+
221
+ When present, `ksp-builder` generates a `.gradle/<package_name>.json` file and
222
+ injects it into the built wheel and sdist. `ksproject` can then discover and
223
+ merge these JSON files from all installed packages to assemble the final Gradle
224
+ build configuration (permissions, gradle dependencies, etc.).
225
+
226
+ ```toml
227
+ [tool.kivy-school]
228
+ app_name = "MyApp"
229
+
230
+ [tool.kivy-school.android]
231
+ package_name = "org.example.myapp"
232
+ gradle_dependencies = [
233
+ "com.google.firebase:firebase-analytics:21.0.0",
234
+ ]
235
+ permissions = [
236
+ "INTERNET",
237
+ "CAMERA",
238
+ ]
239
+ [tool.kivy-school.android.meta_data]
240
+ "com.google.android.gms.ads.APPLICATION_ID" = "ca-app-pub-3940256099942544~3347511713"
241
+
242
+ ```
243
+
244
+ ### Java sources — `[tool.pyjnius]`
245
+
246
+ Java source files are injected into the wheel and sdist under `.java/`, following
247
+ the [pyjnius-builder](https://github.com/kivy-school/pyjnius-builder) convention.
248
+
249
+ ```toml
250
+ [tool.pyjnius]
251
+ java-paths = ["java/"]
252
+ ```
253
+
254
+ ### Swift packages — `[tool.pyswiftkit]`
255
+
256
+ If `pyswiftkit-builder` is installed and `[tool.pyswiftkit]` is configured,
257
+ `swift build` runs automatically before the wheel is assembled and the compiled
258
+ artifacts are injected into the wheel.
259
+
260
+ ```toml
261
+ [tool.pyswiftkit]
262
+ products = ["mymodule"]
263
+ ```
264
+
@@ -0,0 +1,242 @@
1
+ # ksp-builder
2
+ A PEP 517 build backend for KSProject-based packages that combines Cython
3
+ compilation, Kivy KV compilation, Java source injection
4
+ ([pyjnius-builder](https://github.com/kivy-school/pyjnius-builder) convention),
5
+ Swift build support
6
+ ([pyswiftkit-builder](https://github.com/Py-Swift/pyswiftkit-builder)), and
7
+ Android Gradle configuration injection into a single backend.
8
+
9
+ **Requires Python ≥ 3.13** (uses `tomllib` from the standard library and aligns
10
+ with the minimum version requirements of pyswiftkit-builder and pyjnius-builder).
11
+
12
+ ## Usage
13
+
14
+ ```toml
15
+ [build-system]
16
+ requires = ["ksp-builder"]
17
+ build-backend = "ksp_builder"
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ ### Build scripts — `[tool.ksp-builder]`
23
+
24
+ Run your own scripts around the build. `before_build` runs before the artifact
25
+ is assembled, `after_build` runs once it exists on disk. Both keys are optional
26
+ and paths are relative to the project root.
27
+
28
+ ```toml
29
+ [tool.ksp-builder]
30
+ before_build = "scripts/before_build.py"
31
+ after_build = "scripts/sign_wheel.sh"
32
+ ```
33
+
34
+ The scripts do not have to be Python. A `.py` path is run with the interpreter
35
+ running the build; any other path is executed directly, so it needs its
36
+ executable bit set (`chmod +x`) and a shebang naming its interpreter:
37
+
38
+ ```sh
39
+ #!/usr/bin/env bash
40
+ set -e
41
+ codesign --sign "$SIGNING_IDENTITY" "$KSP_BUILD_ARTIFACT"
42
+ ```
43
+
44
+ Scripts run with the project root as their working directory. `after_build`
45
+ receives the artifact path as its first argument. Both are given the
46
+ environment variables `KSP_BUILD_PROJECT_ROOT`, `KSP_BUILD_TARGET`
47
+ (`wheel`, `sdist` or `editable`), and — for `after_build` —
48
+ `KSP_BUILD_ARTIFACT`. A script that exits non-zero fails the build.
49
+
50
+ ### Cython compilation — `[tool.ksp-builder]`
51
+
52
+ Compiles your package sources into extension modules, replacing the
53
+ `cythonized_app` `setup.py` (there is no `setup.py` with a PEP 517 backend, so
54
+ `ksp-builder` injects the `ext_modules` itself).
55
+
56
+ ```toml
57
+ [tool.ksp-builder]
58
+ cythonize = true
59
+ py_to_pyx = true
60
+ ```
61
+
62
+ The two switches are separate on purpose:
63
+
64
+ | Setting | Effect |
65
+ | --- | --- |
66
+ | `cythonize = true` | Compiles the `.pyx` sources already in your packages. `.py` modules are untouched. |
67
+ | `py_to_pyx = true` | Also copies each `.py` module to `.dist_src/` as a `.pyx` and compiles it. **Requires `cythonize`** — on its own it does nothing. |
68
+
69
+ `__init__.py` and `__main__.py` are never converted — the first keeps the
70
+ package importable, the second keeps `python -m yourpackage` working, since
71
+ runpy needs a code object that a compiled extension cannot provide. Both are
72
+ still shipped, just as `.py`. Top-level modules (`py-modules`) are left alone
73
+ too. A converted module ships only as its
74
+ compiled extension: the original `.py` is dropped from the wheel. Generated
75
+ `.pyx` and C files are written to `.dist_src/` at the project root, which you
76
+ can add to `.gitignore`. Nothing is ever written back into your package: a
77
+ build leaves the source tree exactly as it found it.
78
+
79
+ #### `.kv` files and other assets
80
+
81
+ Plain setuptools ships only Python modules, so a cythonized Kivy app would build
82
+ fine and then fail at runtime with its `.kv` files missing. Whenever it is
83
+ compiling something (`cythonize` or `compile_kv`), `ksp-builder` therefore
84
+ ships **every file in your packages** as package data —
85
+ `.kv`, images, fonts, JSON, whatever is there — including files in nested data
86
+ directories such as `assets/images/`. No configuration needed.
87
+
88
+ ```
89
+ kvapp/__init__.py -> kvapp/__init__.py
90
+ kvapp/app.py -> kvapp/app.cpython-313-darwin.so
91
+ kvapp/app.kv -> kvapp/app.kv
92
+ kvapp/data/images/logo.png -> kvapp/data/images/logo.png
93
+ kvapp/fonts/Roboto.ttf -> kvapp/fonts/Roboto.ttf
94
+ ```
95
+
96
+ Assets land next to the compiled extension, so `Path(__file__).parent` still
97
+ finds them. Directories containing an `__init__.py` are skipped here and
98
+ collected as the packages they are. (With `compile_kv = true` the `.kv` files
99
+ are the exception: they are compiled into their modules instead of shipped —
100
+ see below.)
101
+
102
+ Sources and build output are the one exception — `.py`, `.pyx`, `.pxd`, `.c`,
103
+ `.h`, `.o`, `.so`, `.pyd`, `.pyc` and `__pycache__` are never shipped as assets,
104
+ since that would hand back the very sources the compilation just removed. To
105
+ ship one of those deliberately (a prebuilt binary, say), name it explicitly in
106
+ `[tool.setuptools.package-data]`, which is merged with what is found here.
107
+
108
+ Set `include_assets = false` to turn the whole behaviour off and go back to
109
+ declaring package data yourself.
110
+
111
+ Package layout still comes from `[tool.setuptools]` — `packages`, `package-dir`
112
+ and `packages.find` are read as-is (with the same src/flat auto-discovery
113
+ setuptools does), so nothing is duplicated in this section. The remaining keys
114
+ only tune the Cython step:
115
+
116
+ ```toml
117
+ [tool.ksp-builder]
118
+ cythonize = true
119
+ py_to_pyx = true
120
+ cythonize_exclude = ["main.py", "**/legacy/*.py"] # keep these as .py
121
+ cythonize_keep_py = true # ship the .py sources too
122
+ include_assets = false # stop auto-shipping assets
123
+
124
+ [tool.ksp-builder.cythonize_directives]
125
+ language_level = "3" # the default
126
+ boundscheck = false
127
+ ```
128
+
129
+ A pattern in `cythonize_exclude` without a `/` matches that file name at any
130
+ depth; a pattern with one is matched against the whole project-relative path.
131
+
132
+ Editable installs (`pip install -e .`) compile real `.pyx` sources but never
133
+ convert `.py`, so your edits keep taking effect during development. Sdists are
134
+ never cythonized — compilation happens when a wheel is built from the sdist.
135
+ Note that setuptools does not add `.pyx` files to an sdist on its own, so if you
136
+ ship hand-written Cython sources, include them (`MANIFEST.in` with
137
+ `recursive-include <pkg> *.pyx *.pxd`) or a wheel built from the sdist will have
138
+ nothing to compile.
139
+
140
+ ### Kivy KV compilation — `[tool.ksp-builder]`
141
+
142
+ ```toml
143
+ [tool.ksp-builder]
144
+ compile_kv = true
145
+ ```
146
+
147
+ Turns every `.kv` file in your packages into a Python module using the
148
+ [`compilekv`](https://github.com/kivy-school/compilekv) library: the KV rules
149
+ become real widget classes, so nothing has to be parsed at startup and the
150
+ result can be compiled like any other module.
151
+
152
+ Each `.kv` is compiled together with the `.py` of the same name beside it, and
153
+ the module the two produce replaces that `.py` in the wheel. A `.kv` with no
154
+ `.py` beside it simply becomes a module of its own. The `.kv` itself is *not*
155
+ shipped — it has been compiled into the module, and shipping it too would only
156
+ invite a second, conflicting set of rules at runtime.
157
+
158
+ ```
159
+ kvapp/app.py + kvapp/app.kv -> kvapp/app.py (the two, merged)
160
+ kvapp/theme.kv -> kvapp/theme.py (no .py of its own)
161
+ ```
162
+
163
+ Only `.kv` files sitting **directly in a package** are compiled, since a
164
+ compiled KV file becomes an importable module and there is no module name for
165
+ one in a plain data directory. A `.kv` under `kvapp/ui/` stays ordinary
166
+ package data.
167
+
168
+ #### All three switches together
169
+
170
+ `compile_kv` composes with the Cython switches. With all three on, the module
171
+ compiled out of the `.kv` is staged as a `.pyx` and compiled to an extension,
172
+ so neither the `.kv` nor the `.py` that fed it reaches the wheel:
173
+
174
+ ```toml
175
+ [tool.ksp-builder]
176
+ cythonize = true
177
+ py_to_pyx = true
178
+ compile_kv = true
179
+ ```
180
+
181
+ ```
182
+ kvapp/__init__.py -> kvapp/__init__.py
183
+ kvapp/__main__.py -> kvapp/__main__.py
184
+ kvapp/app.py + kvapp/app.kv -> kvapp/app.cpython-313-darwin.so
185
+ kvapp/theme.kv -> kvapp/theme.cpython-313-darwin.so
186
+ kvapp/assets/logo.png -> kvapp/assets/logo.png
187
+ ```
188
+
189
+ A module named by `cythonize_exclude` is still compiled out of its KV file, it
190
+ just stays a plain `.py`. `__init__` and `__main__` are never turned into
191
+ extensions here either.
192
+
193
+ Editable installs (`pip install -e .`) leave `.kv` files alone — the source
194
+ tree is what gets imported, so a generated module would never be used and your
195
+ `.kv` edits keep taking effect during development.
196
+
197
+ ### Android Gradle config — `[tool.kivy-school.android]`
198
+
199
+ When present, `ksp-builder` generates a `.gradle/<package_name>.json` file and
200
+ injects it into the built wheel and sdist. `ksproject` can then discover and
201
+ merge these JSON files from all installed packages to assemble the final Gradle
202
+ build configuration (permissions, gradle dependencies, etc.).
203
+
204
+ ```toml
205
+ [tool.kivy-school]
206
+ app_name = "MyApp"
207
+
208
+ [tool.kivy-school.android]
209
+ package_name = "org.example.myapp"
210
+ gradle_dependencies = [
211
+ "com.google.firebase:firebase-analytics:21.0.0",
212
+ ]
213
+ permissions = [
214
+ "INTERNET",
215
+ "CAMERA",
216
+ ]
217
+ [tool.kivy-school.android.meta_data]
218
+ "com.google.android.gms.ads.APPLICATION_ID" = "ca-app-pub-3940256099942544~3347511713"
219
+
220
+ ```
221
+
222
+ ### Java sources — `[tool.pyjnius]`
223
+
224
+ Java source files are injected into the wheel and sdist under `.java/`, following
225
+ the [pyjnius-builder](https://github.com/kivy-school/pyjnius-builder) convention.
226
+
227
+ ```toml
228
+ [tool.pyjnius]
229
+ java-paths = ["java/"]
230
+ ```
231
+
232
+ ### Swift packages — `[tool.pyswiftkit]`
233
+
234
+ If `pyswiftkit-builder` is installed and `[tool.pyswiftkit]` is configured,
235
+ `swift build` runs automatically before the wheel is assembled and the compiled
236
+ artifacts are injected into the wheel.
237
+
238
+ ```toml
239
+ [tool.pyswiftkit]
240
+ products = ["mymodule"]
241
+ ```
242
+