Files
firmware/versions.env
T
Oleksandr ShyshatskyiandClaude Opus 5 09a9f868e3 klippy compiles c_helper.so at first run, and this printer has no compiler
So the "build system" for that .so genuinely is one gcc line -- the argument
list in klippy/chelper/__init__.py -- and pkg_build could not express it,
because it always ran make. That is why Klipper was the last cross-build
still living in bin/patch.sh, which unpacked the Ingenic toolchain into
work/.mips-toolchain, wrote its own gcc invocation, cached on an mtime
comparison and gated the result itself: every one of the things pkg/lib.sh
exists to do once.

PKG_CC_SHARED is that line, appended to `$CC -shared -fPIC`. A knob and not
a verb, for the reason pkg_build's own header gives about the three
mechanisms with one user each: the recipe says WHAT to link, pkg_build owns
HOW, and -EL -mnan=2008 stay spelled once in pkg_toolchain where the wrapper
is gated before anything is built on it.

  pkg/klipper -> anvil-klipper_0.13.0+gitb722a4c8-1_mipsel_xburst2.ipk

Both gates moved into the recipe, next to the ship list they check -- the
same move the s6 presence checks made. They now run on `make packages` too,
so a c_helper.so that is not nan2008, or one missing a symbol the klippy
beside it cdefs, fails the build that produced the .ipk rather than only a
full firmware build. The symbol gate is the one that matters: cffi resolves
lazily, so a stale .so imports cleanly and dies at connect on the printer.

KLIPPER_FORK is gone. It pointed the build at a local checkout instead of
the pin, which is a second source for a recipe allowed exactly one -- and it
is the same variable that let v20260824-nova-kakhovka ship a klippy tree
nobody had built, set to "" in CI. To build your own fork, put its tarball
in vendor/ and re-pin KLIPPER_VERSION and KLIPPER_SHA256; the sha is then
the guarantee every other source in this repo carries. bin/fetch-assets.sh
loses two of its conditions with it, and test/test-chelper.py loses the
config.env fallback that printed "SKIP: no KLIPPER_FORK configured" -- a
green line for a check that had not run.

bin/patch.sh section 1 is now staging and nothing else: a cp -a into the
SOFTWARE component, where the stock klipperDaemon looks, plus the
chelper.tar its run.sh extracts. That tar stays out of the .ipk, where it
would be a second copy of a file the package already contains. The package
installs the tree under $MODDIR/klipper, inert until phase 7 of
docs/notes/80-s6-migration.md makes the installed copy the live one -- at
which point these lines are deleted rather than moved.

Measured:
  * two cold builds of pkg/klipper byte-identical, work/pkg deleted between
  * c_helper.so is 0x70001407 -- nan2008, o32, mips32r2
  * all 66 functions klippy declares are exported, checked after pkg_ship
    strips rather than before
  * the whole feed still builds: 38 recipes, 41 packages
  * 236 static tests, including the staged-recipe list that now names klipper
  * BUILD_KLIPPER=stock removes the recipe from pkg_recipes entirely

Not verified here: bin/patch.sh end to end. It needs the stock FlashForge
package, which the packaging lane deliberately does without; bin/verify.sh's
klippy-tree and chelper.tar assertions are what cover the staging rewrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 19:10:07 +03:00

514 lines
32 KiB
Bash

