Files
firmware/pkgs/klipper/pkg.conf
Oleksandr ShyshatskyiandClaude Opus 5 28eb20f3da Package numpy, because klippy does not start without it
extras/stepper_resonance_tester.py opens with a bare `import numpy as np`;
klippy.py:122 walks EVERY config section through a load_object whose
importlib.import_module is not wrapped in except ImportError; printer.base.cfg
includes FlashForge's printer.vibration.cfg, which declares
[stepper_resonance_tester]. Since klippy moved to our own CPython 3.13 that has
not been a printer missing resonance testing. It has been a klippy that never
reaches ready.

So this is not a scientific extra. anvil-klipper depends on anvil-python-numpy
the way it depends on cffi, except that the others mean "klippy imports this"
and this one means "klippy starts". It also gives back SHAPER_CALIBRATE and
TEST_RESONANCES, dead since the same switch, and the VFA calibration behind
STEPPER_RESONANCE_FACTORY_CALIBRATE that docs/notes/44-vfa-calibration.md
traces end to end.

WHAT WAS NOT OBVIOUS, in the order it bit.

numpy 2.x builds with meson-python, and meson has never heard of
_PYTHON_SYSCONFIGDATA_NAME -- the trick every other python-* recipe here leans
on. pkg_mesoncross writes a real cross file instead, naming the $PKG_XW
wrappers rather than the raw toolchain: the wrappers carry -EL -mnan=2008, and
a cross file that reaches past them builds a tree that passes every gate which
only reads symbols and is refused at exec() on the printer. pkg_pywheel grew a
PKG_PY_PIP_ARGS branch to pass the file as --config-settings, which is the one
thing PEP 517 does standardise.

The cross file's `python =` is the BUILD python, and that is not a mistake:
meson introspects an interpreter by running it and asking sysconfig, and
pkg_pytarget has already made that sysconfig answer for mipsel. The old trick
does reach meson, just not by its own mechanism.

numpy's meson.build hard-errors below gcc 8.4 and ours is 7.2. The gate is the
SciPy Toolchain Roadmap's support statement, not a requirement of the code:
this toolchain compiles if constexpr, structured bindings, std::optional,
std::string_view, and pocketfft_hdronly.h -- 3635 lines of the heaviest
template C++ in the tree, and the half np.fft.rfft needs. Probed before the
floor was lowered, and lowered rather than removed, behind a guard that fails
loudly if upstream moves the check. 19 extension modules, every one at e_flags
0x70001405/0x70001407, 14MB shipped after pruning tests/, f2py/, stubs and
static libs.

meson-python is self-hosting, so pkg_buildpython runs meson and ninja just to
install the backend -- hence ninja-build in the image and PYPKG_HOST_LIST in
dependency order rather than alphabetical. And pip puts meson and cython in
work/.py-host/bin, which nothing had ever looked in: without that on PATH the
backend fails to install with `meson executable "meson" not found` while pip
list shows it right there.

THE GATE THAT WOULD HAVE CAUGHT ALL OF IT is qa/replica/test_klippy_extras_import.py:
walk the real /usr/data/config include graph, map every section to its extra,
import each on the printer's own interpreter. It needs no MCU, because config
load happens before klippy:mcu_identify. Nothing asked that question before,
which is how two comments in this repo came to assert the module guarded its
own import. test_numpy.py asks for numeric answers rather than a module object
-- linalg.lstsq specifically, because -Dallow-noblas selects the bundled
lapack_lite and _r2_sinusoidal_fit calls it every calibration round. And
test_install.py's include check read only ff-*.cfg, so FlashForge's four --
printer.vibration.cfg among them -- were never asked about at all.

