A wheel could only be built while three scratch trees happened to coexist

That is why bin/patch.sh section 5c was 825 lines and why a Pillow bump
rebuilt CPython. Cross-building an extension module needs the untrimmed
staging tree (INCLUDEPY points into it), the private sysroot of static C
libraries (pillow links zlib, cffi links libffi) and an x86-64 build-python
of the same version -- and all three existed only mid-section, so the wheels
had to be built there too, on a cache with two stamps that invalidated each
other.

Each of those three is now a durable thing: anvil-python-dev carries the
headers, pkg_deps fills each recipe's own sysroot from the feed, and
pkg_buildpython caches one build-python for all nineteen recipes. So the
coupling goes away with them -- `make packages PKG=python-pillow` builds
Pillow.

  pkg/python            -> anvil-python + anvil-python-dev
  pkg/python-<name> x18 -> anvil-python-distro, anvil-python-pillow, ...

Three verbs in pkg/lib.sh, shared by all nineteen rather than one per awkward
project: pkg_buildpython provides the build-python, pkg_pytarget points its
sysconfig at the target headers in the sysroot, pkg_pywheel builds one wheel
and unpacks it into the staging site-packages. greenlet's gcc 7.2 designated
initializers, lmdb's LMDB_FORCE_CPYTHON and pillow's build_ext options are
recipe lines and a PKG_PY_SETUP_ARGS knob, not three more verbs.

Eighteen versions that were invisible are now in the feed index, anvil-moonraker
declares the thirteen packages it imports, and a printer that does not want
gcode thumbnails can leave Pillow uninstalled.

PY_STAMP, PYPKG_STAMP and PY_BUILD are gone: pkg_stamp derives the key from
PKG_BUILD_DEPENDS, and bin/fetch-assets.sh asks pkg_needs instead of
re-deriving a stamp bin/patch.sh also derived. Two of its four toolchain
conditions were that second spelling.

Measured:
  * 40 packages; 959 site-packages files, none owned by two packages
  * 12 mipsel extension modules -- the count the old gate demanded
  * every Depends resolves inside the feed (24 declare one, 0 unsatisfiable)
  * two cold builds byte-identical, with work/pkg, work/.py-host and the
    unpacked toolchain deleted in between
  * 233 static tests, 4 new: the PYPKG_LIST/recipe agreement both ways, no
    version pinned twice, patch.sh builds every package, and no Depends
    naming a package no recipe produces

Not verified here: the replica suite. It needs the stock FlashForge package,
which the packaging lane deliberately does without.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oleksandr ShyshatskyiandClaude Opus 5 committed 2026-08-28 18:28:30 +03:00
1 parent 2cfe3a59d5
commit 8cc92e8266
61 files changed
+1780 -969

No files matched your search

+6 -4
View File
@@ -9,7 +9,7 @@ Everything below was measured on the printer replica (real `rootfs.squashfs`
under qemu-mipsel) or read out of the objects with `readelf`, not off a
project page. `build.sh` beside this file rebuilds the whole thing in a
throwaway container and is where the numbers came from;
`bin/patch.sh` section 5c is the same build wired into the real build lane,
`pkg/python` is the same build wired into the real build lane,
and `test/integration/printer/case-python.sh` is the gate that proves the
result works on the printer's own kernel.
@@ -275,9 +275,11 @@ the extensions is the actual work in adopting this; not the interpreter.
That is how the numbers above were measured, and it is kept here for the
record and for trying a version bump before it becomes a pin. It is not how
the shipped interpreter is built: `bin/patch.sh` section 5c compiles it in
place inside the repo's own build image, from the sha256-pinned tarballs, and
caches it in `work/.py313`.
the shipped interpreter is built: `pkg/python` compiles it inside the repo's
own build image, from the sha256-pinned tarballs, against the seven static
libraries the feed builds as their own `-dev` packages, and caches it in
`work/pkg/python`. `bin/patch.sh` section 5c runs that recipe and stages what
it produced; it no longer contains a compiler invocation of its own.
The gate, against the tree the build actually produced:
+4 -4
View File
@@ -2,10 +2,10 @@
# ---------------------------------------------------------------------------
# Cross-build CPython 3.13 for the FlashForge Creator 5 Pro (Ingenic mipsel).
#
# THIS IS THE MEASUREMENT HARNESS, NOT THE BUILD. bin/patch.sh section 5c is
# what produces the interpreter a package ships: it compiles in place, inside
# the repo's own build image, from the tarballs pinned in versions.env, and
# caches the result in work/.py313. The two are the same build -- same flags,
# THIS IS THE MEASUREMENT HARNESS, NOT THE BUILD. pkg/python is what produces
# the interpreter a package ships: it compiles inside the repo's own build
# image, from the tarballs pinned in versions.env, and caches the result in
# work/pkg/python. The two are the same build -- same flags,
# same wrappers, same gates -- but this one re-execs itself inside a
# throwaway debian:bookworm, which is how every number in README.md beside it
# was measured, and which is the fastest way to try a change to the recipe