rustc desde fuente está compilando el stage1 en el worker. El documento recoge el frente entero: el hueco medido (12 crates y 42 555 líneas de Rust, 44 recetas `cargo-*`, CERO de rustc, y el `rustc` que construye todo eso saliendo del LAB — que ENTRA en el ArtifactHash), los tres escalones, y lo que costó cada uno. Los cinco muros del escalón 2, todos con un mensaje que no nombra la causa: 1. `llvm18` no sirve: `bad LLVM version, need >=21`. Mínimos medidos en el propio bootstrap (1.87→18 · 1.90→19 · 1.93/1.95→20 · 1.97→21). Bajar de rustc no es salida porque el stage0 de N es N-1 o N. De ahí `llvm21` — y `llvm18` se queda, que lo usa mesa. 2. **El toolchain musl oficial no puede producir proc-macros** (crt-static=true ⇒ sin dylibs): muere compilando el propio bootstrap de rustc, que usa `clap` con derive. Sirve para compilar programas, no para arrancar el build de rustc. El stage0 es el del lab. 3. x.py DESCARGA el stage0 si no le nombras el local: en un sandbox sin red, `RuntimeError: failed verification` en `download_toolchain()`, que no menciona ni la red ni el stage0. 4. El triple de Alpine no es el canónico: sin fijarlo, la sección `[target.…-unknown-…]` no aplica a nada y x.py se pone a construir LLVM solo; forzando el canónico, el stage0 no tiene su std. 5. cmake + zig = «compiler broken» en la prueba de ABI. CC=gcc, igual que llvm21 y cmake. Y del escalón 1, lo que no estaba en el grafo: el prebuilt no ejecuta sin `musl-shared` (cargador) y sin `gcc-libs` (`libgcc_s.so.1`) — dos paquetes sellados y en el perfil equivocado. Más una propiedad que vale: no hace falta un `cc`, el toolchain trae su propio lld. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
5.2 KiB
SDD 31 — El compilador de Rust: de «lo presta el lab» a «es nuestro»
«sin compilador de rust, takana no tiene sentido» — el usuario, 2026-09-14.
Tiene razón por una vía que no es de gusto, y conviene escribirla entera antes de discutir cómo.
1. El hueco, medido
| pregunta | respuesta (2026-09-14) |
|---|---|
| ¿cuánto de takana es Rust? | 12 crates, 42 555 líneas — todo el instrumental |
recetas cargo-* en el corpus |
44 |
recetas de rustc o cargo |
0 |
¿de dónde sale el rustc que construye todo eso? |
del LAB (.dev-fs/alpine), un rootfs ajeno |
Y el lab entra en el ArtifactHash. O sea: la distro depende de bytes de otro para poder
reconstruir su propio instrumental. No se ve porque funciona — es la misma forma de
subcomando-sin-driver, a escala de toolchain: 44 subcomandos de cargo y ningún cargo.
2. Los tres escalones
rust-toolchain-bin— el tarball oficial de rust-lang, verificado por su sha256 y sellado tal cual conforeign = true(bytes ajenos: claseajeno, fuera del recuento del corpus). Hecho y probado en la caja:rustc 1.97.0,cargo 1.97.0, y un binario compilado que corre.rustdesde fuente — conllvm21del corpus y el stage0 del lab. En curso.- La cadena
mrustc → 1.90 → 1.91.0 → 1.91.1del selfhost (docs/10-roadmap.md,docs/11-bootstrap.md), que quita hasta el escalón 1. Pendiente.
3. Lo que costó el escalón 1, y no estaba en el grafo
El artefacto sella (799 M) y no ejecuta hasta tener dos cosas que la distro publicaba por
separado — las dos sealed, las dos en el perfil equivocado:
- El cargador musl.
rustces PIE dinámico (NEEDED librustc_driver-*.so,NEEDED libc.so). En el worker ni arranca (cannot execute: required file not found, que es como se ve la falta del intérprete); en la caja sí, porque ahí estámusl-shared. libgcc_s.so.1(_Unwind_Resume: symbol not found). Lo publicagcc-libs, que estaba declarado sólo en los cuatro perfiles de escritorio.
Y no hace falta un cc. El primer rustc hola.rs falla con linker \cc` not found, y la respuesta no es traer un compilador de C: el toolchain **trae su propio lld** (rustlib//bin/rust-lld). Con -C linker-flavor=ld.lld -C link-self-contained=yes` enlaza y
el binario corre. Una distro sin compilador de C puede compilar Rust igual.
4. Lo que costó el escalón 2 — cinco muros, todos con su mensaje engañoso
-
llvm18no sirve. El bootstrap de rustc lo dice en una línea:panic!("bad LLVM version: {version}, need >=21"). Mínimos medidos leyendosrc/bootstrap/src/core/build_steps/llvm.rs: 1.87 → ≥18 · 1.90 → ≥19 · 1.93/1.95 → ≥20 · 1.97 → ≥21. Y bajar de rustc no es salida: el stage0 de rustc N es N-1 o N, y lo que tenemos es 1.97. Por eso existellvm21— yllvm18se queda, porque lo usamesa-llvmpipey mesa 24.0.9 no soporta 21. Dos consumidores con rangos incompatibles ⇒ dos artefactos. -
El toolchain musl oficial NO puede producir proc-macros. Está compilado con
crt-static = true, y los proc-macros son dylibs:error: cannot produce proc-macro for `clap_derive` as the target `x86_64-unknown-linux-musl` does not support these crate types…compilando el propio bootstrap de rustc, que usa
clapcon derive. O sea: el prebuilt sirve para compilar programas y no para arrancar el build de rustc. El stage0 es elrustcdel LAB, que Alpine construye con soporte de dylib. (Y por eso el rustc que salga de acá llevacrt-static = false: contrueheredaría el mismo defecto y no podría compilar ninguna de las 44 recetascargo-*que usanderive.) -
x.py DESCARGA el stage0 si no le nombras el local. Sin
build.rustc/build.cargo, en un sandbox sin red muere conRuntimeError: failed verificationendownload_toolchain()— un mensaje que no menciona ni la red ni el stage0. -
El triple de Alpine no es el canónico. Sin fijar
build, x.py detectax86_64-alpine-linux-musly entonces una sección[target.x86_64-unknown-linux-musl]no aplica a nada: ignora elllvm-configexterno y se pone a construir LLVM él solo. Y al forzar el canónico, el stage0 del lab no tiene la std de ese triple (can't find crate for std). Se usa el de Alpine en todo, que además es con el que ya se compila el corpus entero. -
cmake + zig = «compiler broken». Los build scripts nativos (jemalloc, compiler-rt) usan cmake, y con el wrapper de zig la prueba de ABI falla.
CC=gcc CXX=g++, igual que enllvm21ycmake.
5. Lo que falta decidir
- Dónde vive el toolchain. Hoy
rust-toolchain-binestá enperfil.servidor(799 M; un escritorio no compila nada). El de fuente irá donde se decida — y probablemente sustituya al prebuilt en ese perfil en vez de sumarse. - La config del sitio: que las flags del linker (
-C linker-flavor=ld.lld) sean el default en un/etc/cargo/config.toml, para quecargo buildfuncione sin recordarlas. - El escalón 3: cuándo se conecta la cadena del selfhost al corpus. Es lo único que quita la dependencia de bytes ajenos, y es el trabajo más caro de los tres.