diff --git a/crates/hammer-cli/src/alpine_import.rs b/crates/hammer-cli/src/alpine_import.rs index f9e1ec2b..c29d83e7 100644 --- a/crates/hammer-cli/src/alpine_import.rs +++ b/crates/hammer-cli/src/alpine_import.rs @@ -260,13 +260,20 @@ fn func_body(text: &str, name: &str) -> Option { /// NO toca `$CBUILD/$CHOST/$srcdir/$builddir` ni `--shared`. `$CBUILD/$CHOST` los RESUELVE el lab en /// runtime (env con el triple nativo saneado, Etapa G Fase 3 — `hammer-build`); `$srcdir/$builddir` y /// `--shared` quedan para el humano. `make DESTDIR="$pkgdir"` → `make DESTDIR="/out"`. +/// +/// Además ENVUELVE el cuerpo en una función shell: abuild ejecuta `build()/package()` COMO funciones, +/// donde `local i; …` es válido; el lab corre la fase bajo `sh -c ""` plano, donde `local` +/// fuera de función es error (rompía gzip al instalar). Envolver restaura el contexto de abuild sin +/// tocar el sandbox ni las fases planas escritas a mano del corpus (sólo se envuelve lo importado). fn translate_abuild(body: &str, name: &str, version: &str) -> String { - body.replace("${pkgdir}", "/out") + let subst = body + .replace("${pkgdir}", "/out") .replace("$pkgdir", "/out") .replace("${pkgname}", name) .replace("$pkgname", name) .replace("${pkgver}", version) - .replace("$pkgver", version) + .replace("$pkgver", version); + format!("_abuild_phase() {{\n{subst}\n}}\n_abuild_phase") } /// Primer hash de un bloque `sha512sums=" \n..."`. @@ -348,6 +355,16 @@ package() { assert!(!t.contains("$pkg")); } + #[test] + fn translate_wraps_body_in_function_for_local() { + // abuild package() usa `local i;` (válido en función); el lab corre la fase plana ⇒ hay que + // envolverla o `local` rompe (gzip). El cuerpo queda DENTRO de la función, invocada al final. + let t = translate_abuild("local i; for i in a b; do mv $i /out/; done", "p", "1"); + assert!(t.starts_with("_abuild_phase() {"), "{t}"); + assert!(t.trim_end().ends_with("_abuild_phase"), "se invoca al final: {t}"); + assert!(t.contains("local i;"), "el `local` se preserva dentro de la función: {t}"); + } + #[test] fn no_patches_is_empty() { let apk = "pkgname=zlib\npkgver=1.3\nsource=\"https://zlib.net/zlib-$pkgver.tar.gz\"\nsha512sums=\"x z\"\n"; diff --git a/recipes/0001-softmagic-compare-FILE_GUID-values-in-wire-format.patch b/recipes/0001-softmagic-compare-FILE_GUID-values-in-wire-format.patch new file mode 100644 index 00000000..49deb6b4 --- /dev/null +++ b/recipes/0001-softmagic-compare-FILE_GUID-values-in-wire-format.patch @@ -0,0 +1,76 @@ +From 80297b6e252963d2c08f84f40bc3b5e517505ff4 Mon Sep 17 00:00:00 2001 +From: Natanael Copa +Date: Tue, 10 Mar 2026 11:54:48 +0100 +Subject: [PATCH] softmagic: compare FILE_GUID values in wire format + +Comparing GUIDs with memcmp() on native struct storage breaks on +big-endian systems like s390x, since EFI GUIDs are stored in mixed-endian +wire format. Convert the parsed magic GUID to on-disk byte order before +comparing against file contents. + +Fixes efi-signature-list-sha256 test on s390x. +--- + src/file.h | 1 + + src/funcs.c | 20 ++++++++++++++++++++ + src/softmagic.c | 2 +- + 3 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/src/file.h b/src/file.h +index 06cf91d1..6f4087cd 100644 +--- a/src/file.h ++++ b/src/file.h +@@ -559,6 +559,7 @@ file_protected int file_checkfmt(char *, size_t, const char *); + file_protected size_t file_printedlen(const struct magic_set *); + file_protected int file_print_guid(char *, size_t, const uint64_t *); + file_protected int file_parse_guid(const char *, uint64_t *); ++file_protected int file_compare_guid(const uint64_t *, const uint64_t *); + file_protected int file_replace(struct magic_set *, const char *, const char *); + file_protected int file_printf(struct magic_set *, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +diff --git a/src/funcs.c b/src/funcs.c +index fa62b26d..c3cc0341 100644 +--- a/src/funcs.c ++++ b/src/funcs.c +@@ -955,6 +955,26 @@ file_print_guid(char *str, size_t len, const uint64_t *guid) + #endif + } + ++file_protected int ++file_compare_guid(const uint64_t *mguid, const uint64_t *fguid) ++{ ++ const struct guid *m = CAST(const struct guid *, CAST(const void *, mguid)); ++ const unsigned char *f = CAST(const unsigned char *, CAST(const void *, fguid)); ++ unsigned char mbuf[16]; ++ ++ mbuf[0] = (unsigned char)(m->data1); ++ mbuf[1] = (unsigned char)(m->data1 >> 8); ++ mbuf[2] = (unsigned char)(m->data1 >> 16); ++ mbuf[3] = (unsigned char)(m->data1 >> 24); ++ mbuf[4] = (unsigned char)(m->data2); ++ mbuf[5] = (unsigned char)(m->data2 >> 8); ++ mbuf[6] = (unsigned char)(m->data3); ++ mbuf[7] = (unsigned char)(m->data3 >> 8); ++ memcpy(mbuf + 8, m->data4, 8); ++ ++ return memcmp(mbuf, f, 16); ++} ++ + file_protected int + file_pipe_closexec(int *fds) + { +diff --git a/src/softmagic.c b/src/softmagic.c +index 1a198005..ee85356d 100644 +--- a/src/softmagic.c ++++ b/src/softmagic.c +@@ -2407,7 +2407,7 @@ magiccheck(struct magic_set *ms, struct magic *m, file_regex_t **m_cache) + return matched; + case FILE_GUID: + l = 0; +- v = memcmp(m->value.guid, p->guid, sizeof(p->guid)); ++ v = file_compare_guid(m->value.guid, p->guid); + break; + default: + file_magerror(ms, "invalid type %d in magiccheck()", m->type); +-- +2.53.0 + diff --git a/recipes/0002-inliniac-revert-previous-and-always-offset.patch b/recipes/0002-inliniac-revert-previous-and-always-offset.patch new file mode 100644 index 00000000..177e2579 --- /dev/null +++ b/recipes/0002-inliniac-revert-previous-and-always-offset.patch @@ -0,0 +1,25 @@ +Patch-Source: https://github.com/file/file/commit/c54605718190ad8fe9c25cb475f1f32ca7cd54f7 +--- +From c54605718190ad8fe9c25cb475f1f32ca7cd54f7 Mon Sep 17 00:00:00 2001 +From: Christos Zoulas +Date: Sun, 12 Apr 2026 22:15:14 +0000 +Subject: [PATCH] PR/725: inliniac: Revert previous and always set offset. + +--- + src/softmagic.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/softmagic.c b/src/softmagic.c +index 9aaf8b3e5..7cf7c798f 100644 +--- a/src/softmagic.c ++++ b/src/softmagic.c +@@ -1577,8 +1577,7 @@ msetoffset(struct magic_set *ms, struct magic *m, struct buffer *bb, + ms->offset = offset; + ms->eoffset = 0; + } else { +- if (b->fd != -1) +- ms->offset = ms->eoffset + offset; ++ ms->offset = ms->eoffset + offset; + } + } + if ((ms->flags & MAGIC_DEBUG) != 0) { diff --git a/recipes/echild-strerror.patch b/recipes/echild-strerror.patch new file mode 100644 index 00000000..5626b690 --- /dev/null +++ b/recipes/echild-strerror.patch @@ -0,0 +1,14 @@ +musl and glibc use different string values for the ECHILD errno. + +diff -upr gawk-5.1.0.orig/test/testext.ok gawk-5.1.0/test/testext.ok +--- gawk-5.1.0.orig/test/testext.ok 2021-02-06 12:39:05.990158877 +0100 ++++ gawk-5.1.0/test/testext.ok 2021-02-06 12:39:24.080236494 +0100 +@@ -21,7 +21,7 @@ var_test: sym_update of ARGC failed - co + var_test: sym_update("testvar") succeeded + var_test() returned 1, test_var = 42 + +-test_errno() returned 1, ERRNO = No child processes ++test_errno() returned 1, ERRNO = No child process + + fubar = 9 + rumpus = -5 diff --git a/recipes/file.toml b/recipes/file.toml new file mode 100644 index 00000000..34d280d9 --- /dev/null +++ b/recipes/file.toml @@ -0,0 +1,40 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "file" +version = "5.47" + +[source] +tarball = "https://astron.com/pub/file/file-5.47.tar.gz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "a19c1f2b584bcfa70d4a02545667a90ff9e069523a5fc7b84d79b2b32cb7e59b73c555943784a13d56fa6d4618e18a254fd6102187def1a4a7d936b41ae7ce31" +sha256 = "45672fec165cb4cc1358a2d76b5d57d22876dcb97ab169427ac385cbe1d5597a" +patches = ["0001-softmagic-compare-FILE_GUID-values-in-wire-format.patch", "0002-inliniac-revert-previous-and-always-offset.patch"] + +[build] +compiler = "gcc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = ''' +_abuild_phase() { +./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --datadir=/usr/share \ + --enable-static + make +} +_abuild_phase +''' +# de package() de Alpine (traducido $pkgdir→/out): +install = ''' +_abuild_phase() { +make DESTDIR="/out" install +} +_abuild_phase +''' diff --git a/recipes/gawk.toml b/recipes/gawk.toml new file mode 100644 index 00000000..d436ea4a --- /dev/null +++ b/recipes/gawk.toml @@ -0,0 +1,36 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "gawk" +version = "5.3.2" + +[source] +tarball = "https://ftp.gnu.org/gnu/gawk/gawk-5.3.2.tar.xz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "2268150fa35ae049a6ff3d0d0fa110db10477014c25f50e2ab4e3ee5fd60133369d2a994f59db4eb718020a0af5c4003ae7278c63e7fffa72f431ff4a1429e48" +sha256 = "f8c3486509de705192138b00ef2c00bbbdd0e84c30d5c07d23fc73a9dc4cc9cc" +patches = ["echild-strerror.patch"] + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = ''' +CFLAGS="$CFLAGS -flto=auto" \ + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --disable-nls \ + --disable-pma + make +''' +# de package() de Alpine (traducido $pkgdir→/out): +install = "make DESTDIR=\"/out\" install" diff --git a/recipes/gcc15.patch b/recipes/gcc15.patch new file mode 100644 index 00000000..c9174df3 --- /dev/null +++ b/recipes/gcc15.patch @@ -0,0 +1,44 @@ +Patch-Source: https://lists.gnu.org/archive/html/which-bugs/2025-03/msg00000.html +--- +From 16a1647fc26953fab659de5f55d4c0defdfb894f Mon Sep 17 00:00:00 2001 +From: Khem Raj +Date: Sat, 22 Mar 2025 17:56:19 -0700 +Subject: [PATCH] getopt: Fix signature of getenv function + +This happens on musl systems using GCC 15 + +../which-2.21/getopt.h:106:12: error: conflicting types for 'getopt'; have +'int(void)' + 106 | extern int getopt (); + | ^~~~~~ +--- + getopt.c | 2 +- + getopt.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/getopt.c b/getopt.c +index 9ac2ed6..7e14270 100644 +--- a/getopt.c ++++ b/getopt.c +@@ -205,7 +205,7 @@ static char *posixly_correct; + /* Avoid depending on library functions or files + whose names are inconsistent. */ + +-char *getenv(); ++char *getenv(const char*); + + static char *my_index(str, chr) const char *str; + int chr; +diff --git a/getopt.h b/getopt.h +index f080053..635fc46 100644 +--- a/getopt.h ++++ b/getopt.h +@@ -102,7 +102,7 @@ struct option { + errors, only prototype getopt for the GNU C library. */ + extern int getopt(int argc, char *const *argv, const char *shortopts); + #else /* not __GNU_LIBRARY__ */ +-extern int getopt(); ++extern int getopt(int, char * const [], const char *); + #endif /* __GNU_LIBRARY__ */ + extern int getopt_long(int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); + extern int getopt_long_only(int argc, char *const *argv, const char *shortopts, const struct option *longopts, diff --git a/recipes/gzip.toml b/recipes/gzip.toml new file mode 100644 index 00000000..ad76df79 --- /dev/null +++ b/recipes/gzip.toml @@ -0,0 +1,54 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "gzip" +version = "1.14" + +[source] +tarball = "https://ftp.gnu.org/gnu/gzip/gzip-1.14.tar.xz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "82aef53188b3e69b51b7ddab5b8c44a11a5b73c0039b22a315a0c7d244694feab0146748add4265901eb1b4c0cee8a9eb69594995f098830d964091af97079c5" +sha256 = "01a7b881bd220bfdf615f97b8718f80bdfd3f6add385b993dcf6efd14e8c0ac6" + +[build] +compiler = "gcc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = ''' +_abuild_phase() { +# avoid text relocation + export DEFS="NO_ASM" + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info + make +} +_abuild_phase +''' +# de package() de Alpine (traducido $pkgdir→/out): +install = ''' +_abuild_phase() { +make DESTDIR=/out install + + rm -rf "/out"/usr/lib/charset.alias + rmdir -p "/out"/usr/lib 2>/dev/null || true + + mkdir -p "/out"/bin + local i; for i in gzip gunzip zcat; do + mv "/out"/usr/bin/$i "/out"/bin/ + done + + # http://bugs.alpinelinux.org/issues/4011 + ln -sf /bin/gunzip "/out"/usr/bin/uncompress +} +_abuild_phase +''' + +# depends de runtime de Alpine (NO build-deps): less diff --git a/recipes/ignore-apk-tools-checksums.patch b/recipes/ignore-apk-tools-checksums.patch new file mode 100644 index 00000000..cd6a1e1c --- /dev/null +++ b/recipes/ignore-apk-tools-checksums.patch @@ -0,0 +1,17 @@ +Make GNU tar ignore the apk-tools header fields + +This should problably not be upstreamed + +diff --git a/src/xheader.c b/src/xheader.c +index 1347ce1..89b3a44 100644 +--- a/src/xheader.c ++++ b/src/xheader.c +@@ -1777,5 +1777,8 @@ struct xhdr_tab const xhdr_tab[] = { + they are restored *only once* during extraction later on. */ + { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true }, + ++ /* Ignore apk-tools headers */ ++ { "APK-TOOLS", dummy_coder, dummy_decoder, 0, true }, ++ + { NULL, NULL, NULL, 0, false } + }; diff --git a/recipes/tar.toml b/recipes/tar.toml new file mode 100644 index 00000000..5fc9c995 --- /dev/null +++ b/recipes/tar.toml @@ -0,0 +1,41 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "tar" +version = "1.35" + +[source] +tarball = "https://ftp.gnu.org/gnu/tar/tar-1.35.tar.xz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "8b84ed661e6c878fa33eb5c1808d20351e6f40551ac63f96014fb0d0b9c72d5d94d8865d39e36bcb184fd250f84778a3b271bbd8bd2ceb69eece0c3568577510" +sha256 = "4d62ff37342ec7aed748535323930c7cf94acf71c3591882b26a7ea50f3edc16" +patches = ["ignore-apk-tools-checksums.patch"] + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = ''' +export CFLAGS="$CFLAGS -flto=auto" + gl_cv_func_gettimeofday_clobber=no \ + gl_cv_func_tzset_clobber=no \ + ./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --localstatedir=/var + make +''' +# de package() de Alpine (traducido $pkgdir→/out): +install = ''' +make DESTDIR="/out" install + + mkdir -p "/out"/bin + mv -v "/out"/usr/bin/tar "/out"/bin/ +''' diff --git a/recipes/tree.toml b/recipes/tree.toml new file mode 100644 index 00000000..eb67fe90 --- /dev/null +++ b/recipes/tree.toml @@ -0,0 +1,23 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "tree" +version = "2.3.2" + +[source] +tarball = "https://gitlab.com/OldManProgrammer/unix-tree/-/archive/2.3.2/unix-tree-2.3.2.tar.gz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "c22dd6bd6074f521959d31c3c9adbccec5a039029ca818df8345c7572f5b29b4b7b813e9ce4336cc0d600095130a709f7cbf7f88765a7a1e6894b16701d1fcd9" +sha256 = "513a53cbc42ca1f4ea06af2bab1f5283524a3848266b1d162416f8033afc4985" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = "make LDFLAGS=\"$LDFLAGS\" CFLAGS=\"$CFLAGS\"" +# de package() de Alpine (traducido $pkgdir→/out): +install = "make PREFIX=\"/out/usr\" MANDIR=\"/out/usr/share/man\" install" diff --git a/recipes/which.toml b/recipes/which.toml new file mode 100644 index 00000000..1ddb7294 --- /dev/null +++ b/recipes/which.toml @@ -0,0 +1,30 @@ +# Importada de Alpine aports por `hammer import-alpine` (Etapa G). PUNTO DE PARTIDA — pero +# YA trae los parches de musl de Alpine (lo que un import de nix pierde). Pendiente: el +# sha256 del tarball (el wrapper lo calcula), y adaptar build/install del shell de abuild. +name = "which" +version = "2.23" + +[source] +tarball = "https://ftp.gnu.org/gnu/which/which-2.23.tar.gz" +# FIXME sha256: el wrapper lo calcula (Alpine publica sha512). sha512 de Alpine: +# sha512 = "738807f79e8cfc5967541a28ae7021247c04c4177279f09be2c19c069af450a7e3b19baf9079fe5569b25b4630bb400be242a123647e52c9fe54f0ad007317bf" +sha256 = "a2c558226fc4d9e4ce331bd2fd3c3f17f955115d2c00e447618a4ef9978a2a73" +patches = ["gcc15.patch"] + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +flags = [] + +[build.phases] +# de build() de Alpine (traducido; el lab provee $CBUILD/$CHOST — Etapa G Fase 3; revisá --shared para estático): +compile = ''' +./configure \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr + make +''' +# de package() de Alpine (traducido $pkgdir→/out): +install = "make DESTDIR=\"/out\" install"