el enlazador va DENTRO del compilador — /etc/cargo/config.toml es una ruta que cargo no lee

La prueba de la caja recién instalada —`cargo build` en un proyecto nuevo, sin flags ni rutas a
mano— falló con `linker `cc` not found`, y destapó que la pieza que había empaquetado era falsa:
**cargo NO LEE `/etc/cargo/config.toml`**. Sólo lee `$CARGO_HOME/config.toml`, los
`.cargo/config.toml` del proyecto y sus padres, y lo que se le pase con `--config` — que es lo que yo
venía pasando a mano en cada prueba, y por eso «funcionaba».

Y el arreglo no era mover el fichero: **el que enlaza es `rustc`, y rustc no lee la config de
cargo**. Las flags van DENTRO del compilador, en el spec del triple:

· `linker = "ld.lld"` + `linker_flavor = Gnu(Cc::No, Lld::Yes)` — sin esto rustc busca `cc`, que una
  caja takana no tiene.
· `crt_static_default = true` — sin esto enlaza dinámico y pide `-lgcc`, que no existe (la distro se
  construye con zig, que trae compiler-rt).
· `crt_static_allows_dylibs = true` — su pareja obligatoria: sin ella `crt-static` apaga los dylibs
  y con ellos los PROC-MACROS (serde_derive, clap_derive, las 44 recetas `cargo-*` con `derive`).
  El compilador en sí sigue dinámico: el `bootstrap.toml` fija `crt-static = false` para el build, y
  eso gana sobre el default del spec.

⇒ `cargo-config` (receta, árbol y copia en scripts/) se van enteros: un paquete que instala un
fichero que nadie abre es peor que no tenerlo.

