diff --git a/crates/hammer-build/src/lib.rs b/crates/hammer-build/src/lib.rs index 633d8507..519c04e9 100644 --- a/crates/hammer-build/src/lib.rs +++ b/crates/hammer-build/src/lib.rs @@ -117,6 +117,27 @@ fn materialize_build_deps( Ok(dirs) } +/// Resuelve el directorio de zig para una receta: el por defecto del lab (`cfg.zig_dir`), salvo que +/// la receta fije `build.zig_version`, en cuyo caso se usa el zig de esa versión, ubicado como +/// **hermano** del por defecto (`<…>/zig-x86_64-linux-`, el layout de `.dev-fs/tools/`). +/// Escotilla por-receta para esquivar la regresión de zig 0.14 sin tocar el toolchain global. +fn effective_zig_dir(cfg: &BuildConfig, recipe: &Recipe) -> hammer_core::Result { + let Some(version) = &recipe.build.zig_version else { + return Ok(cfg.zig_dir.clone()); + }; + let parent = cfg.zig_dir.parent().unwrap_or_else(|| Path::new(".")); + let dir = parent.join(format!("zig-x86_64-linux-{version}")); + if !dir.join("zig").is_file() { + return Err(hammer_core::Error::Recipe(format!( + "zig_version=\"{version}\": no encuentro el ejecutable zig en {} \ + (bajá ese zig a {}/)", + dir.display(), + parent.display(), + ))); + } + Ok(dir) +} + /// Compila una receta y devuelve el hash del artefacto sellado en el store. /// Si el hash ya existe en el store, devuelve sin recompilar (caché). pub fn build( @@ -129,7 +150,10 @@ pub fn build( tracing::info!(hash = %h, name = %recipe.name, "caché: artefacto ya en el store"); return Ok(h); } - sandbox::ensure_layout(&cfg.rootfs, &cfg.zig_dir)?; + // zig efectivo de ESTA receta: el por defecto del lab, salvo que la receta fije `zig_version` + // (escotilla para esquivar la regresión de zig 0.14 en ciertos binarios musl; ver SDD 11 §7.2b). + let zig_dir = effective_zig_dir(cfg, recipe)?; + sandbox::ensure_layout(&cfg.rootfs, &zig_dir)?; // Materializa las deps de build (las construye si faltan) ANTES del fetch: sus árboles sellados // se apilan como capas overlay del sandbox para que headers/libs/pkgconfig estén en /usr. @@ -165,7 +189,7 @@ pub fn build( } let sb = Sandbox { rootfs: cfg.rootfs.clone(), - zig_dir: cfg.zig_dir.clone(), + zig_dir: zig_dir.clone(), src_dir: src_tree.clone(), out_dir: out_dir.clone(), cache_dir: cfg.cache_root.clone(), diff --git a/crates/hammer-core/src/recipe.rs b/crates/hammer-core/src/recipe.rs index 9cf6c7c5..698a4664 100644 --- a/crates/hammer-core/src/recipe.rs +++ b/crates/hammer-core/src/recipe.rs @@ -96,6 +96,13 @@ pub struct Build { pub link: LinkMode, #[serde(default)] pub flags: Vec, + /// Versión de zig a usar para ESTA receta, distinta del zig por defecto del lab. Sirve de + /// escotilla por-receta para esquivar la regresión de zig 0.14 (que miscompila ciertos binarios + /// musl dinámicos: flex/cmake/python/binutils/…): `zig_version = "0.13.0"` los construye con el + /// zig bueno sin tocar el toolchain global ni el baseline de reproducibilidad (estas piezas son + /// herramientas, no inputs del of_tree del 4/4). `None` ⇒ el zig por defecto. Ver SDD 11 §7.2b. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub zig_version: Option, /// Overrides explícitos de las fases del build. Lo no especificado se decide por /// heurística en el lab (ver `docs/02-build-lab.md` §4). #[serde(default)] @@ -209,6 +216,11 @@ impl Recipe { self.build.target.as_bytes().to_vec(), self.build.link.as_str().as_bytes().to_vec(), ]; + // Sólo entra al hash si está fijado: una receta sin `zig_version` mantiene su hash de antes + // de existir el campo (compatibilidad hacia atrás; el baseline 9adefb82 no se mueve). + if let Some(zv) = &self.build.zig_version { + v.push(format!("zig:{zv}").into_bytes()); + } for p in &self.source.patches { let resolved = self.base_dir.join(p); let bytes = std::fs::read(&resolved).map_err(|e| { diff --git a/recipes/binutils.toml b/recipes/binutils.toml index bce39a7c..13d53c5f 100644 --- a/recipes/binutils.toml +++ b/recipes/binutils.toml @@ -34,14 +34,18 @@ tarball = "https://ftp.gnu.org/gnu/binutils/binutils-2.45.1.tar.gz" sha256 = "2b63ae0ad015eb2755294f127bfd179c1849979dd87b6e8e1a7376964a4323dc" [build] -compiler = "zig-cc" -target = "x86_64-linux-musl" -link = "dynamic" +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" +# zig 0.13.0 en vez del 0.16: con zig 0.14+ binutils miscompila (`as` SEGFAULTEA, "Internal error"). +# Es la REGRESIÓN de zig 0.14 en C/musl (bisect con flex: 0.13.0 limpio, 0.14+ rotos). Antes se +# esquivaba con CC=gcc; ahora con el zig bueno por-receta gcc deja de hacer falta. SDD 11 §7.2b. +zig_version = "0.13.0" [build.phases] # --disable-nls: sin gettext (ausente en el toolchain hermético). --disable-werror: binutils promueve # varios warnings a error por defecto. --disable-gprofng: el profiler de binutils 2.4x no es parte del # toolchain ar/ld/as y arrastra chequeos de profiling (mcount) innecesarios. -configure = "CC=gcc CXX=g++ ./configure --prefix=/usr --disable-nls --disable-werror --disable-gprofng" -compile = "CC=gcc CXX=g++ make -j\"$(nproc)\"" +configure = "./configure --prefix=/usr --disable-nls --disable-werror --disable-gprofng" +compile = "make -j\"$(nproc)\"" install = "make install DESTDIR=/out PREFIX=/usr" diff --git a/recipes/elfutils.toml b/recipes/elfutils.toml index e48b9a77..5d3169d9 100644 --- a/recipes/elfutils.toml +++ b/recipes/elfutils.toml @@ -15,7 +15,8 @@ # libeu.a pero objtool NO referencia (sólo necesita el core de libelf) ⇒ basta que # COMPILEN; las funcs argp quedan como undefined inertes en el .a. # - rawmemchr: shim inline (lo usa libelf/elf_getarsym.c), force-include global. -# CC=gcc (NO zig): consistente con binutils/openssl; evita miscompilación. +# zig 0.13.0 (NO gcc): la regresión de zig 0.14 en C/musl (bisect con flex) miscompila; 0.13.0 la +# esquiva, consistente con binutils/openssl/flex. $CC (= zig cc del lab) para los stubs y las fases. name = "elfutils" version = "0.194" @@ -25,9 +26,10 @@ tarball = "https://sourceware.org/elfutils/ftp/0.194/elfutils-0.194.tar.bz2" sha256 = "09e2ff033d39baa8b388a2d7fbc5390bfde99ae3b7c67c7daaf7433fbcf0f01e" [build] -compiler = "zig-cc" -target = "x86_64-linux-musl" -link = "dynamic" +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" +zig_version = "0.13.0" [build.phases] configure = """ @@ -92,13 +94,13 @@ printf '%s\\n' \ 'void argp_error(const struct argp_state*s,const char*f,...){}' \ 'void argp_state_help(const struct argp_state*s,FILE*t,unsigned f){}' \ 'void argp_usage(const struct argp_state*s){}' > compat/argp-stub.c && \ -gcc -c compat/argp-stub.c -o compat/argp-stub.o && ar rcs compat/libargp.a compat/argp-stub.o && \ +$CC -c compat/argp-stub.c -o compat/argp-stub.o && ar rcs compat/libargp.a compat/argp-stub.o && \ printf '%s\\n' \ 'typedef struct{int x;}FTS;typedef struct{int y;}FTSENT;' \ 'FTS*fts_open(char*const*p,int o,int(*c)(const FTSENT**,const FTSENT**)){return 0;}' \ 'FTSENT*fts_read(FTS*f){return 0;}' 'FTSENT*fts_children(FTS*f,int o){return 0;}' \ 'int fts_set(FTS*f,FTSENT*e,int o){return 0;}' 'int fts_close(FTS*f){return 0;}' > compat/fts-stub.c && \ -gcc -c compat/fts-stub.c -o compat/fts-stub.o && ar rcs compat/libfts.a compat/fts-stub.o && \ +$CC -c compat/fts-stub.c -o compat/fts-stub.o && ar rcs compat/libfts.a compat/fts-stub.o && \ printf '%s\\n' \ 'struct obstack;void(*obstack_alloc_failed_handler)(void);int obstack_exit_failure;' \ 'int _obstack_begin(struct obstack*o,int s,int a,void*(*c)(long),void(*f)(void*)){return 1;}' \ @@ -106,15 +108,15 @@ printf '%s\\n' \ 'void _obstack_newchunk(struct obstack*o,int l){}' 'int _obstack_allocated_p(struct obstack*o,void*p){return 0;}' \ 'void _obstack_free(struct obstack*o,void*p){}' 'int _obstack_memory_used(struct obstack*o){return 0;}' \ > compat/obstack-stub.c && \ -gcc -c compat/obstack-stub.c -o compat/obstack-stub.o && ar rcs compat/libobstack.a compat/obstack-stub.o && \ -CC=gcc CFLAGS="-O2 -g -I$PWD/compat" CPPFLAGS="-I$PWD/compat" LDFLAGS="-L$PWD/compat" \ +$CC -c compat/obstack-stub.c -o compat/obstack-stub.o && ar rcs compat/libobstack.a compat/obstack-stub.o && \ +CFLAGS="-O2 -g -I$PWD/compat" CPPFLAGS="-I$PWD/compat" LDFLAGS="-L$PWD/compat" \ ./configure --prefix=/usr --libdir=/usr/lib --disable-nls --disable-demangler \ --disable-libdebuginfod --disable-debuginfod --program-prefix=eu- """ # El -include del shim de rawmemchr va SÓLO en compile (en configure rompe el test "undeclared builtins" # de autoconf). CFLAGS aquí reemplaza el de configure (mismo valor + el -include); AM_CFLAGS (warnings/ # -Werror) se preserva. -compile = "CC=gcc make -C lib CFLAGS=\"-O2 -g -I$PWD/compat -include $PWD/compat/musl-extra.h\" && CC=gcc make -C libelf libelf.a libelf.so CFLAGS=\"-O2 -g -I$PWD/compat -include $PWD/compat/musl-extra.h\"" +compile = "make -C lib CFLAGS=\"-O2 -g -I$PWD/compat -include $PWD/compat/musl-extra.h\" && make -C libelf libelf.a libelf.so CFLAGS=\"-O2 -g -I$PWD/compat -include $PWD/compat/musl-extra.h\"" install = """ mkdir -p /out/usr/lib/pkgconfig /out/usr/include && \ cp -a libelf/libelf.a libelf/libelf.so* /out/usr/lib/ && \ diff --git a/recipes/flex.toml b/recipes/flex.toml index 76cebd6a..9bf025df 100644 --- a/recipes/flex.toml +++ b/recipes/flex.toml @@ -14,16 +14,17 @@ tarball = "https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.ta sha256 = "e87aae032bf07c26f85ac0ed3250998c37621d95f8bd748b31f15b33c45ee995" [build] -compiler = "zig-cc" -target = "x86_64-linux-musl" -link = "dynamic" +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" +# zig 0.13.0 en vez del 0.16 por defecto: flex se auto-construye (un `stage1flex` procesa su propio +# scan.l → stage1scan.c) y con zig 0.14+ ese stage1flex sale miscompilado → "unrecognized rule". El +# bug es una REGRESIÓN introducida en zig 0.14 (bisect: 0.13.0 compila flex LIMPIO, 0.14/0.15/0.16 +# fallan), específica del path musl DINÁMICO. Antes esto se esquivaba con `CC=gcc`; ahora con el zig +# bueno por-receta ⇒ gcc deja de hacer falta (toolchain 100% zig). No es input del 4/4. SDD 11 §7.2b. +zig_version = "0.13.0" -# CC=gcc/CXX=g++ (NO zig), igual que binutils/python3/cmake: flex se auto-construye (un `stage1flex` -# procesa su propio scan.l → stage1scan.c). Con `zig cc` ese stage1flex sale miscompilado y falla con -# "unrecognized rule" al parsear scan.l (zig 0.16 miscompila binarios musl no-triviales). gcc nativo -# (como Alpine) lo produce funcional. link=dynamic: gcc enlaza el musl del toolchain (estático con -# libtool+stage-scanner es frágil; flex es herramienta de build-time, corre, no se enlaza en el 4/4). [build.phases] -configure = "CC=gcc CXX=g++ ./configure --prefix=/usr --disable-nls --disable-shared" -compile = "CC=gcc CXX=g++ make -j\"$(nproc)\"" +configure = "./configure --prefix=/usr --disable-nls --disable-shared" +compile = "make -j\"$(nproc)\"" install = "make install DESTDIR=/out PREFIX=/usr" diff --git a/recipes/openssl.toml b/recipes/openssl.toml index f203b438..f6a6c8a7 100644 --- a/recipes/openssl.toml +++ b/recipes/openssl.toml @@ -22,11 +22,15 @@ tarball = "https://github.com/openssl/openssl/releases/download/openssl-3.5.4/op sha256 = "967311f84955316969bdb1d8d4b983718ef42338639c621ec4c34fddef355e99" [build] -compiler = "zig-cc" -target = "x86_64-linux-musl" -link = "static" +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +# zig 0.13.0 en vez del 0.16: el gcc era precautorio por el riesgo de miscompilación de zig 0.16 en +# binarios grandes (regresión 0.14). 0.13.0 lo esquiva (mismo bisect que flex). openssl es C+asm GAS, +# que el assembler integrado de zig cc (clang) maneja. Con el zig bueno por-receta gcc no hace falta. +zig_version = "0.13.0" [build.phases] -configure = "CC=gcc ./Configure linux-x86_64 --prefix=/usr --libdir=lib no-shared no-docs no-tests no-apps" -compile = "CC=gcc make -j\"$(nproc)\" build_libs" +configure = "./Configure linux-x86_64 --prefix=/usr --libdir=lib no-shared no-docs no-tests no-apps" +compile = "make -j\"$(nproc)\" build_libs" install = "make DESTDIR=/out install_dev" diff --git a/recipes/python3.toml b/recipes/python3.toml index f7204b81..b99a0f6a 100644 --- a/recipes/python3.toml +++ b/recipes/python3.toml @@ -27,14 +27,18 @@ tarball = "https://www.python.org/ftp/python/3.12.10/Python-3.12.10.tgz" sha256 = "15d9c623abfd2165fe816ea1fb385d6ed8cf3c664661ab357f1782e3036a6dac" [build] -compiler = "zig-cc" -target = "x86_64-linux-musl" -link = "dynamic" +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "dynamic" +# zig 0.13.0 (NO gcc): con zig 0.14+ el intérprete `_bootstrap_python` SEGFAULTEA en "freeze modules" +# (regresión 0.14 en C/musl, misma clase que flex). 0.13.0 la esquiva ⇒ gcc deja de hacer falta. +# CFLAGS=-Wno-error=date-time se conserva: clang (zig cc) NO silencia -Wdate-time vía SOURCE_DATE_EPOCH. +zig_version = "0.13.0" [deps] build = ["zlib"] [build.phases] -configure = "CC=gcc CXX=g++ ./configure --prefix=/usr --without-ensurepip --disable-test-modules --with-ensurepip=no CFLAGS=\"-Wno-error=date-time\"" +configure = "./configure --prefix=/usr --without-ensurepip --disable-test-modules --with-ensurepip=no CFLAGS=\"-Wno-error=date-time\"" compile = "make -j\"$(nproc)\"" install = "make install DESTDIR=/out"