250 líneas de comentario en 151 scripts. Control verificado: el diff no toca NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa. El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces: las tres eran el MOTD que el script escribe DENTRO de la imagen construida — texto del producto, no comentario del script. Se cambiaron aparte y a propósito, que es rebranding, no limpieza. Y el hallazgo caro: casaba contra , que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate. La etapa 4 lo movió a y el script quedó casando NADA. No fallaba: imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja. Además 14 rutas de módulo en docs, que el barrido anterior no tocó porque no es frontera de palabra.
10 KiB
rust-frontier — self-hosting the builder's rust toolchain
The last frontier of selfhost-verify variante (b): replace the Alpine
rustc/cargo 1.91.1 in /toolchain with a hammer-built rust, via the purist
mrustc chain (no prebuilt rustc binary). Path:
mrustc → rustc 1.90.0 (builds bundled LLVM) → 1.91.0 → 1.91.1
These scripts + patch are the working artifacts. The mrustc tree itself lives in
.scratch/mrustc (gitignored — it's a large third-party clone, upstream master
be69c74); only our deltas are tracked here.
Files
run_rustc.patch— patch against mrustcbe69c74forrun_rustc/Makefileandrun_rustc/rustc_proxy.sh. Apply from the mrustc root:git -C .scratch/mrustc apply /path/to/run_rustc.patchbuild-stage0-musl.sh— rebuild the mrustc stage0rustc+cargowith a musl host triple (output-1.90.0-x86_64-unknown-linux-musl/). Run inside the Alpine devfs sandbox (gcc, MRUSTC_TARGET_VER=1.90). Reuses the already-built LLVM andbin/mrustc.run-runrustc.sh— run arun_rustcmake target inside the devfs sandbox, pointed at the musl-host stage0.bootstrap.toml— rust's own bootstrap config for hop 1 (1.90.0 → 1.91.0):build/host/target = x86_64-unknown-linux-musl, stage0rustc/cargo= the run_rustc musl-host 1.90.0 prefix, external LLVM 20.1.8 reused viallvm-config(clears bootstrap's>=19gate, no LLVM rebuild),crt-static = false(dynamic link against the devfs system musl, like Alpine and the run_rustc toolchain),rpath = true. Copy it into the rust source root.bootstrap-1.91.1.toml— config for hop 2 (1.91.0 → 1.91.1). Same as hop 1 except: stage0rustc = /stage0/bin/rustc(the installed hop-1 toolchain, bound read-only viaSTAGE0), stage0cargo= the run_rustc 1.90.0 cargo (1.90 satisfies bootstrap's "minor-1" check for a 1.91.x source),extended = true+tools = ["cargo"]so the install yields a hammer-built cargo, andcargo-native-static = true— the hermetic devfs has no system OpenSSL/curl/libgit2, so this makes bootstrap build cargo with theall-staticfeature set (vendored openssl/libgit2 + static curl/libz, compiled from the vendored C sources via gcc). Without it the cargo tool fails onopenssl-sys.swap-rust-into-toolchain.sh— overlay the hammer-built rust 1.91.1 (.scratch/rust-1.91.1-prefix) onto a toolchain dir (default.dev-fs/alpine) sohammer build/bootstrap stage1compile the 4/4 rust inputs (arje-zero, hammerd) with hammer-rust. Additive + reversible: hammer'sx86_64-unknown-linux-muslrustlib and hash-suffixed.sos don't collide with Alpine'sx86_64-alpine-linux-musl, so only/usr/bin/{rustc,cargo}are replaced (Alpine backed up to*.alpine).--restoreundoes it. Wired intoselfhost-verify.shasSWAP_RUST=1(applies before the stage1 build, restores on exit via trap). This swap changesof_tree(rustc emits the binaries), so the criterion is auto-consistency against a NEWRUST_EXPECT_REF, not the Alpine9adefb82.run-xpy.sh— run rust'sx.pyinside the devfs sandbox, binding the source at/src, the install prefix at/out, and the mrustc tree at/mrustc(so the stage0 +llvm-configpaths inbootstrap.tomlresolve). Set theSTAGE0env var to also bind a prior hop's installed prefix read-only at/stage0. Usage:run-xpy.sh <SRCDIR> <PREFIXDIR> -- build --stage 2 compiler/rustc library/std.
The runners hardcode
/home/sergio/hammerand assume the prepared devfs at.dev-fs/alpine(256-TLS-key musl loader + g++; see the bootstrap-devfs notes).
Why the patches exist (the snags)
The lab is musl-only (Alpine devfs); the 4/4 inputs (arje-zero/hammerd) are
built --target x86_64-unknown-linux-musl. Three coupled problems had to be fixed
to bootstrap a usable rust there:
-
stage-1 std built for gnu (
mmap64/open64undefined at link).minicargowas invoked without--target, so it used mrustc's default gnu triple — and gnu std references glibc's LFS64 symbols that musl 1.2.5 doesn't export. Fix: pass--target $(RUSTC_TARGET)to minicargo (Makefile:162) and to the stage-1 smoke-test (Makefile:172). A correct musl std uses the base libc symbols (open/mmap/…), matching Alpine's reference std. -
self-contained CRT absent. musl's default link is
-static-pieagainst alib/rustlib/<t>/lib/self-contained/CRT dir that the mrustc sysroot never populates (also needs static-lunwind). Fix:-C target-feature=-crt-static -C link-self-contained=noso the Alpine gcc supplies crt1/crti/crtn + crtbeginS/crtendS and links dynamically against the system musl (RUSTFLAGS in Makefile:111, plus both hello_world smoke-test rules: the stage-1 one at Makefile:172 and the final fully-bootstrapped one at Makefile:262 — the latter only surfaces once stage-2/3 actually completes, i.e. after snag #3 is fixed). -
host-triple = gnu on a musl system (the deep one). cargo compiles build-scripts and proc-macros for the host; mrustc's stage0 rustc has its host triple baked as
x86_64-unknown-linux-gnu(not runtime-overridable). A proxy--target muslinject fixes build-script bins, but proc-macros are dlopen'd and must match the host triple structurally — injection makes them target artifacts that rustc rejects. Aliasing musl rlibs as gnu also fails: rustc reads the triple from rlib metadata (E0461), not the path.The only fix is a stage0 rustc whose host triple is musl — that's what
build-stage0-musl.shproduces (CFG_COMPILER_HOST_TRIPLEviaRUSTC_TARGET,--targetviaMRUSTC_TARGET). Then host==target==musl: cargo doesn't cross-compile, proc-macros load natively, andrustc_proxy.shonly needs the CRT flags (no--targetinject).Gotcha: do not pass
OUTDIR_SUFon make's command line — a command-line var can't be reassigned by the makefile, so the-muslsuffix append is skipped and the build contaminates the gnu output dir. Let it default. -
final cargo needs vendored OpenSSL. The fully-bootstrapped
prefix/bin/cargorule (Makefile:235) builds cargo with default features, soopenssl-syslooks for a system OpenSSL via pkg-config and fails (the hermetic musl devfs has none, and noopenssl.pc). Fix: add--features vendored-opensslto that cargo build soopenssl-srccompiles OpenSSL from source — same approach the stage0 cargo build uses. Real cargo (unlike minicargo) handles the OUT_DIR fine, so snag #4 doesn't recur here. Only surfaces after the final hello_world (snag #2/262) passes. -
minicargo OUT_DIR host/target split (only when building the musl stage0's cargo). With
RUSTC_TARGETset, minicargo runs each cargo build-script undercargo-build/host/build_X/, but the target crate'sinclude!/include_bytes!(concat!(env!("OUT_DIR"), "/.."))resolvesOUT_DIRtocargo-build/build_X/(nohost/, empty) → mrustc aborts with signal 6. Hitlibsqlite3-sys(bindgen.rs) and thecargobinary (man.tgz). The gnu build never split (single triple → single dir). Since host==target the script outputs are bit-identical, sobuild-stage0-musl.shsymlinks the target dir to the host dir (symlink_outdirs). Late sys-crates (cargo, curl, openssl, libgit2-sys, libssh2-sys) run their scripts near the end, so it's applied in a retry loop around the incremental cargo build.
Workflow
# 1. apply the run_rustc patch into the mrustc clone
git -C .scratch/mrustc apply scripts/rust-frontier/run_rustc.patch
# 2. rebuild stage0 rustc+cargo with a musl host triple (~hours, reuses LLVM)
.scratch/build-stage0-musl.sh # -> output-1.90.0-x86_64-unknown-linux-musl/
# 3. build the optimised rustc + cargo via run_rustc (stage 1/2/3)
.scratch/run-runrustc.sh all
# 4. climb hop 1 — 1.90.0 -> 1.91.0 (stage0 = the run_rustc musl-host 1.90.0)
cp scripts/rust-frontier/bootstrap.toml .scratch/rustc-1.91.0-src/bootstrap.toml
.scratch/run-xpy.sh .scratch/rustc-1.91.0-src .scratch/rust-1.91.0-prefix -- \
build --stage 2 compiler/rustc library/std
.scratch/run-xpy.sh .scratch/rustc-1.91.0-src .scratch/rust-1.91.0-prefix -- \
install --stage 2 rustc library/std # -> relocatable 1.91.0 (rpath)
# 5. climb hop 2 — 1.91.0 -> 1.91.1 (stage0 rustc = the installed 1.91.0)
cp scripts/rust-frontier/bootstrap-1.91.1.toml .scratch/rustc-1.91.1-src/bootstrap.toml
STAGE0=.scratch/rust-1.91.0-prefix .scratch/run-xpy.sh \
.scratch/rustc-1.91.1-src .scratch/rust-1.91.1-prefix -- \
build --stage 2 compiler/rustc library/std src/tools/cargo
STAGE0=.scratch/rust-1.91.0-prefix .scratch/run-xpy.sh \
.scratch/rustc-1.91.1-src .scratch/rust-1.91.1-prefix -- \
install --stage 2 rustc library/std cargo # -> hammer rustc+cargo 1.91.1
# 6. swap rust 1.91.1 into /toolchain + auto-consistency verify (in-VM)
# PRESEED=hammerd es OBLIGATORIO con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea
# ~1973 crates; en un rebuild in-VM completo (PRESEED=all) ese vendor desborda el
# rootfs en RAM de la VM → ENOSPC. Con PRESEED=hammerd se preseedea el arje-zero
# host-built (takana-rust, locked) y sólo hammerd se reconstruye in-VM.
SWAP_RUST=1 KVM=1 MEM=16384 PRESEED=hammerd ./scripts/selfhost-verify.sh
DONE (2026-06-16): both hops built. rust-1.91.1-prefix/bin/{rustc,cargo} run
standalone (rpath), rustc 1.91.1 (ed61e7d7e) host x86_64-unknown-linux-musl
LLVM 20.1.8, cargo 1.91.1. Alpine's exact rust version, fully self-hosted from
the mrustc chain (mrustc → 1.90.0 → 1.91.0 → 1.91.1). Each hop ≈45 min compile
(LLVM reused throughout); hop 2's cargo needed the cargo-native-static fix.
✓ AUTO-CONSISTENCY VERIFICADA IN-VM (2026-06-18): SWAP_RUST=1 … PRESEED=hammerd
→ el host construye stage1 con hammer-rust (of_tree 7fa6cb4e), la VM lo reconstruye
con hammer-rust y reproduce 7fa6cb4e bit a bit (stage1' == stage1, DRIVER_RC=0).
RUST_EXPECT_REF=7fa6cb4e (≠ el 9adefb82 de Alpine — rustc emite los bytes). El drift
de arje-zero quedó cerrado fijando su Cargo.lock en tawasuyu (recipes/arje-zero.toml
→ commit 9967b02c): build --locked ⇒ bit-reproducible (lock1 == lock2).
Full diagnostic history is in the selfhost-verify-rust-frontier memory.