tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definiciones

⎇ b0085e7cf6978a7f9e27aa863f756a463fc1407e recipes/qemu.toml TOML · 278 líneas · 17.4 KB Historia crudo

qemu: receta completa y BLOQUEADA — el muro es del corpus, no de qemu 485cd0747 · Sergio ·

1# ⛔ **BLOQUEADA — NO CONSTRUYE TODAVÍA. El muro es del CORPUS, no de qemu.**
2#
3# Esta receta queda commiteada con todo el rastro medido porque el diagnóstico vale y porque lo que
4# falta es una decisión con presupuesto, no un ajuste de perillas. Seis vueltas de `configure`, cada
5# una descartando una hipótesis con una medición:
6#
7#   1. «el venv necesita red»            → NO: `python3` trae `venv` y `ensurepip` con su wheel de
8#                                          pip, así que el venv se crea offline
9#   2. «falta setuptools»                → lo promoví del corpus, y GRATIS (0 de 92 hashes). Falló igual
10#   3. «falta pip importable»            → escribí `py3-pip`; medido DENTRO del sandbox,
11#                                          `find_spec` da True para los dos. Falló igual
12#   4. «falta qemu.qmp»                  → ✅ correcto: descomprimir su wheel vendorizado lo satisfizo
13#   5. «quedan 3 entradas de cortesía»   → ✅ correcto: `[tooling]` trae setuptools/wheel/pip con
14#                                          sólo `accepted`, detectadas por `importlib.metadata`
15#   6. el muro de verdad ⇓
16#
17# ══ EL MURO: EL CORPUS NO TIENE PAQUETES DE PYTHON *INSTALADOS*, TIENE DIRECTORIOS COPIADOS ═════
18# `[tooling]` incluye `"qemu" = { path = "python/" }`: pip tiene que preparar el paquete propio de
19# qemu con el gancho PEP 517, y eso revienta así:
20#
21#     Preparing editable metadata (pyproject.toml) did not run successfully.
22#     AttributeError: 'Distribution' object has no attribute 'entry_points'
23#       …/site-packages/setuptools/config/setupcfg.py:285
24#
25# `py3-setuptools` (y mi `py3-pip`) **copian el árbol del paquete** a `site-packages` — no son
26# instalaciones: no hay `.dist-info`, no hay metadatos de `entry_points`, y el shim de distutils
27# queda a medias. Para importar basta; para que setuptools actúe como **backend de build de otro
28# paquete**, no.
29#
30# ⇒ Lo que haría falta es que `py3-setuptools` sea una instalación de verdad (con su `.dist-info`).
31# **Y eso cuesta**: la receta vive en el corpus con **31 consumidores en `incoming-gnome`**, así que
32# cambiar su contenido cambia su hash y el de todos ellos. Es exactamente la cascada que la
33# promoción había evitado, y decidir pagarla es del operador.
34#
35# ⚠ Lo que NO hay que hacer: fabricar un `.dist-info` a mano para que `importlib.metadata` conteste.
36# Resolvería la detección (punto 5) y **no** este muro, porque acá setuptools tiene que ejecutar de
37# verdad. Sería un verde falso de los que este repo persigue.
38#
39# Todo lo demás de esta receta está medido y sirve tal cual el día que eso se destrabe: las perillas,
40# el porqué de `--without-default-features`, la instalación del wheel vendorizado y el parche de
41# `pythondeps.toml`.
42#
43# qemu 11.1.2 — la máquina virtual. Con esto la distro puede ARRANCAR SUS PROPIAS IMÁGENES.
44#
45# ══ POR QUÉ ENTRA, Y POR QUÉ IMPORTA MÁS ACÁ QUE EN OTRA DISTRO ════════════════════════════════
46# `censo-distro.py` marcaba «virtualización» **3 de 3 ausente**, y su comentario es el argumento
47# entero: «ni invitado ni anfitrión: **no se puede probar la propia ISO sin salir**».
48#
49# Este proyecto genera imágenes de disco y perfiles de escritorio, y las prueba en QEMU **desde
50# fuera** — hay memorias enteras sobre arrancar los escritorios en QEMU en otra máquina. O sea que
51# la distro dependía de un anfitrión ajeno para verificarse a sí misma. Con esta receta, el bucle
52# «construir imagen → arrancarla → mirarla» se cierra adentro.
53#
54# ══ ⚠ `--without-default-features` ES LA CLAVE DE QUE ESTO SEA UNA RECETA Y NO UNA CAMPAÑA ══════
55# El `meson.build` de qemu son **5.072 líneas** y nombra docenas de `dependency()`: gnutls, libssh,
56# libnfs, libiscsi, capstone, libcbor, libdw, libcurl, libgcrypt, epoxy, blkio, igvm, selinux,
57# pipewire… Ninguna de ellas es obligatoria: **todas cuelgan de un `if get_option(...)`**, y un grep
58# ingenuo las lista como si lo fueran (lo hice, y daba 20 deps falsas).
59#
60# La perilla que lo resuelve es de qemu y está para esto: `--without-default-features`
61# (`configure:244`) apaga TODO lo opcional, y después se enciende a mano sólo lo que esta distro
62# necesita. Con eso las deps reales son `glib`, `pixman`, `ncurses`, `zlib` y `libslirp` — todas en
63# el corpus.
64#
65# ══ LO QUE SE ENCIENDE, Y PARA QUÉ SIRVE CADA COSA ═════════════════════════════════════════════
66#   --target-list=x86_64-softmmu   un solo objetivo. Emular un MIPS o un ARM es otra decisión; esto
67#                                  es para arrancar NUESTRAS imágenes, que son x86-64
68#   --enable-slirp                 red sin privilegios (`-netdev user`). ⚠ Desde qemu 7.2 la pila
69#                                  slirp **dejó de venir incluida**: sin `recipes/libslirp.toml` la
70#                                  única red sería `tap`, que pide root y un puente. Ver esa receta
71#   --enable-tools                 `qemu-img` y `qemu-nbd`: crear, convertir y montar las imágenes.
72#                                  Sin esto hay emulador y nada con qué preparar un disco
73#   --enable-curses                ver la VM **en la terminal** (`-display curses`). En una caja sin
74#                                  escritorio —que es donde se construye— esto es lo que de verdad
75#                                  se usa
76#   --enable-vnc                   la otra forma de mirarla, desde otra máquina
77#   --enable-pixman                lo piden curses y vnc para el framebuffer
78#   --enable-kvm                   aceleración por hardware. ⚠ Es de COMPILACIÓN: en `gioser` no hay
79#                                  `/dev/kvm` (es un vServer, está medido en la memoria del repo) y
80#                                  qemu cae a TCG solo. Se enciende porque en metal —el objetivo
81#                                  `metal-tigerlake`— sí lo hay, y apagarlo lo haría imposible allí
82#   --disable-docs                 sphinx no está en el corpus
83#
84# ══ ⚠ EL VENV DE PYTHON: COMPROBADO ANTES, NO DESPUÉS ══════════════════════════════════════════
85# El `configure` de qemu crea un entorno virtual de Python (`mkvenv.py create pyvenv`,
86# `configure:940`) y «llama a `ensurepip` como respaldo cuando hace falta». En un sandbox sin red eso
87# es un fallo esperando. Medido antes de lanzar: nuestro `python3` trae `venv`, `ensurepip` **con el
88# wheel `pip-25.0.1` incluido** y `tomllib` — o sea que el venv se crea **offline**. Si algún día el
89# `python3` del corpus se construye sin `ensurepip`, esta receta se cae y la causa está acá escrita.
90#
91# ⚠⚠ **PERO COMPROBAR EL VENV NO ALCANZÓ, Y ÉSTE FUE EL MURO REAL.** El venv se crea bien y después
92# el configure hace, LITERALMENTE sin condición:
93#
94#     # Unconditionally install our tooling group.
95#     $mkvenv ensuregroup … pythondeps.toml tooling || exit 1        (configure:988)
96#
97# y el grupo `[tooling]` es `"qemu.qmp" = {…}`. qemu **vendoriza** ese wheel —`python/wheels/` trae
98# `qemu_qmp`, `meson` y `pycotap`— pero para instalarlo pip arranca su trío de bootstrap y murió así:
99#
100#     ERROR: Could not find a version that satisfies the requirement setuptools>=44.1.1
101#            (from versions: none)
102#
103# El `site-packages` de nuestro `python3` está **vacío** (sólo un README), y no hay red. **No hay
104# perilla**: `--without-default-features` no toca esto, y el grupo `tooling` es incondicional.
105#
106# ⇒ Entra `py3-setuptools`, **y promoverlo salió GRATIS** — medido, no supuesto: la receta ya existía
107# en `incoming-gnome`, 31 recetas de esa cola dependen de ella, y moverla al corpus cambió **0 de 92
108# hashes**. Razón: su única dep es `python3` (runtime), que resuelve al del corpus desde los dos
109# sitios, así que ni su hash ni los de sus consumidores se mueven. Estaba además ya sellada al mismo
110# hash, o sea cero reconstrucciones.
111#
112# **Que 31 consumidores dependan de algo NO implica cascada.** La cascada aparece cuando las deps
113# PROPIAS del promovido resuelven distinto — eso es lo que pasó al promover `glib-introspected`, que
114# depende de `glib` y en el corpus encuentra la estática. Acá no había nada que resolver distinto.
115name    = "qemu"
116version = "11.1.2"
117license = "GPL-2.0-only"
118
119[source]
120tarball = "https://download.qemu.org/qemu-11.1.2.tar.xz"
121sha256  = "731b5681e4bb18be313231579b8efd0296c5b015fa36dc533874b639ba838016"
122
123[build]
124compiler = "zig-cc"
125target   = "x86_64-linux-musl"
126# `dynamic` y no `static`: sus deps del corpus son todas `.a` hoy, así que el binario sale casi
127# autosuficiente de todos modos — pero forzar `-static` rompería el día que una de ellas sólo
128# publique `.so`, que es exactamente lo que le pasó a `gdb` con `gmp`/`mpfr`.
129link     = "dynamic"
130flags    = []
131
132[build.phases]
133configure = '''
134set -e
135[ -f ./configure ] || { echo "qemu: no hay ./configure" >&2; exit 1; }
136
137# ⚠⚠ **SE INSTALA EL WHEEL QUE QEMU YA TRAE, Y ESO EVITA QUE SU BUILD SALGA A LA RED.**
138#
139# Su `configure` hace, literalmente sin condición (`configure:988`):
140#
141#     # Unconditionally install our tooling group.
142#     $mkvenv ensuregroup … pythondeps.toml tooling || exit 1
143#
144# y el grupo `[tooling]` es `"qemu.qmp" = { accepted = ">=0.0.5", installed = "0.0.6" }`. El campo
145# `accepted` es la clave: significa **«versiones aceptadas si se usa un paquete del sistema»**. Si
146# `qemu.qmp` ya está instalado, mkvenv no instala NADA y no necesita red.
147#
148# Si NO está, intenta instalarlo en el venv — y para eso pip arranca su trío de bootstrap
149# (`setuptools`, `wheel`, `pip`) **como wheels locales**, que no existen, y muere:
150#
151#     ERROR: Could not find a version that satisfies the requirement setuptools>=44.1.1
152#
153# ⚠ Y declarar `py3-setuptools` + `py3-pip` NO alcanzó, aunque parecía la cura: comprobado con un
154# diagnóstico dentro del sandbox, `find_spec` encuentra los dos (`pip: True setuptools: True`). Esos
155# dos sólo deciden si el venv lleva pip propio (`mkvenv.py:244-251`); no evitan la INSTALACIÓN de
156# qemu.qmp. Siguen declarados porque esa rama también hace falta, pero no eran el muro.
157#
158# ⇒ La salida exacta: **el wheel ya viene en el tarball** (`python/wheels/qemu_qmp-0.0.6-…whl`), y el
159# layout de un wheel puro ES el layout instalado — trae `qemu/qmp/` y su `.dist-info`. Así que se
160# descomprime en `site-packages` y queda un paquete del sistema legítimo, con su metadata de verdad
161# en vez de una fabricada a mano (que es lo que hubiera hecho falta copiando `python/qemu` a pelo:
162# `importlib.metadata` necesita el `.dist-info` para responder la versión).
163#
164# El `/` del sandbox es un `--tmp-overlay`, así que escribir en `site-packages` es legítimo y se
165# descarta con el build.
166# ⚠⚠ **Y HAY TRES ENTRADAS MÁS EN `[tooling]` QUE SON COMPROBACIONES DE CORTESÍA.** El grupo no es
167# sólo `qemu.qmp`:
168#
169#     "setuptools" = { accepted = ">=44.1.1" }
170#     "wheel"      = { accepted = ">=0.34.2" }
171#     # pip should be guaranteed by mkvenv, this is merely a sanity check for
172#     # which version we are counting on being present.
173#     "pip"        = { accepted = ">=20.3.4" }
174#
175# Las tres tienen **sólo `accepted` y ningún `installed`**: o sea que únicamente se satisfacen con un
176# paquete del SISTEMA, y mkvenv detecta eso con `importlib.metadata.version()`, que necesita el
177# `.dist-info`. `py3-setuptools` y `py3-pip` instalan los paquetes **sin metadata** —son copias del
178# árbol, no instalaciones de wheel— así que mkvenv los ve ausentes, intenta instalarlos y muere sin
179# red. (Ese fue el muro después de resolver `qemu.qmp`: la lista de «installing» se quedó en esos
180# tres.)
181#
182# ⇒ Se quitan de `pythondeps.toml`, y el argumento es de upstream: su propio comentario dice que la
183# de `pip` es «**merely a sanity check** for which version we are counting on being present». Las
184# tres lo son — nada del build las IMPORTA: sirven para instalar, y con `qemu.qmp` ya presente no
185# queda nada que instalar.
186#
187# ⚠ Y se hace ACÁ y no en `py3-setuptools` a propósito: esa receta vive en el corpus con **31
188# consumidores en `incoming-gnome`**, y cambiar su contenido cambiaría su hash y el de todos ellos
189# — la cascada que la promoción había evitado. Añadirles `.dist-info` es su propia unidad de
190# trabajo, con su propio presupuesto.
191PD=pythondeps.toml
192for m in setuptools wheel pip; do
193  grep -qE "^\"$m\" *=" "$PD" || { echo "qemu: $PD ya no declara \"$m\" en [tooling] — upstream lo cambió; REVISAR si este parche sigue haciendo falta" >&2; exit 1; }
194done
195sed -i -E '/^"(setuptools|wheel|pip)" *=/d' "$PD"
196for m in setuptools wheel pip; do
197  grep -qE "^\"$m\" *=" "$PD" && { echo "qemu: no pude quitar \"$m\" de $PD" >&2; exit 1; }
198done
199grep -q '"qemu.qmp"' "$PD" || { echo "qemu: el sed se llevó qemu.qmp de $PD — eso NO debía pasar" >&2; exit 1; }
200echo "qemu: quitadas las 3 comprobaciones de cortesía de [tooling]; qemu.qmp intacto"
201
202W=$(ls python/wheels/qemu_qmp-*.whl 2>/dev/null | head -1)
203[ -n "$W" ] || { echo "qemu: no hay python/wheels/qemu_qmp-*.whl — upstream dejó de vendorizarlo y este build necesitaría red" >&2; ls python/wheels 2>/dev/null >&2; exit 1; }
204SP=$(python3 -c 'import sysconfig; print(sysconfig.get_path("purelib"))')
205python3 -m zipfile -e "$W" "$SP" || { echo "qemu: no pude descomprimir $W en $SP" >&2; exit 1; }
206# Comprobar lo que mkvenv va a preguntar: el módulo Y su versión por metadata.
207python3 - <<'PYEOF' || exit 1
208import importlib.util as u
209from importlib.metadata import version, PackageNotFoundError
210if not u.find_spec("qemu.qmp"):
211    raise SystemExit("qemu: qemu.qmp no es importable tras instalar el wheel")
212try:
213    v = version("qemu.qmp")
214except PackageNotFoundError:
215    raise SystemExit("qemu: qemu.qmp importable pero SIN metadata — mkvenv no podría leer su version y lo reinstalaria")
216print("qemu: qemu.qmp", v, "instalado como paquete del sistema (sin red)")
217PYEOF
218./configure --prefix=/usr --sysconfdir=/etc \
219  --without-default-features \
220  --target-list=x86_64-softmmu \
221  --enable-slirp --enable-tools --enable-pixman \
222  --enable-curses --enable-vnc --enable-kvm \
223  --disable-docs --disable-werror
224'''
225compile = 'make -j"$(nproc)"'
226# ⚠ EL GUARDIÁN LO EJECUTA, Y COMPRUEBA LA RED DONDE SE VE DE VERDAD.
227#
228# Que `qemu-system-x86_64` exista no dice nada: un emulador sin su tabla de máquinas o sin red es un
229# binario que arranca y no puede arrancar NADA. Y `--enable-slirp` por sí solo no garantiza que
230# slirp entrara: su `dependency()` podría haber fallado en silencio con otra combinación de
231# perillas.
232#
233# `-netdev help` lista los backends REGISTRADOS, y `user` aparece **sólo si slirp se enlazó**. Eso
234# es la prueba, no el flag de configure. Lo mismo `-machine help` para las máquinas y `qemu-img`
235# creando una imagen de verdad: tres comprobaciones de extremo a extremo que no necesitan ni KVM ni
236# `/dev` (el sandbox va con `--unshare-all`, así que arrancar una VM sí está fuera de alcance).
237install = '''
238set -e
239make install DESTDIR=/out
240Q=/out/usr/bin/qemu-system-x86_64
241[ -x "$Q" ] || { echo "qemu: no quedó qemu-system-x86_64 — NO sello" >&2; ls /out/usr/bin 2>/dev/null >&2; exit 1; }
242[ -x /out/usr/bin/qemu-img ] || { echo "qemu: falta qemu-img — habría emulador y nada con qué preparar un disco" >&2; exit 1; }
243
244# Ejecución y truncado SEPARADOS: el estado de una tubería es el del último comando.
245sal=$("$Q" --version 2>&1) || { echo "qemu: el emulador no arranca" >&2; printf '%s\n' "$sal" >&2; exit 1; }
246printf '%s\n' "$sal" | head -1 | grep -qE 'QEMU emulator version 11\.1\.2' || { echo "qemu: --version inesperado: $(printf '%s\n' "$sal" | head -1)" >&2; exit 1; }
247
248# LA RED: `user` sólo aparece si slirp se enlazó de verdad.
249nd=$("$Q" -netdev help 2>&1) || true
250printf '%s\n' "$nd" | grep -qw user || {
251  echo "qemu: '-netdev help' no lista 'user' — slirp NO entró, así que la única red sería tap (root + puente)" >&2
252  printf '%s\n' "$nd" | head -10 >&2
253  exit 1
254}
255# LAS MÁQUINAS: sin tabla de máquinas no se puede arrancar nada.
256mq=$("$Q" -machine help 2>&1) || true
257printf '%s\n' "$mq" | grep -qE '^q35|pc-q35|^pc ' || {
258  echo "qemu: '-machine help' no lista q35 ni pc — el emulador no tiene con qué arrancar una imagen" >&2
259  printf '%s\n' "$mq" | head -8 >&2
260  exit 1
261}
262# LAS HERRAMIENTAS, de extremo a extremo: crear una imagen y volver a leerla.
263t=$(mktemp -d)
264/out/usr/bin/qemu-img create -f qcow2 "$t/p.qcow2" 16M >/dev/null 2>&1 || { echo "qemu: qemu-img no pudo crear un qcow2" >&2; exit 1; }
265/out/usr/bin/qemu-img info "$t/p.qcow2" 2>&1 | grep -q 'qcow2' || { echo "qemu: qemu-img creó una imagen que no puede volver a leer" >&2; exit 1; }
266echo "qemu: $(printf '%s\n' "$sal" | head -1) · red user (slirp) + $(printf '%s\n' "$mq" | grep -c . ) máquinas + qemu-img"
267'''
268
269[deps]
270build = ["make", "meson", "samurai", "pkgconf", "python3", "glib", "pixman", "ncurses",
271         "zlib", "libslirp", "libffi", "pcre2", "libseccomp", "binutils", "bison", "flex",
272         # ⚠ Los DOS, y `py3-pip` por una razón que no es «instalar cosas»: `mkvenv.py:244-251` decide
273         # con `find_spec("setuptools") and find_spec("pip")`. Con setuptools pero SIN pip importable
274         # toma la rama del `ensurepip`, y en Python 3.12 **ensurepip ya no incluye setuptools** — así
275         # que ese camino va al índice y muere sin red. Con los dos presentes crea el venv
276         # `with_pip=False` y resuelve su wheel vendorizado con el pip del sistema. Ver
277         # `recipes/py3-pip.toml`.
278         "py3-setuptools", "py3-pip"]