ff-toolchange.cfg wraps the two search commands in _FF_SHAPER_PREP, as
SHAPER_CALIBRATE already was. STEPPER_RESONANCE_FACTORY_CALIBRATE is
deliberately NOT wrapped: it is a gcode_macro, not a registered command, and
same-named sections merge under RawConfigParser(strict=False) -- a wrapper
would not wrap it, it would BE it, with rename_existing pointing at itself. It
needs none, since its body is two calls to a command that is wrapped. No
SAVE_CONFIG in any macro either; it restarts klippy and stays the operator's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJr2x7aDaY889qwSxbdLt8
2026-08-31 13:42:57 +03:00

63 lines
3.2 KiB
Plaintext

# anvil-klipper -- the Klipper4FlashForge fork: the whole klippy tree, and
# c_helper.so cross-compiled from the chelper sources shipped beside it.
#
# ARCHITECTURE IS THE DEFAULT, mipsel_xburst2, and that is not a formality:
# c_helper.so is the only ELF in the tree and the kernel refuses anything that
# is not o32/nan2008/mips32r2. An `all` here would let this install on a
# machine whose kernel cannot exec the one file that matters.
PKG_NAME=anvil-klipper
# THE PIN IS A COMMIT, composed as pkgs/moonraker composes its own: the
# release this fork descends from, then the short sha. The fork carries no
# version of its own, so 0.13.0+git<sha> is what opkg can order and a bare
# 40-character sha is not.
PKG_VERSION="0.13.0+git$(printf '%.8s' "$KLIPPER_VERSION")"
PKG_RELEASE=1
PKG_SECTION=base
PKG_EXCLUDE=".version"
# THE INTERPRETER IS A DEPENDS: etc/s6-rc/source/klipper/run execs $FF_PYTHON
# against $MODDIR/klipper/klippy, so klippy is on our CPython 3.13 and the
# tree it runs is the one THIS package installs.
#
# THE LIST IS scripts/klippy-requirements.txt MINUS WHAT IS NOT REACHABLE
# HERE. python-can is not packaged (no CAN transport on this machine) and it
# is import-guarded, so its absence costs nothing.
#
# NUMPY IS NOT IN THAT CLASS, and it is the one Depends here whose absence is
# not a missing feature but a dead printer. extras/stepper_resonance_tester.py
# opens with a bare `import numpy as np`, and klippy.py:122 walks every config
# section through a load_object that does not catch ImportError -- so with
# printer.base.cfg including FlashForge's printer.vibration.cfg
# ([stepper_resonance_tester]), a numpy-less install is a klippy that never
# reaches ready. Read the rest of this list as "klippy imports it"; read this
# one as "klippy does not start". docs/notes/44-vfa-calibration.md has the
# chain.
#
# markupsafe and setuptools are not named
# because anvil-python-jinja2 declares the first and nothing needs the second.
#
# WHAT THIS FIXES: cffi and greenlet were installed by a hardcoded list in
# bin/payload.sh, so every build had them and no `opkg install anvil-klipper`
# did. pyserial and jinja2 were worse -- they arrived only as
# anvil-moonraker's Depends, so a BUILD_MOONRAKER=0 build shipped a klippy
# that imports serialhdl and dies the first time it opens an MCU.
#
# anvil-klipper-config is not optional either: printer.base.cfg is what
# printer.cfg includes and every other config hangs off it.
PKG_DEPENDS="anvil-klipper-config, anvil-python, anvil-python-cffi,
anvil-python-greenlet, anvil-python-pyserial,
anvil-python-jinja2, anvil-python-numpy"
PKG_BUILD_DEPENDS=""
# NO PKG_WHEN. The fork is the only Klipper, and nothing here is optional --
# the toolchanger extras this package carries and the ff-*.cfg that declare
# them are written against v0.13.
# KLIPPER_VERSION says nothing about payload/, which is five toolchanger
# extras -- so without this an edit to ff_toolchange.py reads as "already
# current" and the printer gets the previous release's.
PKG_STAMP_EXTRA="$(pkg_payload_hash)"
PKG_DESCRIPTION="The Klipper4FlashForge fork of Klipper (v0.13 generation), with c_helper.so cross-compiled from the chelper sources it ships with."