# Pinned third-party payload pieces.
#
# These are NOT vendored in the repo: bin/fetch-assets.sh downloads them into
# vendor/ (gitignored) and refuses anything whose sha256 does not match. Bump
# a version here and the next build picks it up; the old file stays cached.
#
# To bump: edit the version -- for HelixScreen that means HELIX_FILE too,
# which embeds it, and for the PYPKG_* python packages the FILE is
# the only place the version appears -- set the matching sha256 to
# SKIP, then run
# `make vendor`. It prints the real hash to paste back over SKIP.
# With a stale hash left in place fetch-assets.sh exits 1 instead of
# printing anything.
MAINSAIL_VERSION="v2.18.2"
MAINSAIL_SHA256="df2ba7c301f7bfc8ac9f122741a6ba08356d679ecfa1f62f898d0337802d5de5"
# Klipper: the creator5 branch of Klipper4FlashForge/klipper -- upstream
# v0.13.0 plus the FlashForge commits the printer actually needs (see the
# fork's README-creator5.md). A COMMIT, because the branch moves; bump it by
# pasting the new head sha here and re-hashing.
#
# This pin is the ONLY source. pkg/klipper extracts this tarball and builds
# c_helper.so from ITS sources with the pinned toolchain below, so the .so can
# never be older than the klippy tree beside it. The v20260824 release shipped
# without any of that: config.env's KLIPPER_FORK -- a second, unhashed source
# that pointed at a local checkout -- was empty in CI, bin/patch.sh silently
# kept the stock 0.12-era tree, and the package half-overwrote the fork on
# updated printers, where klippy died at connect with a cffi arg-count error.
# KLIPPER_FORK is gone with the migration to pkg/: a recipe names its source
# once, so there is no configuration in which a fork build has no fork.
KLIPPER_VERSION="b722a4c8b1aedfb939a7688a9b30e7864fddd505"
KLIPPER_SHA256="dbd81ad75701a56562708a398be9efda1cfa9af84211445e5c86f0683c2326e4"
# The cross-toolchain that builds c_helper.so: Ingenic's gcc 7.2.0 /
# glibc 2.29 for the X2000 (mips32r2, little-endian, o32, nan2008 -- the
# printer's kernel refuses anything else, and Debian's mips cross-compilers
# produce big-endian or legacy-nan objects). The same toolchain the K1
# community uses for the identical SoC family; mirrored as a release asset
# by ballaswag/k1-discovery. ~203MB, so fetch-assets.sh only pulls it when
# the build actually needs to compile chelper.
MIPS_TOOLCHAIN_VERSION="1.0.0"
MIPS_TOOLCHAIN_FILE="mips-gcc720-glibc229.tar.gz"
MIPS_TOOLCHAIN_SHA256="c0415a857c6b384820ec61b8a30c89e528585f3ef07d580fc856e80313c9d2e8"
HELIX_VERSION="v0.99.115-creator5.2"
HELIX_FILE="helixscreen-creator5-v0.99.115.tar.gz"
HELIX_SHA256="dd66dd9b1bdd73f77d7c7f7d76003b14121c5cf102a755083ef7506fbdb22d7d"
# Moonraker. The stock one is a 2022 build (API 1.0.5) that predates the
# webcam "enabled" flag that modern Mainsail requires -- see bin/patch.sh.
#
# A COMMIT, NOT A RELEASE, and not a matter of taste. FlashForge built python
# 3.8.2 WITHOUT the _sqlite3 module and shipped no libsqlite3 anywhere on the
# printer, so every Moonraker from v0.9.0 onward dies at startup with
# "ModuleNotFoundError: No module named '_sqlite3'" -- v0.9.0 is where the
# database moved from lmdb to sqlite. The last release that still uses lmdb is
# v0.8.0, and that one predates the webcam flag. No release has both, so this
# pins the newest commit that has the webcam fix and still uses lmdb: 9d0d09d,
# 2023-12-05, the commit before 80c7620 "database: migrate from lmdb to
# sqlite". lmdb 1.3.0 is installed, so the existing database is read in place
# and nothing is converted.
#
# Before moving this pin: the blocker is native modules, not the python
# version (Moonraker still supports >=3.7). Check every dependency of a newer
# build against what is actually in Python-3.8.2/lib/python3.8/lib-dynload and
# site-packages on the factory image -- a missing C extension is fatal and a
# pure-python one is not.
#
# CHECK IT BEFORE YOU SHIP IT. Nothing on the printer checks this any more and
# there is no stock build to fall back to: the installer replaces Moonraker
# unconditionally, so a pin the interpreter cannot load leaves the machine
# without a web UI until the next flash. `make test-moonraker` is where that is
# caught -- it imports every configured component on the printer's own
# interpreter, in the replica, before anything ships.
MOONRAKER_VERSION="9d0d09de8063922696359c1b88c86a86d6fdb296"
MOONRAKER_SHA256="6ddcba08a1a41f173fc6ee393a24e41f27b25f079e7c49921b87273ecff595a8"
# ---------------------------------------------------------------- s6
# The process supervisor and its service manager. skarnet releases these four
# as a matched set and they will not build against each other across
# generations, so they are pinned together and bumped together.
#
# EXECLINE IS HERE NOW, AND IT USED TO BE DELIBERATELY ABSENT. The argument
# for leaving it out was that `run` scripts are plain `#!/bin/sh` and execline
# would be "another 53 binaries / 2.1MB for nothing". The first half is still
# true and the second half was wrong twice over:
#
# * s6-rc has no --disable-execline. It links -lexecline unconditionally,
# and s6-rc-compile writes execline scripts into every compiled database
# it produces -- s6rc-oneshot-runner and s6rc-fdholder are generated
# whether or not any of OUR services need them. So the moment s6-rc is in
# the picture, execline is not optional.
# * 2.1MB was measured unstripped and statically linked against musl. Built
# the way everything is built now -- Ingenic glibc, dynamic libc, cross
# stripped -- all 53 binaries come to 1008KB. There is no longer a size
# worth writing a selection list for, and a selection list would be a
# trap: which execline programs a compiled database references depends on
# the service definitions, so a subset that works today fails the day
# somebody adds a pipeline, at boot, naming a program nobody chose.
#
# THREE things about this build are load-bearing, all of them measured on the
# replica rather than reasoned about (see tools/supervisor/README.md):
#
# * It is configured --prefix=/usr/data/anvil, because s6 bakes its
# libexecdir in AT COMPILE TIME: every waiting verb (s6-svc -w,
# s6-svwait) execs s6-svlisten, which spawns s6-ftrigrd from that baked-in
# path. Built for any other prefix, `s6-svstat` and respawn keep working
# and only the waiting verbs fail -- so it fails late and partially, which
# is the worst way for a supervisor to fail. Moving $MODDIR means
# rebuilding s6.
# * It is compiled with -D_FILE_OFFSET_BITS=64. Without it readdir()
# returns EOVERFLOW on this box (32-bit build, 64-bit inodes) and
# s6-svscan cannot see its own service directory: it starts cleanly and
# then supervises nothing.
# * It links libc DYNAMICALLY, against the printer's own glibc 2.29 -- the
# same libc.so.6 the interpreter already links, so this adds no dependency
# the payload did not have. It used to be linked statically against musl,
# on the strength of a real measurement: static glibc came to 73MB against
# musl's 3.6MB. That measurement compared two STATIC builds and dynamic
# glibc was never in it. Measured now: the whole supervision stack --
# s6's 21 binaries, execline's 53, s6-rc's 5 -- is under 2MB. The musl
# toolchain, and with it the only second libc in this tree, is gone.
#
# ONE MORE THING THAT IS NOT OBVIOUS AND COST AN AFTERNOON: s6 and s6-rc do
# not link against glibc 2.29 without -lpthread. skalibs' pthread_mutex_tailock
# reaches pthread_mutex_timedlock, which glibc kept in libpthread until 2.34
# merged it into libc. Upstream never hits this and musl never had the split.
# pkg/s6 and pkg/s6-rc pass it through PKG_MAKE_ARGS; the consequence is that
# every s6 binary carries a NEEDED libpthread.so.0, which a stock glibc rootfs
# has.
SKALIBS_VERSION="2.15.1.0"
SKALIBS_SHA256="f9c905e74935c6fe911c7e344e3e89d5fbd2014c1a04650b524b15ce9b5635d1"
S6_VERSION="2.15.1.0"
S6_SHA256="eab9c46e22b66b16135f9a05ec68a0ea287d9060b84d10defaaa2caad158ab52"
EXECLINE_VERSION="2.9.9.2"
EXECLINE_SHA256="908ed4db3a6b3a23a205d8fd4cf2a71089156f2aeae0f54656045aafad2dee32"
S6RC_VERSION="0.7.0.0"
S6RC_SHA256="bf5b8ce0da5a4ee70d642b818b61d9916a7a9b64a457595f388113e54a188688"
# THE musl CROSS-TOOLCHAIN WAS PINNED HERE, and the note is kept because the
# mistake it records is worth not relearning.
#
# It built s6 -- mipsel-buildroot-linux-musl, from Bootlin rather than
# musl.cc, which is a single volunteer-run host with no CDN that went
# unreachable from CI more than once. The first target chosen was mips32el:
# plain mips32r1, whose musl crt and libc are legacy-NaN by construction. This
# printer's kernel refuses a legacy-NaN binary, and the failure was not a
# refusal to run but a supervisor that started and then did nothing. It
# shipped once. The fix was mips32r5el, whose toolchain is nan2008 throughout.
#
# The lesson survives its cause: THE TOOLCHAIN DECIDES THE ABI AS MUCH AS THE
# SOURCES DO, which is why the toolchain filename is part of every recipe's
# cache stamp (pkg_stamp) and why the ABI gate exists at all. The toolchain
# itself is gone -- the supervision stack builds with the Ingenic glibc
# toolchain like everything else, and this tree has one libc again.
# ---------------------------------------------------------------- CPython
# A SECOND Python for the printer, cross-compiled here and SHIPPED BUT NOT
# USED (bin/patch.sh section 5c). FlashForge's own /usr/prog/Python-3.8.2 is
# still what everything runs on -- see payload/anvil-env.sh.
#
# WHY IT EXISTS AT ALL. FlashForge built 3.8.2 without _sqlite3 and shipped no
# libsqlite3 anywhere on the rootfs, which is the single fact that pins
# MOONRAKER_VERSION above to a 2023 commit: every Moonraker from v0.9.0 on
# stores its database in sqlite and dies at startup on this interpreter. An
# interpreter that can `import sqlite3` is what unpins that, and it is 3.13
# rather than a 3.8 with one module added because a from-source build costs
# the same either way. Measured on the replica: the stock 3.8.2 cannot import
# sqlite3 and this one creates a database, writes it, closes it and reads it
# back. test/integration/printer/case-python.sh is that measurement, kept.
#
# EVERY VERSION BELOW IS PINNED WITH A sha256 FOR THE SAME REASON THE REST OF
# THIS FILE IS: nothing binary is vendored, so the only thing standing between
# a source URL and code the printer executes is the hash. python.org, sqlite.org
# and skarnet are not equally trustworthy, but they are equally irrelevant once
# the hash is checked.
#
# What each pin is load-bearing for, because "bump to latest" is wrong for at
# least four of them:
#
# PY_VERSION The interpreter, and the ONLY version this build works
# for as written: cross-building CPython needs a build
# python of the SAME version (configure hard-errors
# otherwise), so patch.sh builds an x86-64 3.13.7 from this
# same tarball first. Bump it and both halves move
# together, which is why there is one variable.
# OPENSSL_VERSION 3.0.x DELIBERATELY, not 3.1/3.2/3.5. Two things about
# 3.0.15 are relied on and both were hit for real: `no-docs`
# does not exist before 3.1 (patch.sh must not pass it), and
# the linux-mips32 target bakes `-mips2` into its cflags,
# which this toolchain -- default -mfp64 -- rejects unless
# an explicit -mips32r2 lands after it on the command line.
# 3.0 is also the LTS branch (security fixes to 2026-09).
# Moving to 3.5 means re-deriving the target flags.
# SQLITE_* THE reason the whole build exists. The amalgamation
# ("autoconf") tarball, not the source tree: one .c file, no
# tcl, no fossil. Note the URL carries a YEAR directory that
# does not appear in the version number, hence SQLITE_YEAR.
# ZLIB_VERSION zlib.net serves current releases from /fossils/ once they
# are superseded, so the URL below is the stable one for an
# old version as well as a current one. Needed by the zlib
# module and by OpenSSL's compression.
# LIBFFI_VERSION _ctypes. Nothing on the rootfs can stand in: it ships
# libffi.so.7 and FlashForge's own /usr/prog carries .so.8,
# which is exactly the mess a static libffi.a avoids.
# XZ_VERSION lzma. 5.4.7 and not 5.6.x ON PURPOSE: 5.6.0/5.6.1 are the
# backdoored releases (CVE-2024-3094). 5.4.7 is the
# maintained branch that never carried it.
# BZIP2_VERSION bz2. Last release was 2019; this is not a stale pin, it is
# the whole project.
# EXPAT_* pyexpat, and with it every stdlib XML parser. Built here
# rather than left to CPython's bundled copy so there is one
# expat to patch when the next parser CVE lands. The tag has
# its own spelling (R_2_6_4) because libexpat's git tags use
# underscores; both appear in the URL.
#
# IF ONE OF THESE MOVES AND THE HASH IS NOT UPDATED, fetch-assets.sh stops the
# build. If a hash is updated without re-running the gate, the failure is
# silent and lands on a printer: a dependency that quietly stops linking makes
# CPython record the module as "missing" rather than fail, which is precisely
# how _sqlite3 disappeared during the spike. `make test-python` is the check.
PY_VERSION="3.13.7"
PY_SHA256="6c9d80839cfa20024f34d9a6dd31ae2a9cd97ff5e980e969209746037a5153b2"
OPENSSL_VERSION="3.0.15"
OPENSSL_SHA256="23c666d0edf20f14249b3d8f0368acaee9ab585b09e1de82107c66e1f3ec9533"
SQLITE_VERSION="3460100"
SQLITE_YEAR="2024"
SQLITE_SHA256="67d3fe6d268e6eaddcae3727fce58fcc8e9c53869bdd07a0c61e38ddf2965071"
ZLIB_VERSION="1.3.1"
ZLIB_SHA256="9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23"
LIBFFI_VERSION="3.4.6"
LIBFFI_SHA256="b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e"
XZ_VERSION="5.4.7"
XZ_SHA256="8db6664c48ca07908b92baedcfe7f3ba23f49ef2476864518ab5db6723836e71"
BZIP2_VERSION="1.0.8"
BZIP2_SHA256="ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269"
EXPAT_VERSION="2.6.4"
EXPAT_TAG="R_2_6_4"
EXPAT_SHA256="fd03b7172b3bd7427a3e7a812063f74754f24542429b634e0db6511b53fb2278"
# ------------------------------------------- CPython's third-party packages
# The 18 sdists that become $MODDIR/lib/python3.13/site-packages -- what
# Moonraker and klippy import, cross-built for mipsel by bin/patch.sh section
# 5c against the interpreter pinned above. SHIPPED BUT NOT USED, exactly like
# the interpreter itself: payload/anvil-env.sh still points FF_PYTHON at
# FlashForge's 3.8.2 and the switch is a later commit.
#
# THE LIST IS NOT moonraker-requirements.txt, and that is a decision rather
# than an omission. That file installs every OPTIONAL component's
# dependencies -- apprise, ldap3, paho-mqtt, zeroconf, python-periphery. None
# of those components is enabled in assets/moonraker.conf, so none of them is
# built. What is here is the closure of what the ENABLED set imports
# (moonraker.conf's sections plus server.CORE_COMPONENTS) plus klippy's, and
# nothing else. msgspec and uvloop are absent on purpose: Moonraker guards
# both with contextlib.suppress(ImportError) and falls back to the stdlib,
# which was measured rather than assumed.
#
# Every one is an SDIST, and bin/patch.sh builds them with `--no-binary :all:`
# for a reason that is recorded here because it cost real time to find: pip
# will HELPFULLY download a prebuilt manylinux wheel when one exists, and on
# the first run of the spike it did exactly that for cffi, greenlet and
# tornado. Three x86-64 .so files went into the tree, built cleanly, imported
# cleanly on the build host, and would have failed at import on the printer
# with nothing but ENOEXEC to explain themselves. Only the ABI gate caught
# them. A wheel is not a build; it is somebody else's build.
#
# WHERE THE URLS COME FROM. bin/fetch-assets.sh composes
# files.pythonhosted.org/packages/source/<initial>/<project>/<file>, the
# stable path form, rather than the hashed /packages/<a>/<b>/<64 hex>/ one a
# browser copies out of PyPI -- that path embeds a digest of the file and
# changes shape with no version bump, which is a URL nobody can maintain by
# hand. So the FILE is what is pinned here: it carries the version, the way
# HELIX_FILE does, and its SPELLING is upstream's business (MarkupSafe,
# dbus_next and streaming_form_data are not the names their projects use).
#
# PYPKG_LIST IS THE SHIP LIST AND THE FETCH LIST -- no longer the build order,
# which pkg_order derives from each recipe's PKG_BUILD_DEPENDS. Each entry is a
# recipe: `distro` is pkg/python-distro, which produces anvil-python-distro.
# bin/fetch-assets.sh fetches from this list and bin/patch.sh stages from it,
# and qa/static checks the list against the recipe directories both ways, so an
# entry added here and nowhere else fails a test rather than being fetched,
# hashed and silently never built. The variable names are the list entry
# uppercased with - as _, which is how every reader finds them; the VERSION is
# not pinned separately, it is read out of the FILE name (see pypkg_version).
PYPKG_LIST="distro inotify-simple libnacl dbus-next preprocess-cancellation
pyserial pyserial-asyncio pycparser smart-open jinja2 setuptools
tornado markupsafe cffi greenlet lmdb streaming-form-data pillow"
# ---- pure python. No compiler is invoked; they are here to be PRESENT. ----
PYPKG_DISTRO_FILE="distro-1.9.0.tar.gz"
PYPKG_DISTRO_SHA256="2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"
PYPKG_INOTIFY_SIMPLE_FILE="inotify_simple-1.3.5.tar.gz"
PYPKG_INOTIFY_SIMPLE_SHA256="8440ffe49c4ae81a8df57c1ae1eb4b6bfa7acb830099bfb3e305b383005cc128"
# libnacl is pure python and reaches libsodium through ctypes -- which is why
# SODIUM_VERSION at the bottom of this file exists at all, and why a bump to
# either one is a reason to re-run the ed25519 half of the moonraker gate.
PYPKG_LIBNACL_FILE="libnacl-2.1.0.tar.gz"
PYPKG_LIBNACL_SHA256="f3418da7df29e6d9b11fd7d990289d16397dc1020e4e35192e11aee826922860"
PYPKG_DBUS_NEXT_FILE="dbus_next-0.2.3.tar.gz"
PYPKG_DBUS_NEXT_SHA256="f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5"
# preprocess-cancellation 0.2.1 is a poetry project whose sdist declares NO
# [build-system] table at all and ships no setup.py, so there is no backend to
# call and pip's legacy fallback would have to guess. It is also ONE FILE, so
# pkg/python-preprocess-cancellation copies preprocess_cancellation.py into
# site-packages rather than building it -- see the comment there. (The same release also declares
# version 0.0.0 in its own metadata, a broken upstream release, so a version
# specifier would be refused by pip even if there were something to build.)
PYPKG_PREPROCESS_CANCELLATION_FILE="preprocess_cancellation-0.2.1.tar.gz"
PYPKG_PREPROCESS_CANCELLATION_SHA256="e2f1224e1ba1603bdfdbf6937caaf91082dc849e5122e80a5328aa999433ce79"
PYPKG_PYSERIAL_FILE="pyserial-3.4.tar.gz"
PYPKG_PYSERIAL_SHA256="6e2d401fdee0eab996cf734e67773a0143b932772ca8b42451440cfed942c627"
PYPKG_PYSERIAL_ASYNCIO_FILE="pyserial-asyncio-0.6.tar.gz"
PYPKG_PYSERIAL_ASYNCIO_SHA256="b6032923e05e9d75ec17a5af9a98429c46d2839adfaf80604d52e0faacd7a32f"
PYPKG_PYCPARSER_FILE="pycparser-2.22.tar.gz" # cffi's only runtime dep
PYPKG_PYCPARSER_SHA256="491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"
# smart_open is not optional, whatever the name suggests, and it is not
# Moonraker that wants it: streaming-form-data's targets.py imports it at
# MODULE SCOPE, so `import streaming_form_data` raises ModuleNotFoundError
# without it. Measured in the replica, where every other package imported and
# that one did not. moonraker-requirements.txt pins it for the same reason.
PYPKG_SMART_OPEN_FILE="smart_open-6.4.0.tar.gz"
PYPKG_SMART_OPEN_SHA256="be3c92c246fbe80ebce8fbacb180494a481a77fcdcb7c1aadb2ea5b9c2bee8b9"
PYPKG_JINJA2_FILE="jinja2-3.1.4.tar.gz"
PYPKG_JINJA2_SHA256="4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"
# setuptools/pkg_resources are a runtime dependency of nothing in this list.
# They are the fix for a MEASURED failure: a tree without them made lmdb fall
# back to its cffi path and try to invoke mips-linux-gnu-gcc ON THE PRINTER at
# Moonraker startup, and there is no compiler on a printer. (Installing lmdb
# as a wheel rather than as an egg removes that path as well, so this is now
# belt and braces -- 4.9MB of it, 36% of site-packages, and the one obvious
# thing to drop if the data partition ever gets tight.)
#
# It is ALSO the host-side build backend for most of this list:
# pkg_buildpython installs this same sdist into the shared x86-64
# build-python. One pin, both jobs -- so the setuptools that generates the
# shipped .so files is the setuptools whose hash is checked here.
PYPKG_SETUPTOOLS_FILE="setuptools-75.6.0.tar.gz"
PYPKG_SETUPTOOLS_SHA256="8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"
# ---- native: twelve mipsel extension modules come out of these six -------
# tornado's C speedup is optional and its published wheel carries an x86-64
# copy of it, which is exactly what --no-binary :all: above is for.
PYPKG_TORNADO_FILE="tornado-6.4.tar.gz"
PYPKG_TORNADO_SHA256="72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"
# markupsafe -- jinja2's escape accelerator, on the path of every gcode macro
# klippy renders. A pure-python fallback (_native.py) exists if it ever fights.
PYPKG_MARKUPSAFE_FILE="MarkupSafe-2.1.5.tar.gz"
PYPKG_MARKUPSAFE_SHA256="d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"
# cffi -- klippy's chelper, and the package that justifies the whole glibc
# decision: this is what dlopens FlashForge's own c_helper.so, measured. Wants
# libffi, and gets the static -fPIC one patch.sh already built for _ctypes.
PYPKG_CFFI_FILE="cffi-1.17.1.tar.gz"
PYPKG_CFFI_SHA256="1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"
# greenlet -- klippy's reactor. The MIPS stack-switching assembly was never
# the problem; C++ designated initializers were. greenlet 3.x writes its
# PyTypeObjects as designated initializers, which gcc 7.2 -- the only compiler
# that produces this printer's ABI -- accepts ONLY in declaration order with
# no gaps, and refuses anything else with "sorry, unimplemented: non-trivial
# designated initializers not supported". pkg/python-greenlet runs
# tools/python-packages/fill-designators.py over the sources first. Bumping
# this pin means re-running that, and it fails loudly rather than quietly: the
# script refuses to guess when a designator is not in the target's own headers.
PYPKG_GREENLET_FILE="greenlet-3.1.1.tar.gz"
PYPKG_GREENLET_SHA256="4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"
# lmdb -- Moonraker's database AT THE PIN ABOVE, and the package that
# disappears the moment MOONRAKER_VERSION moves past 80c7620 (lmdb -> sqlite),
# which is the whole reason for having built an interpreter with _sqlite3.
# pkg/python-lmdb sets LMDB_FORCE_CPYTHON=1; do not "fix" that with
# LMDB_FORCE_CFFI=0 -- setup.py tests the variable for PRESENCE, so the string
# "0" selects cffi.
PYPKG_LMDB_FILE="lmdb-1.4.1.tar.gz"
PYPKG_LMDB_SHA256="1f4c76af24e907593487c904ef5eba1993beb38ed385af82adb25a858f2d658d"
# streaming-form-data -- Moonraker's upload parser, Cython.
#
# NOT 1.13.0, which is what moonraker-requirements.txt pins. That release
# ships a PREGENERATED _parser.c from a Cython that predates 3.13: it calls
# the four-argument _PyLong_AsByteArray, which grew a fifth parameter in
# 3.13, so every integer conversion in the file fails to compile. 1.19.1
# ships C that is already regenerated, which is also why no Cython is pinned
# among the host backends below. Moonraker asks five names of it
# (StreamingFormDataParser, ParseFailedException, FileTarget, ValueTarget,
# SHA256Target), all present throughout the 1.x line -- and this pin is why
# smart_open above is in the list at all.
PYPKG_STREAMING_FORM_DATA_FILE="streaming_form_data-1.19.1.tar.gz"
PYPKG_STREAMING_FORM_DATA_SHA256="8172dd509a42ac0d1033deb1d13acb68f0cbae7e1492257d13af0922bdc4dd9b"
# pillow -- Moonraker's ONLY use of it is components/file_manager/metadata.py,
# which runs as a SUBPROCESS to pull thumbnails out of gcode, so it is not on
# the startup path: without it Moonraker runs and Mainsail shows no thumbnails.
#
# NOT 10.3.0, which is what moonraker-requirements.txt pins, and not for any
# reason to do with this target: 10.3.0 CANNOT BE BUILT BY ANY 3.13, cross or
# native. Its setup.py reads its own version with exec(...) followed by
# locals()["__version__"], and PEP 667 (new in 3.13) made a function's
# locals() a snapshot -- so the name exec() defined is not there any more and
# setup.py dies with KeyError: '__version__' before it compiles a single file.
# 11.0.0 is the first line that supports 3.13. Moonraker asks PIL for
# Image.open, resize and save, all unchanged across that gap.
PYPKG_PILLOW_FILE="pillow-11.0.0.tar.gz"
PYPKG_PILLOW_SHA256="72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"
# ---- host-side build backends. THESE NEVER SHIP. -------------------------
# Two of the sdists above name a PEP 517 backend that is not setuptools, and
# pkg_pywheel builds everything with --no-build-isolation -- which is not a
# preference either: with isolation ON, pip rewrites PYTHONPATH for the build
# subprocess, the cross _sysconfigdata module stops being importable, and the
# build either dies outright ("No module named
# '_sysconfigdata__linux_mipsel-linux-gnu'") or, far worse, silently answers
# every sysconfig question for x86-64 and produces host objects. So every
# backend has to be present in advance, and every one is pinned by hash for
# the same reason the compilers are: this is code that runs on the build
# machine and decides what the printer executes.
#
# Both bootstrap themselves (their sdists declare backend-path), so no third
# package is needed to install them, and neither is on PYPKG_LIST -- they go
# into the shared x86-64 build-python that pkg_buildpython caches under
# work/.py-host, and never leave it. setuptools, the third backend, is on the
# ship list above and ships as a package of its own as well.
PYPKG_HOST_LIST="flit-core poetry-core"
PYPKG_FLIT_CORE_FILE="flit_core-3.10.1.tar.gz" # jinja2's backend
PYPKG_FLIT_CORE_SHA256="66e5b87874a0d6e39691f0e22f09306736b633548670ad3c09ec9db03c5662f7"
PYPKG_POETRY_CORE_FILE="poetry_core-1.9.1.tar.gz" # libnacl's backend
PYPKG_POETRY_CORE_SHA256="7a2d49214bf58b4f17f99d6891d947a9836c9899a67a5069f52d7b67217f61b8"
# --------------------------------------------------------------- libsodium
# The LAST /usr/prog string in the picture. libnacl -- and with it Moonraker's
# `authorization` component, which signs its JWTs with ed25519 -- is pure
# python and loads libsodium through ctypes.cdll.LoadLibrary, i.e. dlopen. So
# unlike the interpreter's seven static dependencies this one MUST be shared,
# and it is the only library of ours that lands in $MODDIR/lib as a .so.
#
# It costs anvil-env.sh nothing, which is the pleasant part and worth writing
# down because it is an accident: libnacl's third fallback is
# __file__[0:__file__.find("lib")+3] + "/libsodium.so", and __file__ is
# $MODDIR/lib/python3.13/site-packages/libnacl/__init__.py -- so it resolves
# $MODDIR/lib/libsodium.so BY ABSOLUTE PATH, measured working with
# LD_LIBRARY_PATH unset entirely. That works because the prefix happens to
# contain the string "lib"; a prefix without it would break silently. Adding
# $MODDIR/lib to ANVIL_LIBS also works and is the belt-and-braces option, but
# it would put our libraries in front of everything for every mod process,
# which is the kind of entry this file's own history argues against.
#
# 1.0.20 is the current stable release and there is nothing exotic about the
# pin. It cross-builds in 24 seconds and is cached on this version string in
# work/.sodium, the same shape as work/.s6 and work/.py313.
SODIUM_VERSION="1.0.20"
SODIUM_SHA256="ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19"
# ------------------------------------------------------------------- opkg
# The package manager, cross-built by pkg/opkg/ and shipped as ONE static
# binary. See docs/notes/85-packaging.md for why opkg and not apk; the short
# version is that neither has usable binaries for this target (Alpine has no
# mips port at all, OpenWrt's is musl against our glibc), so the choice came
# down to which format is cheapest to produce, and an .ipk is an ar archive of
# two tarballs.
#
# Fetched from downloads.yoctoproject.org, which is opkg's actual home -- the
# project moved from openmoko to Yocto years ago and the old URLs are gone
# rather than stale.
#
# 0.7.0 AND NOT 0.6.3, which this was pinned to first, for a reason that is
# specifically about building it against musl. Two files in 0.6.3 --
# opkg_remove.c and opkg_archive.c -- call basename() without including
# <libgen.h>, and get away with it only because glibc's <string.h> also
# declares a (differently-behaved) basename. musl does not, so gcc 14 stops at
# "implicit declaration of function 'basename'" and, worse, would have silently
# truncated the returned pointer through an implicit int on a compiler that
# only warned. 0.7.0 adds both includes. That is the whole delta that matters
# here, and it is the difference between a pin bump and carrying a patch.
#
# THE PREFIX IS BAKED IN AT COMPILE TIME. opkg resolves its own state directory
# from the --prefix it was configured with, NOT from --offline-root, so it must
# be built --prefix=/usr/data/anvil or it will look in the wrong place and
# conclude nothing is installed. Measured on the build host against this exact
# release. Same class of trap as s6's libexecdir and execline's shebangdir
# above; three for three, and the reason $MODDIR now lives in bin/common.sh
# where a recipe cannot spell it differently.
OPKG_VERSION="0.7.0"
OPKG_SHA256="d973fd0f1568f58f87d6aecd9aa95e3e1f60214a45cee26704bf8fe757c54567"
# libarchive is what opkg reads .ipk files with, and it is NOT optional: opkg's
# ./configure hard-fails at the pkg-config check for it (measured -- there is
# no internal tar fallback any more, that went in 0.3.0). BUILD-ONLY and never
# shipped, like skalibs: it is linked into the static binary and there is no
# libarchive.so anywhere in the payload.
#
# Cut down to almost nothing by pkg/opkg/build.sh -- an .ipk is gzip and tar
# and that is the whole requirement, so every other codec is configured out
# rather than cross-built. Bumping this is safe as long as those --without
# flags still name real options; libarchive renames them occasionally, and a
# flag it no longer recognises is only a warning, so check the configure log
# for "unrecognized options" after a bump rather than trusting the exit code.
LIBARCHIVE_VERSION="3.7.9"
LIBARCHIVE_SHA256="aa90732c5a6bdda52fda2ad468ac98d75be981c15dde263d7b5cf6af66fd009f"
# opkg-utils: opkg-build and opkg-make-index, the UPSTREAM tools that build an
# .ipk and index a feed. bin/build-packages.sh drives them rather than
# assembling packages itself -- an earlier revision of this work hand-rolled an
# ar-and-two-tarballs script, and the answer to "is there not already something
# for this" is yes, this.
#
# PINNED BY COMMIT, NOT BY TARBALL, and that is upstream's doing rather than a
# preference: opkg-utils has no release tarball anywhere. git.yoctoproject.org
# is its home and has cgit snapshots DISABLED (every format returns "Unsupported
# snapshot format"), downloads.yoctoproject.org carries opkg but not opkg-utils,
# and the GitHub mirror at shr-project/opkg-utils last saw a commit in 2012 and
# carries no tags. So this is a git sha -- which is a content hash of the whole
# tree, the same guarantee the sha256s above give, expressed in git's own
# spelling. bin/fetch-assets.sh clones and then verifies HEAD against it.
#
# 68a969f is tag 0.7.0. Bump by moving the tag and the sha together.
OPKG_UTILS_VERSION="0.7.0"
OPKG_UTILS_COMMIT="68a969f0e867ace0d94faf8ebe7c7bb67f59d386"