1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
# mkwheels
Build a reproducible, pinned Python wheels tarball for vendoring into a
SlackBuild (or any offline `pip install`). Generic over package + version, with
two source modes: PyPI packages and GitHub source releases.
## Usage
```
mkwheels pypi --name PKG --ver VER [--epoch N]
mkwheels gh --repo OWNER/REPO --ver VER [--name PKG] [--tag TAG] [--epoch N]
```
Common flags:
- `--ver VER` — version for the output filename. A leading `v` is stripped.
- `--epoch N` — optional `SOURCE_DATE_EPOCH`. Omitted → auto-derived (see each
mode). Pass it to override.
- `OUTPUT` env var overrides the output directory (default: current dir).
### pypi mode
- `--name PKG` — the PyPI package, downloaded at exactly `--ver`.
- Epoch auto-derived from the PyPI release upload time.
### gh mode
For packages not on PyPI (GitHub source, possibly with git dependencies). The
tagged source is downloaded and pip builds the project plus its whole
dependency tree (PyPI deps and any `git+` deps) into wheels.
- `--repo OWNER/REPO` — the GitHub repository.
- `--name PKG` — output name; defaults to the repo basename, lowercased.
- `--tag TAG` — git tag to fetch; a leading `v` is stripped for naming, and the
real ref is resolved by trying `<tag>` then `v<tag>`. Defaults to `--ver`.
- Epoch auto-derived from the GitHub release `published_at` (the repo must
publish a GitHub Release for the tag).
Outputs `<name>-wheels-<ver>.tar.gz` and `requirements.txt` (pinned + hashed).
Prints the md5sum and the resolved epoch. The `requirements.txt` is an audit
record of the resolved versions, not the install input: the SlackBuild installs
straight from the wheel files (`--find-links`), it does not re-resolve the
lockfile.
## Requirements
`bash`, `python3` + `pip`, `jq`, `curl`, `tar`, `gzip`, `md5sum`.
## Reproducibility
PyPI releases are immutable, so the wheel set for a fixed version is
deterministic. The tarball normalizes tar metadata (sorted entries, fixed
mtime/owner, `gzip -n`) so it is byte-identical for the same inputs + epoch.
In `gh` mode the project (and any source-only deps) are built from source.
With a fixed epoch this is byte-identical on the same machine, which is what
vendoring needs: build once, upload, pin the md5. Wheels containing compiled
extensions may differ across machines/toolchains, so build the vendored tarball
on the target platform.
Git-sourced dependencies (packages whose upstream pins a git URL) are frozen at
build time: pip resolves whatever is current, and the emitted `requirements.txt`
records the exact resolved versions. Once built, the tarball is the source of
truth.
## SBo integration
Run `mkwheels`, upload the tarball to your package host, and set
`DOWNLOAD_x86_64` / `MD5SUM_x86_64` in the SlackBuild `.info` to point at it.
The SlackBuild then `pip install --no-index --find-links=<wheels>` into a venv.
## Test
`./selftest` builds twice with a fixed epoch in both modes (`pypi` six,
`gh` pyparsing) and asserts each pair of wheels tarballs is byte-identical. Run
it after changing the tar/packing or mode-resolution logic.
## License
GPLv2 (v2-only). See `LICENSE`. Copyright (C) 2026 Danilo M. <danix@danix.xyz>.
|