# ⛔ **BLOQUEADA — NO CONSTRUYE TODAVÍA. El muro es del CORPUS, no de qemu.** # # Esta receta queda commiteada con todo el rastro medido porque el diagnóstico vale y porque lo que # falta es una decisión con presupuesto, no un ajuste de perillas. Seis vueltas de `configure`, cada # una descartando una hipótesis con una medición: # # 1. «el venv necesita red» → NO: `python3` trae `venv` y `ensurepip` con su wheel de # pip, así que el venv se crea offline # 2. «falta setuptools» → lo promoví del corpus, y GRATIS (0 de 92 hashes). Falló igual # 3. «falta pip importable» → escribí `py3-pip`; medido DENTRO del sandbox, # `find_spec` da True para los dos. Falló igual # 4. «falta qemu.qmp» → ✅ correcto: descomprimir su wheel vendorizado lo satisfizo # 5. «quedan 3 entradas de cortesía» → ✅ correcto: `[tooling]` trae setuptools/wheel/pip con # sólo `accepted`, detectadas por `importlib.metadata` # 6. el muro de verdad ⇓ # # ══ EL MURO: EL CORPUS NO TIENE PAQUETES DE PYTHON *INSTALADOS*, TIENE DIRECTORIOS COPIADOS ═════ # `[tooling]` incluye `"qemu" = { path = "python/" }`: pip tiene que preparar el paquete propio de # qemu con el gancho PEP 517, y eso revienta así: # # Preparing editable metadata (pyproject.toml) did not run successfully. # AttributeError: 'Distribution' object has no attribute 'entry_points' # …/site-packages/setuptools/config/setupcfg.py:285 # # `py3-setuptools` (y mi `py3-pip`) **copian el árbol del paquete** a `site-packages` — no son # instalaciones: no hay `.dist-info`, no hay metadatos de `entry_points`, y el shim de distutils # queda a medias. Para importar basta; para que setuptools actúe como **backend de build de otro # paquete**, no. # # ⇒ Lo que haría falta es que `py3-setuptools` sea una instalación de verdad (con su `.dist-info`). # **Y eso cuesta**: la receta vive en el corpus con **31 consumidores en `incoming-gnome`**, así que # cambiar su contenido cambia su hash y el de todos ellos. Es exactamente la cascada que la # promoción había evitado, y decidir pagarla es del operador. # # ⚠ Lo que NO hay que hacer: fabricar un `.dist-info` a mano para que `importlib.metadata` conteste. # Resolvería la detección (punto 5) y **no** este muro, porque acá setuptools tiene que ejecutar de # verdad. Sería un verde falso de los que este repo persigue. # # Todo lo demás de esta receta está medido y sirve tal cual el día que eso se destrabe: las perillas, # el porqué de `--without-default-features`, la instalación del wheel vendorizado y el parche de # `pythondeps.toml`. # # qemu 11.1.2 — la máquina virtual. Con esto la distro puede ARRANCAR SUS PROPIAS IMÁGENES. # # ══ POR QUÉ ENTRA, Y POR QUÉ IMPORTA MÁS ACÁ QUE EN OTRA DISTRO ════════════════════════════════ # `censo-distro.py` marcaba «virtualización» **3 de 3 ausente**, y su comentario es el argumento # entero: «ni invitado ni anfitrión: **no se puede probar la propia ISO sin salir**». # # Este proyecto genera imágenes de disco y perfiles de escritorio, y las prueba en QEMU **desde # fuera** — hay memorias enteras sobre arrancar los escritorios en QEMU en otra máquina. O sea que # la distro dependía de un anfitrión ajeno para verificarse a sí misma. Con esta receta, el bucle # «construir imagen → arrancarla → mirarla» se cierra adentro. # # ══ ⚠ `--without-default-features` ES LA CLAVE DE QUE ESTO SEA UNA RECETA Y NO UNA CAMPAÑA ══════ # El `meson.build` de qemu son **5.072 líneas** y nombra docenas de `dependency()`: gnutls, libssh, # libnfs, libiscsi, capstone, libcbor, libdw, libcurl, libgcrypt, epoxy, blkio, igvm, selinux, # pipewire… Ninguna de ellas es obligatoria: **todas cuelgan de un `if get_option(...)`**, y un grep # ingenuo las lista como si lo fueran (lo hice, y daba 20 deps falsas). # # La perilla que lo resuelve es de qemu y está para esto: `--without-default-features` # (`configure:244`) apaga TODO lo opcional, y después se enciende a mano sólo lo que esta distro # necesita. Con eso las deps reales son `glib`, `pixman`, `ncurses`, `zlib` y `libslirp` — todas en # el corpus. # # ══ LO QUE SE ENCIENDE, Y PARA QUÉ SIRVE CADA COSA ═════════════════════════════════════════════ # --target-list=x86_64-softmmu un solo objetivo. Emular un MIPS o un ARM es otra decisión; esto # es para arrancar NUESTRAS imágenes, que son x86-64 # --enable-slirp red sin privilegios (`-netdev user`). ⚠ Desde qemu 7.2 la pila # slirp **dejó de venir incluida**: sin `recipes/libslirp.toml` la # única red sería `tap`, que pide root y un puente. Ver esa receta # --enable-tools `qemu-img` y `qemu-nbd`: crear, convertir y montar las imágenes. # Sin esto hay emulador y nada con qué preparar un disco # --enable-curses ver la VM **en la terminal** (`-display curses`). En una caja sin # escritorio —que es donde se construye— esto es lo que de verdad # se usa # --enable-vnc la otra forma de mirarla, desde otra máquina # --enable-pixman lo piden curses y vnc para el framebuffer # --enable-kvm aceleración por hardware. ⚠ Es de COMPILACIÓN: en `gioser` no hay # `/dev/kvm` (es un vServer, está medido en la memoria del repo) y # qemu cae a TCG solo. Se enciende porque en metal —el objetivo # `metal-tigerlake`— sí lo hay, y apagarlo lo haría imposible allí # --disable-docs sphinx no está en el corpus # # ══ ⚠ EL VENV DE PYTHON: COMPROBADO ANTES, NO DESPUÉS ══════════════════════════════════════════ # El `configure` de qemu crea un entorno virtual de Python (`mkvenv.py create pyvenv`, # `configure:940`) y «llama a `ensurepip` como respaldo cuando hace falta». En un sandbox sin red eso # es un fallo esperando. Medido antes de lanzar: nuestro `python3` trae `venv`, `ensurepip` **con el # wheel `pip-25.0.1` incluido** y `tomllib` — o sea que el venv se crea **offline**. Si algún día el # `python3` del corpus se construye sin `ensurepip`, esta receta se cae y la causa está acá escrita. # # ⚠⚠ **PERO COMPROBAR EL VENV NO ALCANZÓ, Y ÉSTE FUE EL MURO REAL.** El venv se crea bien y después # el configure hace, LITERALMENTE sin condición: # # # Unconditionally install our tooling group. # $mkvenv ensuregroup … pythondeps.toml tooling || exit 1 (configure:988) # # y el grupo `[tooling]` es `"qemu.qmp" = {…}`. qemu **vendoriza** ese wheel —`python/wheels/` trae # `qemu_qmp`, `meson` y `pycotap`— pero para instalarlo pip arranca su trío de bootstrap y murió así: # # ERROR: Could not find a version that satisfies the requirement setuptools>=44.1.1 # (from versions: none) # # El `site-packages` de nuestro `python3` está **vacío** (sólo un README), y no hay red. **No hay # perilla**: `--without-default-features` no toca esto, y el grupo `tooling` es incondicional. # # ⇒ Entra `py3-setuptools`, **y promoverlo salió GRATIS** — medido, no supuesto: la receta ya existía # en `incoming-gnome`, 31 recetas de esa cola dependen de ella, y moverla al corpus cambió **0 de 92 # hashes**. Razón: su única dep es `python3` (runtime), que resuelve al del corpus desde los dos # sitios, así que ni su hash ni los de sus consumidores se mueven. Estaba además ya sellada al mismo # hash, o sea cero reconstrucciones. # # **Que 31 consumidores dependan de algo NO implica cascada.** La cascada aparece cuando las deps # PROPIAS del promovido resuelven distinto — eso es lo que pasó al promover `glib-introspected`, que # depende de `glib` y en el corpus encuentra la estática. Acá no había nada que resolver distinto. name = "qemu" version = "11.1.2" license = "GPL-2.0-only" [source] tarball = "https://download.qemu.org/qemu-11.1.2.tar.xz" sha256 = "731b5681e4bb18be313231579b8efd0296c5b015fa36dc533874b639ba838016" [build] compiler = "zig-cc" target = "x86_64-linux-musl" # `dynamic` y no `static`: sus deps del corpus son todas `.a` hoy, así que el binario sale casi # autosuficiente de todos modos — pero forzar `-static` rompería el día que una de ellas sólo # publique `.so`, que es exactamente lo que le pasó a `gdb` con `gmp`/`mpfr`. link = "dynamic" flags = [] [build.phases] configure = ''' set -e [ -f ./configure ] || { echo "qemu: no hay ./configure" >&2; exit 1; } # ⚠⚠ **SE INSTALA EL WHEEL QUE QEMU YA TRAE, Y ESO EVITA QUE SU BUILD SALGA A LA RED.** # # Su `configure` hace, literalmente sin condición (`configure:988`): # # # Unconditionally install our tooling group. # $mkvenv ensuregroup … pythondeps.toml tooling || exit 1 # # y el grupo `[tooling]` es `"qemu.qmp" = { accepted = ">=0.0.5", installed = "0.0.6" }`. El campo # `accepted` es la clave: significa **«versiones aceptadas si se usa un paquete del sistema»**. Si # `qemu.qmp` ya está instalado, mkvenv no instala NADA y no necesita red. # # Si NO está, intenta instalarlo en el venv — y para eso pip arranca su trío de bootstrap # (`setuptools`, `wheel`, `pip`) **como wheels locales**, que no existen, y muere: # # ERROR: Could not find a version that satisfies the requirement setuptools>=44.1.1 # # ⚠ Y declarar `py3-setuptools` + `py3-pip` NO alcanzó, aunque parecía la cura: comprobado con un # diagnóstico dentro del sandbox, `find_spec` encuentra los dos (`pip: True setuptools: True`). Esos # dos sólo deciden si el venv lleva pip propio (`mkvenv.py:244-251`); no evitan la INSTALACIÓN de # qemu.qmp. Siguen declarados porque esa rama también hace falta, pero no eran el muro. # # ⇒ La salida exacta: **el wheel ya viene en el tarball** (`python/wheels/qemu_qmp-0.0.6-…whl`), y el # layout de un wheel puro ES el layout instalado — trae `qemu/qmp/` y su `.dist-info`. Así que se # descomprime en `site-packages` y queda un paquete del sistema legítimo, con su metadata de verdad # en vez de una fabricada a mano (que es lo que hubiera hecho falta copiando `python/qemu` a pelo: # `importlib.metadata` necesita el `.dist-info` para responder la versión). # # El `/` del sandbox es un `--tmp-overlay`, así que escribir en `site-packages` es legítimo y se # descarta con el build. # ⚠⚠ **Y HAY TRES ENTRADAS MÁS EN `[tooling]` QUE SON COMPROBACIONES DE CORTESÍA.** El grupo no es # sólo `qemu.qmp`: # # "setuptools" = { accepted = ">=44.1.1" } # "wheel" = { accepted = ">=0.34.2" } # # pip should be guaranteed by mkvenv, this is merely a sanity check for # # which version we are counting on being present. # "pip" = { accepted = ">=20.3.4" } # # Las tres tienen **sólo `accepted` y ningún `installed`**: o sea que únicamente se satisfacen con un # paquete del SISTEMA, y mkvenv detecta eso con `importlib.metadata.version()`, que necesita el # `.dist-info`. `py3-setuptools` y `py3-pip` instalan los paquetes **sin metadata** —son copias del # árbol, no instalaciones de wheel— así que mkvenv los ve ausentes, intenta instalarlos y muere sin # red. (Ese fue el muro después de resolver `qemu.qmp`: la lista de «installing» se quedó en esos # tres.) # # ⇒ Se quitan de `pythondeps.toml`, y el argumento es de upstream: su propio comentario dice que la # de `pip` es «**merely a sanity check** for which version we are counting on being present». Las # tres lo son — nada del build las IMPORTA: sirven para instalar, y con `qemu.qmp` ya presente no # queda nada que instalar. # # ⚠ Y se hace ACÁ y no en `py3-setuptools` a propósito: esa receta vive en el corpus con **31 # consumidores en `incoming-gnome`**, y cambiar su contenido cambiaría su hash y el de todos ellos # — la cascada que la promoción había evitado. Añadirles `.dist-info` es su propia unidad de # trabajo, con su propio presupuesto. PD=pythondeps.toml for m in setuptools wheel pip; do 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; } done sed -i -E '/^"(setuptools|wheel|pip)" *=/d' "$PD" for m in setuptools wheel pip; do grep -qE "^\"$m\" *=" "$PD" && { echo "qemu: no pude quitar \"$m\" de $PD" >&2; exit 1; } done grep -q '"qemu.qmp"' "$PD" || { echo "qemu: el sed se llevó qemu.qmp de $PD — eso NO debía pasar" >&2; exit 1; } echo "qemu: quitadas las 3 comprobaciones de cortesía de [tooling]; qemu.qmp intacto" W=$(ls python/wheels/qemu_qmp-*.whl 2>/dev/null | head -1) [ -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; } SP=$(python3 -c 'import sysconfig; print(sysconfig.get_path("purelib"))') python3 -m zipfile -e "$W" "$SP" || { echo "qemu: no pude descomprimir $W en $SP" >&2; exit 1; } # Comprobar lo que mkvenv va a preguntar: el módulo Y su versión por metadata. python3 - <<'PYEOF' || exit 1 import importlib.util as u from importlib.metadata import version, PackageNotFoundError if not u.find_spec("qemu.qmp"): raise SystemExit("qemu: qemu.qmp no es importable tras instalar el wheel") try: v = version("qemu.qmp") except PackageNotFoundError: raise SystemExit("qemu: qemu.qmp importable pero SIN metadata — mkvenv no podría leer su version y lo reinstalaria") print("qemu: qemu.qmp", v, "instalado como paquete del sistema (sin red)") PYEOF ./configure --prefix=/usr --sysconfdir=/etc \ --without-default-features \ --target-list=x86_64-softmmu \ --enable-slirp --enable-tools --enable-pixman \ --enable-curses --enable-vnc --enable-kvm \ --disable-docs --disable-werror ''' compile = 'make -j"$(nproc)"' # ⚠ EL GUARDIÁN LO EJECUTA, Y COMPRUEBA LA RED DONDE SE VE DE VERDAD. # # Que `qemu-system-x86_64` exista no dice nada: un emulador sin su tabla de máquinas o sin red es un # binario que arranca y no puede arrancar NADA. Y `--enable-slirp` por sí solo no garantiza que # slirp entrara: su `dependency()` podría haber fallado en silencio con otra combinación de # perillas. # # `-netdev help` lista los backends REGISTRADOS, y `user` aparece **sólo si slirp se enlazó**. Eso # es la prueba, no el flag de configure. Lo mismo `-machine help` para las máquinas y `qemu-img` # creando una imagen de verdad: tres comprobaciones de extremo a extremo que no necesitan ni KVM ni # `/dev` (el sandbox va con `--unshare-all`, así que arrancar una VM sí está fuera de alcance). install = ''' set -e make install DESTDIR=/out Q=/out/usr/bin/qemu-system-x86_64 [ -x "$Q" ] || { echo "qemu: no quedó qemu-system-x86_64 — NO sello" >&2; ls /out/usr/bin 2>/dev/null >&2; exit 1; } [ -x /out/usr/bin/qemu-img ] || { echo "qemu: falta qemu-img — habría emulador y nada con qué preparar un disco" >&2; exit 1; } # Ejecución y truncado SEPARADOS: el estado de una tubería es el del último comando. sal=$("$Q" --version 2>&1) || { echo "qemu: el emulador no arranca" >&2; printf '%s\n' "$sal" >&2; exit 1; } printf '%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; } # LA RED: `user` sólo aparece si slirp se enlazó de verdad. nd=$("$Q" -netdev help 2>&1) || true printf '%s\n' "$nd" | grep -qw user || { echo "qemu: '-netdev help' no lista 'user' — slirp NO entró, así que la única red sería tap (root + puente)" >&2 printf '%s\n' "$nd" | head -10 >&2 exit 1 } # LAS MÁQUINAS: sin tabla de máquinas no se puede arrancar nada. mq=$("$Q" -machine help 2>&1) || true printf '%s\n' "$mq" | grep -qE '^q35|pc-q35|^pc ' || { echo "qemu: '-machine help' no lista q35 ni pc — el emulador no tiene con qué arrancar una imagen" >&2 printf '%s\n' "$mq" | head -8 >&2 exit 1 } # LAS HERRAMIENTAS, de extremo a extremo: crear una imagen y volver a leerla. t=$(mktemp -d) /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; } /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; } echo "qemu: $(printf '%s\n' "$sal" | head -1) · red user (slirp) + $(printf '%s\n' "$mq" | grep -c . ) máquinas + qemu-img" ''' [deps] build = ["make", "meson", "samurai", "pkgconf", "python3", "glib", "pixman", "ncurses", "zlib", "libslirp", "libffi", "pcre2", "libseccomp", "binutils", "bison", "flex", # ⚠ Los DOS, y `py3-pip` por una razón que no es «instalar cosas»: `mkvenv.py:244-251` decide # con `find_spec("setuptools") and find_spec("pip")`. Con setuptools pero SIN pip importable # toma la rama del `ensurepip`, y en Python 3.12 **ensurepip ya no incluye setuptools** — así # que ese camino va al índice y muere sin red. Con los dos presentes crea el venv # `with_pip=False` y resuelve su wheel vendorizado con el pip del sistema. Ver # `recipes/py3-pip.toml`. "py3-setuptools", "py3-pip"]