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>
Semantic delta
No definition changed: this change is text, documentation or data.
7 files · +34 −80
diff --git a/docs/state/targets.toml b/docs/state/targets.toml
index da0d71a1..43764965 100644
--- a/docs/state/targets.toml
+++ b/docs/state/targets.toml
@@ -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
diff --git a/recipes/cargo-config.toml b/recipes/cargo-config.toml
deleted file mode 100644
index 3fbd1da3..00000000
--- a/recipes/cargo-config.toml
+++ /dev/null
@@ -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/"
diff --git a/recipes/cargo-config/tree/etc/cargo/config.toml b/recipes/cargo-config/tree/etc/cargo/config.toml
deleted file mode 100644
index bd0d18e9..00000000
--- a/recipes/cargo-config/tree/etc/cargo/config.toml
+++ /dev/null
@@ -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"]
diff --git a/recipes/incoming/cargo-config.toml b/recipes/incoming/cargo-config.toml
deleted file mode 120000
index 65b32317..00000000
--- a/recipes/incoming/cargo-config.toml
+++ /dev/null
@@ -1 +0,0 @@
-../cargo-config.toml
\ No newline at end of file
diff --git a/recipes/lld21.toml b/recipes/lld21.toml
index 444cfa3f..f13893a9 100644
--- a/recipes/lld21.toml
+++ b/recipes/lld21.toml
@@ -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"
diff --git a/recipes/rust-alpine-target.patch b/recipes/rust-alpine-target.patch
index 80679230..f0a7dc3d 100644
--- a/recipes/rust-alpine-target.patch
+++ b/recipes/rust-alpine-target.patch
@@ -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(),
diff --git a/scripts/servidor/cargo-config.toml b/scripts/servidor/cargo-config.toml
deleted file mode 100644
index bd0d18e9..00000000
--- a/scripts/servidor/cargo-config.toml
+++ /dev/null
@@ -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"]