From db84ee6647cd8a890298f76ff1e03258ffe42091 Mon Sep 17 00:00:00 2001 From: sergio Date: Sun, 21 Jun 2026 10:00:35 -0400 Subject: [PATCH] =?UTF-8?q?Etapa=20G:=20tanda=20C=20tier-2=20=E2=80=94=20i?= =?UTF-8?q?mport-batch=20PREFER=3Dalpine=20+=20importador=20separa=20runti?= =?UTF-8?q?me=20depends?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arranca el tier-2 de la escalera (C clasico via Alpine, que trae los parches musl): - import-batch.sh: env PREFER=nix|alpine. Para tandas C, PREFER=alpine antepone Alpine — el import de nix de un C tiene exito (tarball) pero SIN parches musl => romperia al construir. Refactor a 'tiers' ordenados (misma escalera, dos sentidos). - tandas/cli-c.txt: primera tanda C (tree/which/sed/gawk/gzip/tar/jq/file). - importador Alpine: build-deps = SOLO makedepends. El depends de abuild es RUNTIME (gzip depends=less para zless) — no hace falta para compilar y rompia hammer build (buscaba recipes/less.toml). Ahora va como comentario de provenance. Mismo patron que el fix de buildInputs-de-nix en recetas Rust. VALIDADO: import C 8/8 desde Alpine/main con parches musl bajados; gzip pierde el less espurio. test extracts_source_patches_deps_phases actualizado (depends != build-dep). Co-Authored-By: Claude Opus 4.8 --- crates/hammer-cli/src/alpine_import.rs | 36 ++++++++++++------- scripts/import-batch.sh | 48 ++++++++++++++++---------- tandas/cli-c.txt | 13 +++++++ 3 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 tandas/cli-c.txt diff --git a/crates/hammer-cli/src/alpine_import.rs b/crates/hammer-cli/src/alpine_import.rs index 2e850925..f9e1ec2b 100644 --- a/crates/hammer-cli/src/alpine_import.rs +++ b/crates/hammer-cli/src/alpine_import.rs @@ -60,10 +60,13 @@ pub fn recipe_from_apkbuild(text: &str) -> Result { ) }; - // deps: makedepends + depends (nombres; filtramos self-subpaquetes y los negados `!x`). - let mut deps: Vec = Vec::new(); + // build-deps: SÓLO `makedepends`. El `depends` de abuild es RUNTIME (p.ej. gzip depends="less" + // para `zless`) — no hace falta para compilar y rompería `hammer build` (busca recipes/less.toml). + // Lo emitimos como comentario de provenance, no como `[deps] build`. (checkdepends ni se lee: no + // corremos la suite de tests del paquete.) let self_prefix = format!("{pkgname}-"); - for src in [get("makedepends"), get("depends")] { + let normalize = |src: &str| -> Vec { + let mut v: Vec = Vec::new(); for d in src.split_whitespace() { let d = d.trim(); // Saltamos: negados (!x), variables sin expandir ($x), pins versionados a subpaquetes @@ -84,17 +87,22 @@ pub fn recipe_from_apkbuild(text: &str) -> Result { .unwrap_or(d) .trim_end_matches("-dev") .to_string(); - if !name.is_empty() && !name.starts_with("so:") && !deps.contains(&name) { - deps.push(name); + if !name.is_empty() && !name.starts_with("so:") && !v.contains(&name) { + v.push(name); } } - } - let deps_block = if deps.is_empty() { - String::new() - } else { - let list = deps.iter().map(|d| format!("\"{d}\"")).collect::>().join(", "); - format!("\n[deps]\nbuild = [{list}]\n") + v }; + let deps = normalize(&get("makedepends")); + let runtime = normalize(&get("depends")); + let mut deps_block = String::new(); + if !runtime.is_empty() { + deps_block.push_str(&format!("\n# depends de runtime de Alpine (NO build-deps): {}\n", runtime.join(", "))); + } + if !deps.is_empty() { + let list = deps.iter().map(|d| format!("\"{d}\"")).collect::>().join(", "); + deps_block.push_str(&format!("\n[deps]\nbuild = [{list}]\n")); + } // build()/package() → fases (best-effort, comentadas como abuild-shell a adaptar). let phases_block = { @@ -316,11 +324,13 @@ package() { assert_eq!(recipe.source.tarball.as_deref(), Some("https://example.com/foo-2.5.tar.gz")); // LOS PARCHES DE MUSL cargados (el punto central): assert_eq!(recipe.source.patches, vec!["foo-musl-fix.patch", "foo-static.patch"]); - // deps: makedepends + depends, sin `-dev`: + // build-deps: SÓLO makedepends, sin `-dev`. El runtime `depends="bar"` NO entra como + // build-dep (rompería `hammer build`); queda como comentario de provenance. assert!(recipe.deps.build.contains(&"zlib".to_string())); assert!(recipe.deps.build.contains(&"openssl".to_string())); assert!(recipe.deps.build.contains(&"linux-headers".to_string())); - assert!(recipe.deps.build.contains(&"bar".to_string())); + assert!(!recipe.deps.build.contains(&"bar".to_string()), "depends de runtime no es build-dep"); + assert!(toml.contains("# depends de runtime de Alpine (NO build-deps): bar"), "provenance: {toml}"); // fases capturadas de build()/package() y TRADUCIDAS: $pkgdir → /out. assert!(recipe.build.phases.compile.as_deref().unwrap().contains("./configure")); let install = recipe.build.phases.install.as_deref().unwrap(); diff --git a/scripts/import-batch.sh b/scripts/import-batch.sh index d8a3c675..2b748fb1 100755 --- a/scripts/import-batch.sh +++ b/scripts/import-batch.sh @@ -13,9 +13,10 @@ # Ej: scripts/import-batch.sh ripgrep fd bat hyperfine # scripts/import-batch.sh -f tandas/cli-rust.txt # OUTDIR=recipes/incoming scripts/import-batch.sh sd dust -# Env: OUTDIR (default recipes), NIX_STORE (p.ej. $HOME/.nixstore si /nix/store no es escribible), -# ALPINE_BRANCH (default master), HAMMER (cmd del cli). Un nombre puede mapear nix→alpine -# distintos con `nixattr=alpinepkg` (p.ej. `ripgrep=ripgrep`, `the_silver_searcher=the-silver-searcher`). +# Env: OUTDIR (default recipes), PREFER (nix|alpine; default nix — alpine antepone Alpine para tandas +# C, que necesitan sus parches musl), NIX_STORE (p.ej. $HOME/.nixstore si /nix/store no es +# escribible), ALPINE_BRANCH (default master), HAMMER (cmd del cli). Un nombre puede mapear +# nix→alpine distintos con `nixattr=alpinepkg` (p.ej. `the_silver_searcher=the-silver-searcher`). set -eu here=$(dirname "$0") @@ -31,6 +32,12 @@ else pkgs=$(printf '%s\n' "$@") fi +# Orden de fuentes: PREFER=nix (default, tier-1 Rust/Go) o PREFER=alpine (tier-2 C, trae parches musl). +case "${PREFER:-nix}" in + alpine) TIERS="alpine-main alpine-community nix" ;; + nix|*) TIERS="nix alpine-main alpine-community" ;; +esac + mkdir -p "$OUTDIR" nix_n=0; alpine_n=0; failed=""; got="" @@ -47,22 +54,27 @@ for entry in $pkgs; do out="$OUTDIR/${nixattr}.toml" printf '%-24s ' "$nixattr" >&2 - if "$here/nix-import.sh" "$nixattr" > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then - mv "$out.tmp" "$out" - nix_n=$((nix_n + 1)); got="$got $nixattr" - echo "✓ nix" >&2 - elif "$here/alpine-import.sh" "$alpinepkg" main > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then - mv "$out.tmp" "$out" - alpine_n=$((alpine_n + 1)); got="$got $nixattr" - echo "✓ alpine/main (parches musl)" >&2 - elif "$here/alpine-import.sh" "$alpinepkg" community > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then - mv "$out.tmp" "$out" - alpine_n=$((alpine_n + 1)); got="$got $nixattr" - echo "✓ alpine/community (parches musl)" >&2 - else - rm -f "$out.tmp" + done_pkg="" + # Orden de fuentes según PREFER (default nix, ~100% Rust/Go). PREFER=alpine antepone Alpine para + # tandas C: el import de nix de un C tiene éxito (tarball) pero SIN los parches musl ⇒ rompería al + # construir; Alpine los TRAE. Cada "tier" es una etiqueta y el comando que produce la receta. + for tier in $TIERS; do + case $tier in + nix) src="nix"; cmd_out=$("$here/nix-import.sh" "$nixattr" 2>/dev/null) || cmd_out="" ;; + alpine-main) src="alpine/main"; cmd_out=$("$here/alpine-import.sh" "$alpinepkg" main 2>/dev/null) || cmd_out="" ;; + alpine-comm*) src="alpine/community"; cmd_out=$("$here/alpine-import.sh" "$alpinepkg" community 2>/dev/null) || cmd_out="" ;; + esac + if [ -n "$cmd_out" ]; then + printf '%s\n' "$cmd_out" > "$out" + case $tier in nix) nix_n=$((nix_n + 1)) ;; *) alpine_n=$((alpine_n + 1)) ;; esac + got="$got $nixattr"; done_pkg=1 + echo "✓ $src" >&2 + break + fi + done + if [ -z "$done_pkg" ]; then failed="$failed $nixattr" - echo "✗ ni nix ni Alpine" >&2 + echo "✗ ninguna fuente" >&2 fi done diff --git a/tandas/cli-c.txt b/tandas/cli-c.txt new file mode 100644 index 00000000..45556129 --- /dev/null +++ b/tandas/cli-c.txt @@ -0,0 +1,13 @@ +# Tanda CLI C clásico — Etapa G. Tier 2 de la escalera (C/autotools → import-alpine, trae parches musl). +# Uso: PREFER=alpine OUTDIR=recipes/incoming scripts/import-batch.sh -f tandas/cli-c.txt +# Luego: scripts/pin-recipes.sh recipes/incoming/*.toml && build (medir build-yield C real). +# Sintaxis: un paquete por línea; '#' comenta; `nixattr=alpinepkg` si los nombres difieren. +# Evita por ahora los que piden ncurses (less/nano/htop) hasta portar esa lib al corpus. +tree +which +sed +gawk +gzip +tar +jq +file