From a8a9a396c378f8e57d69318987a74d1088bc818f Mon Sep 17 00:00:00 2001 From: sergio Date: Fri, 17 Jul 2026 10:08:46 -0400 Subject: [PATCH] =?UTF-8?q?static:=20helix=20y=20tuc=20=E2=80=94=20el=20li?= =?UTF-8?q?bgcc=5Fs=20NO=20ven=C3=ADa=20de=20ning=C3=BAn=20C=20(MIENTEN:?= =?UTF-8?q?=200=20en=20todo=20el=20store)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mi hipótesis de partida era falsa y conviene dejarlo escrito: el libgcc_s.so.1 NO lo metía un sys-crate en C, y -static-libgcc (el patrón de cmake) no aplicaba — ese patrón es para C/C++. CAUSA REAL, una sola y compartida: ambas recetas PISABAN la fase compile del lab. El lab sólo autogenera la fase si la receta no la trae (lib.rs:779, `if out.compile.is_none()`). Las dos venían del import de Alpine con un `cargo build --frozen --release` propio que REEMPLAZA el comando del lab entero, y con él las tres cosas que hacen honesto al link=static: -C target-feature=+crt-static -C relocation-model=static -C linker=.hammer-zig-cc Sin crt-static, el rust enlaza dinámico contra musl Y contra libgcc_s — que es el UNWINDER DE LA STD DE RUSTC, no un sys-crate. LA EVIDENCIA QUE LO CIERRA (contraste, sin bisectar): estaba ya escrita en recipes/cargo-hack.toml —'receta MÍNIMA sin fase compile custom (una fase cargo build produce binario DINÁMICO; el PATH DEFAULT del lab aplica crt-static + linker zig → estático)'. cargo-hack es Rust puro y sale estático; tuc TAMBIÉN es Rust puro, sin una línea de C, y arrastraba el mismo libgcc_s. Misma toolchain, único delta = quién arma el cargo. tuc: borrada la fase compile (no aportaba nada sobre el default) + flags=["--bin","tuc"] (cargo rustc exige un único target y tuc expone lib+bin homónimos). NEEDED=0, tuc 1.3.0 corre y 'hola,mundo | tuc -d, -f2' → mundo. helix: la fase custom es OBLIGATORIA (exporta HELIX_DEFAULT_RUNTIME y HELIX_DISABLE_AUTO_GRAMMAR_ BUILD) ⇒ replicado a mano el setup del lab siguiendo cargo-edit. Su raíz es un manifiesto VIRTUAL ⇒ 'cargo rustc' cortaba con 'is a virtual manifest'; resuelto con -p helix-term, que declara [[bin]] name="hx". Sigue con compiler=gcc (el gueto cc-rs/tree-sitter es real). NEEDED=0, helix 25.07.1 corre. MARCADOR: static-audit global = estáticos de verdad: 680 | MIENTEN: 0. Con dos honestidades: el audit corrió DESPUÉS de los rebuilds (si no, sobre-reporta), y quedan 58 'sin artefacto o sin ELF' que NO son un pase sino recetas no medidas. El cero es real para lo que el store cubre hoy, no una prueba de clausura. TRAMPA ARMADA (no tocada): el riesgo generaliza a toda receta Cargo importada de Alpine que traiga compile propio — alpine_import.rs traduce el build() del APKBUILD literal. Hoy las 5 con compile propio llevan crt-static, pero cada receta Rust nueva puede nacer mintiendo. Un gate barato: que el lab avise/falle si una receta Cargo con link=static define compile sin crt-static. Cambia el lab ⇒ re-hashea sellados ⇒ decisión aparte. Co-Authored-By: Claude Opus 4.8 --- recipes/helix.toml | 50 ++++++++++++++++++++++++++++++++++------------ recipes/tuc.toml | 28 ++++++++++++++++++-------- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/recipes/helix.toml b/recipes/helix.toml index 2de75cad..54550310 100644 --- a/recipes/helix.toml +++ b/recipes/helix.toml @@ -20,20 +20,44 @@ link = "static" flags = [] [build.phases] -# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +# Fase compile custom OBLIGATORIA (a diferencia de tuc, que la borró): helix necesita exportar dos +# env vars antes de cargo, y el path default del lab no da dónde ponerlas. Pero entonces hay que +# REPLICAR A MANO el setup estático del lab — mismo patrón que cargo-edit. +# +# POR QUÉ (2026-07-17, frente static-audit): el `cargo build --frozen --release` que traía la receta +# importada de Alpine PISABA el comando del lab entero (`resolve_phases` sólo autogenera la fase si la +# receta no la trae: `if out.compile.is_none()`). Al pisarlo se perdían las tres cosas que hacen +# honesto al `link = "static"`: `+crt-static` (musl adentro), `relocation-model=static` (ET_EXEC en vez +# de PIE) y `-C linker=.hammer-zig-cc` (el wrapper que sanea el triple `x86_64-alpine-linux-musl`, que +# zig rechaza). hx salía PIE dinámico con `NEEDED: libgcc_s.so.1, libc.so`. +# +# Ese `libgcc_s` NO lo metían las tree-house-bindings ni ningún otro C: es el UNWINDER de la std de +# rustc, que sin `+crt-static` se enlaza dinámico contra el libgcc_s de Alpine. El fix de C/C++ +# (`-static-libgcc`) acá no aplica. Prueba: tuc es Rust puro, sin una línea de C, y arrastraba el +# MISMO libgcc_s por la misma causa. +# +# `crt-static` va TRAS `cargo rustc --` y NUNCA en RUSTFLAGS: en build nativo RUSTFLAGS alcanza también +# a los proc-macros (un proc-macro es un dylib PIC) y rustc aborta con "cannot produce proc-macro". +# Como `cargo rustc --` exige UN único target, se fija `--bin hx`. Y `-p helix-term` porque la raíz de +# helix es un manifiesto VIRTUAL (workspace sin paquete propio): sin `-p`, cargo rustc corta con +# "is a virtual manifest, but this command requires running against an actual package". helix-term es +# quien declara `[[bin]] name = "hx"`. +# +# CC=gcc: el GUETO declarado arriba (cc-rs compila el tree-sitter en C y rechaza zig). El linker sigue +# siendo zig-cc: los .o de gcc-musl y el Rust zig-musl son ABI-compatibles (ambos musl, mismo ELF). compile = ''' -_abuild_phase() { -# This path is embedded in the resulting binary so that helix can find - # system grammars - # https://github.com/helix-editor/helix/blob/25.01.1/helix-loader/src/lib.rs#L64 - export HELIX_DEFAULT_RUNTIME=/usr/share/helix/runtime - # Sandbox hermético: helix-term/build.rs intenta `git clone` de 245 gramáticas tree-sitter - # (sin red ⇒ panic). Lo desactivamos; las gramáticas van por runtime/ (build post-install). - export HELIX_DISABLE_AUTO_GRAMMAR_BUILD=1 - # de-Alpinizado: cargo-auditable no está en el lab de hammer; build plano (Etapa G) - cargo build --frozen --release -} -_abuild_phase +# This path is embedded in the resulting binary so that helix can find system grammars +# https://github.com/helix-editor/helix/blob/25.01.1/helix-loader/src/lib.rs#L64 +export HELIX_DEFAULT_RUNTIME=/usr/share/helix/runtime +# Sandbox hermético: helix-term/build.rs intenta `git clone` de 245 gramáticas tree-sitter +# (sin red ⇒ panic). Lo desactivamos; las gramáticas van por runtime/ (build post-install). +export HELIX_DISABLE_AUTO_GRAMMAR_BUILD=1 +# de-Alpinizado: cargo-auditable no está en el lab de hammer; build plano (Etapa G) +printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec zig cc -mcpu=baseline "$@"' > "$PWD/.hammer-zig-cc" +chmod +x "$PWD/.hammer-zig-cc" +RF="-C linker=$PWD/.hammer-zig-cc" +rustc -vV | grep -q 'host: .*-alpine-' || RF="$RF -C link-self-contained=no" +CC=gcc CXX=g++ AR=ar RUSTFLAGS="$RF" cargo rustc --release --locked --offline -p helix-term --bin hx -- -C target-feature=+crt-static -C relocation-model=static ''' # de package() de Alpine (traducido $pkgdir→/out): install = ''' diff --git a/recipes/tuc.toml b/recipes/tuc.toml index 458a3d39..02555c9c 100644 --- a/recipes/tuc.toml +++ b/recipes/tuc.toml @@ -14,16 +14,28 @@ sha256 = "81dc5f4a0355ecdf9515c88c34c365d20f339d316df7dbe72667cd2b18445c61" compiler = "zig-cc" target = "x86_64-linux-musl" link = "static" -flags = [] +flags = ["--bin", "tuc"] [build.phases] -# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): -compile = ''' -_abuild_phase() { -cargo build --frozen --release -} -_abuild_phase -''' +# SIN fase `compile` custom — A PROPÓSITO (2026-07-17, frente static-audit). +# +# La receta importada de Alpine traía `cargo build --frozen --release`. Eso NO era inocente: en +# `resolve_phases` el lab sólo autogenera la fase si la receta NO la trae (`if out.compile.is_none()`), +# así que un `compile` propio REEMPLAZA el comando del lab ENTERO y con él se pierden las tres cosas +# que hacen honesto al `link = "static"`: +# 1. `-C target-feature=+crt-static` (mete musl DENTRO; sin esto el rust de Alpine enlaza dinámico) +# 2. `-C relocation-model=static` (ET_EXEC sin interpreter, en vez de PIE) +# 3. `-C linker=.hammer-zig-cc` (el wrapper que sanea el triple `x86_64-alpine-linux-musl`) +# El binario salía PIE dinámico con `NEEDED: libgcc_s.so.1, libc.musl-x86_64.so.1`. +# +# Ojo con el diagnóstico fácil: ese `libgcc_s` NO lo metía ningún sys-crate en C. tuc es Rust puro. +# Es el UNWINDER de la std de rustc, que sin `+crt-static` se enlaza dinámico contra el libgcc_s de +# Alpine. Por eso el fix no es `-static-libgcc` (eso es para C/C++): es dejar de pisar la fase del lab. +# Contraste que lo prueba: cargo-hack también es Rust puro, no tiene fase compile custom, y sale +# ET_EXEC estático. Misma toolchain, único delta = quién arma el `cargo`. +# +# `flags = ["--bin", "tuc"]`: el path default usa `cargo rustc -- `, que exige UN único target, +# y tuc expone lib + bin homónimos. # de package() de Alpine (traducido $pkgdir→/out): install = ''' _abuild_phase() {