aboutsummaryrefslogtreecommitdiffstats

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.
  • --env KEY=VAL — extra env var for the source build (repeatable).
  • Epoch auto-derived from the GitHub release published_at (the repo must publish a GitHub Release for the tag).

Because the source tarball has no .git, projects using dynamic versioning would fail VCS detection at build time. gh mode therefore sets POETRY_DYNAMIC_VERSIONING_BYPASS and SETUPTOOLS_SCM_PRETEND_VERSION to --ver automatically (harmless when unused); --env overrides them and adds anything else the build needs.

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.