Y de paso, `lld21` instala los cuatro alias como SYMLINKS y no como copias: cmake los duplicaba, 5 ×
96 M = 478 M de artefacto para 97 M de enlazador. Ahora 108 M, y `ld.lld` sigue despachando por
`argv[0]`, que es como upstream lo diseñó.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-09-16 13:07:56 +00:00
co-authored by Claude Opus 5
parent c57fcb214f
commit e903f486d9
7 changed files with 34 additions and 80 deletions
+9 -4
View File
@@ -1361,14 +1361,19 @@ paquetes = [
# · `rust` el compilador y cargo, construidos por la granja desde fuente (353 M).
# · `lld21` el enlazador. NO es opcional: una caja takana **no tiene `cc`**, y sin
# enlazador `rustc` compila objetos y no produce un ejecutable.
# · `cargo-config` las tres flags que unen a los dos (`/etc/cargo/config.toml`). Sin ellas,
# `cargo build` muere con `linker \`cc\` not found` — el toolchain estaría
# completo y mudo.
#
# ⚠ Acá estuvo 20 minutos un tercer paquete, `cargo-config`, que publicaba `/etc/cargo/config.toml`
# con las flags del enlazador. **Cargo NO LEE ESA RUTA** —sólo `$CARGO_HOME/config.toml`, los
# `.cargo/config.toml` del proyecto y sus padres, y lo que se le pase con `--config`— así que el
# paquete habría instalado un fichero que nadie abre. La prueba que lo destapó fue la única que
# valía: `cargo build` en un proyecto nuevo, sin flags y sin rutas, en la caja. Y el arreglo no era
# mover el fichero: **el que enlaza es `rustc`, y rustc no lee la config de cargo** ⇒ las flags van
# DENTRO del compilador, en el spec del triple (`recipes/rust-alpine-target.patch`).
#
# ⚠ Va en `servidor` y NO en `base` por tamaño y por sentido: una imagen de escritorio no compila
# nada; la caja que sirve el repo y construye, sí. Y sigue siendo el paso 2 de tres: el 3 es la
# cadena mrustc del selfhost, que es lo único que quita la dependencia del LAB para arrancar.
"rust", "lld21", "cargo-config",
"rust", "lld21",
# `netup` (2026-09-10): el cliente DHCP propio que levanta la red al arrancar. Entra como RAÍZ
# aunque su binario YA venga dentro del `product-rootfs`, y por una razón concreta: el del producto
# está congelado en el artefacto sellado del bootstrap, así que un arreglo de netup no llega nunca
-32
View File
@@ -1,32 +0,0 @@
# cargo-config — la config que hace que `cargo build` FUNCIONE en una caja takana.
#
# ── POR QUÉ ES UNA RECETA Y NO UN FICHERO SUELTO (SDD 31) ───────────────────────────────────────
# Una distro sin compilador de C puede compilar Rust —está probado— pero **no de fábrica**: sin esta
# config, `cargo build` muere con `linker \`cc\` not found`. Un toolchain que sella, se instala y no
# enlaza es [[subcomando-sin-driver]] otra vez, y la pieza que falta son tres flags.
#
# Va junto a `rust` y `lld21` en el perfil: los tres son el mismo hecho —«esta caja compila Rust»—
# partido en un compilador, un enlazador y la línea que los une.
#
# ⚠ Es config de SISTEMA, no de usuario: `/etc/cargo/config.toml` lo lee cualquier `cargo` de
# cualquier cuenta. Un `~/.cargo/config.toml` haría que el toolchain funcione para quien lo escribió
# y no para el siguiente.
name = "cargo-config"
version = "1"
license = "MIT"
[source]
dir = "cargo-config/tree"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
link = "static"
flags = []
[build.phases]
configure = "true"
compile = "true"
# `cp -r` y no `cp -a`: en el sandbox no hay privilegio para preservar el propietario (la lección de
# `os-release`). El dueño real lo pone la proyección del artefacto.
install = "mkdir -p /out/etc && cp -r /src/etc/. /out/etc/"
@@ -1,19 +0,0 @@
# Config del SITIO para cargo/rustc — SDD 31.
#
# ⚠ Sin esto, `cargo build` en una caja takana falla con `linker `cc` not found`: la distro NO TIENE
# compilador de C, y no hace falta que lo tenga. Lo que hace falta es decirle a rustc que el
# enlazador es el `ld` de binutils y que el modo es estático, que es el natural de musl.
#
# Por qué CADA flag, todos medidos el 2026-09-16 contra el rustc que construimos:
# · `linker-flavor=ld.lld` + `linker=/usr/bin/ld.lld` — el `lld` del corpus (`recipes/lld21.toml`).
# Hasta el 2026-09-16 acá decía el `ld` de binutils, porque el `lld` heredaba `LLVM_ENABLE_ZLIB=0`
# de `llvm21` y no podía leer las secciones de depuración COMPRIMIDAS de la libc del lab. Con
# `llvm21` reconstruido con zlib, lld enlaza y es más rápido. El `ld` de binutils sigue
# funcionando como alternativa: `-C linker-flavor=ld -C linker=/usr/bin/ld`.
# · `target-feature=+crt-static` — sin esto rustc pide `-lgcc`/`-lgcc_s` para el desenrollado y el
# enlace muere en `cannot find -lgcc`: la distro se construye con zig, que trae compiler-rt y no
# libgcc. Estático es además el modo natural de musl.
# · `link-self-contained=yes` — usa los `crt*.o` y la `libc.a` que el propio toolchain trae.
[target.x86_64-alpine-linux-musl]
linker = "/usr/bin/ld.lld"
rustflags = ["-C", "linker-flavor=ld.lld", "-C", "target-feature=+crt-static", "-C", "link-self-contained=yes"]
-1
View File
@@ -1 +0,0 @@
../cargo-config.toml
+5 -1
View File
@@ -75,4 +75,8 @@ cmake -S lld -B build -G Ninja \
-DLLVM_ENABLE_LIBXML2=OFF
'''
compile = "ninja -C build"
install = "DESTDIR=/out ninja -C build install"
# ⚠ LOS CINCO `lld` SON EL MISMO BINARIO, Y cmake LOS INSTALA COMO CINCO COPIAS: medido, 5 × 96 M =
# **478 M de artefacto para 97 M de enlazador**. `lld` despacha por `argv[0]`, así que los cuatro
# alias son symlinks por diseño de upstream — instalarlos como copias es sólo cómo quedó su cmake.
# En una caja cuya raíz son 5,8 G, 380 M de duplicado no son un detalle.
install = "DESTDIR=/out ninja -C build install && cd /out/usr/bin && for f in ld.lld ld64.lld lld-link wasm-ld; do ln -sf lld \"$f\"; done"
+20 -4
View File
@@ -18,7 +18,7 @@ dylibs y, con ellos, los PROC-MACROS. Un rustc con `crt-static` por default no p
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/x86_64_alpine_linux_musl.rs
@@ -0,0 +1,37 @@
@@ -0,0 +1,53 @@
+use crate::spec::{
+ Arch, Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, TargetMetadata, base,
+};
@@ -37,9 +37,25 @@ dylibs y, con ellos, los PROC-MACROS. Un rustc con `crt-static` por default no p
+ | SanitizerSet::MEMORY
+ | SanitizerSet::THREAD;
+ base.supports_xray = true;
+ // A DIFERENCIA del spec canónico, que lo deja en `true`: sin esto no hay dylibs, y sin dylibs
+ // no hay proc-macros. Es el mismo valor que usa Alpine en su rustc.
+ base.crt_static_default = false;
+ // ── ESTÁTICO POR DEFECTO, PERO CON DYLIBS ─────────────────────────────────────────────────
+ // `crt_static_default = true` es lo que hace que un `rustc hola.rs` produzca un binario que
+ // CORRE en una caja takana: sin él, rustc enlaza dinámico y pide `-lgcc`, que no existe —la
+ // distro se construye con zig, que trae compiler-rt—, y el enlace muere en `cannot find -lgcc`.
+ // `crt_static_allows_dylibs = true` es su pareja obligatoria: sin ella, `crt-static` apaga los
+ // dylibs y con ellos los PROC-MACROS (`serde_derive`, `clap_derive` y las 44 recetas `cargo-*`
+ // del corpus que usan `derive`). Los dos juntos = binarios estáticos y proc-macros vivos.
+ // El compilador EN SÍ sigue siendo dinámico: el `bootstrap.toml` fija `crt-static = false` para
+ // el build, que gana sobre este default.
+ base.crt_static_default = true;
+ base.crt_static_allows_dylibs = true;
+
+ // ── EL ENLAZADOR, EN EL COMPILADOR Y NO EN UN FICHERO DE CONFIG ───────────────────────────
+ // Sin esto, `rustc` busca `cc` —que una caja takana NO TIENE— y falla con `linker \`cc\` not
+ // found`. Ponerlo acá y no en un `config.toml` de cargo es la diferencia entre «takana compila
+ // Rust» y «takana compila Rust si sabés tres flags»: el que compila es rustc, y rustc no lee la
+ // config de cargo. `ld.lld` lo publica `recipes/lld21.toml`, que va en el mismo perfil.
+ base.linker = Some("ld.lld".into());
+ base.linker_flavor = LinkerFlavor::Gnu(Cc::No, Lld::Yes);
+
+ Target {
+ llvm_target: "x86_64-alpine-linux-musl".into(),
-19
View File
@@ -1,19 +0,0 @@
# Config del SITIO para cargo/rustc — SDD 31.
#
# ⚠ Sin esto, `cargo build` en una caja takana falla con `linker `cc` not found`: la distro NO TIENE
# compilador de C, y no hace falta que lo tenga. Lo que hace falta es decirle a rustc que el
# enlazador es el `ld` de binutils y que el modo es estático, que es el natural de musl.
#
# Por qué CADA flag, todos medidos el 2026-09-16 contra el rustc que construimos:
# · `linker-flavor=ld.lld` + `linker=/usr/bin/ld.lld` — el `lld` del corpus (`recipes/lld21.toml`).
# Hasta el 2026-09-16 acá decía el `ld` de binutils, porque el `lld` heredaba `LLVM_ENABLE_ZLIB=0`
# de `llvm21` y no podía leer las secciones de depuración COMPRIMIDAS de la libc del lab. Con
# `llvm21` reconstruido con zlib, lld enlaza y es más rápido. El `ld` de binutils sigue
# funcionando como alternativa: `-C linker-flavor=ld -C linker=/usr/bin/ld`.
# · `target-feature=+crt-static` — sin esto rustc pide `-lgcc`/`-lgcc_s` para el desenrollado y el
# enlace muere en `cannot find -lgcc`: la distro se construye con zig, que trae compiler-rt y no
# libgcc. Estático es además el modo natural de musl.
# · `link-self-contained=yes` — usa los `crt*.o` y la `libc.a` que el propio toolchain trae.
[target.x86_64-alpine-linux-musl]
linker = "/usr/bin/ld.lld"
rustflags = ["-C", "linker-flavor=ld.lld", "-C", "target-feature=+crt-static", "-C", "link-self-contained=yes"]