tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1569Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ 2e4cfcaa6b78066e05fa0d49c8d7c8ebf0a055d6 recipes/exiv2.toml TOML · 173 siq'ikuna · 11.5 KB Ñawpa willay ch'uya

exiv2 + un falso verde del vigía de sonames 2e4cfcaa6 · Sergio ·

1# exiv2 0.28.9 — metadatos de imagen (EXIF, IPTC, XMP). Entra por una razón concreta y visible.
2#
3# ══ POR QUÉ NO ES UN ADORNO: SIN ESTO LAS FOTOS SALEN GIRADAS ══════════════════════════════════
4# Una cámara de móvil casi nunca rota los píxeles: guarda la imagen como la leyó el sensor y anota
5# la orientación en una etiqueta EXIF. Un visor que no lee EXIF muestra **todas las fotos verticales
6# tumbadas**, sin ningún error y sin que el usuario pueda hacer nada. Es la clase de fallo que no
7# aparece en ninguna métrica del repo y que cualquiera nota en el primer segundo.
8#
9# ⇒ Entra como dep de `swayimg` (el visor), cuya opción `exif` la exige por pkg-config.
10#
11# ══ LO QUE CUESTA: NADA NUEVO ══════════════════════════════════════════════════════════════════
12# Medido sobre `cmake/findDependencies.cmake`, no sobre los paquetes de otra distro:
13#   · `ZLIB` y `Brotli`   REQUIRED — los dos están en el corpus
14#   · `EXPAT`             REQUIRED **sólo si** XMP está encendido; está en el corpus
15#   · `Filesystem`        es `std::filesystem` de C++17, que la libc++ de zig provee
16#   · `CURL`              sólo con `EXIV2_ENABLE_WEBREADY`, que se apaga
17#   · `Intl`              sólo con NLS, que se apaga
18#   · `Iconv`             `find_package` sin REQUIRED ⇒ opcional de verdad (musl trae iconv)
19#   · `inih`              ⚠ **ME EQUIVOQUÉ ACÁ**: leí `find_package(inih)` sin REQUIRED y lo conté
20#                         como opcional. Su `cmake/Findinih.cmake:28` hace
21#                         `message(FATAL_ERROR "inih library not found")` **por su cuenta**, así que
22#                         corta igual. Leí la LLAMADA y no el MÓDULO — la misma trampa que este repo
23#                         documenta para `find_library` sin REQUIRED, donde la cura es leer el
24#                         `else()`. Lo que sí hay es un guard una línea arriba
25#                         (`if(EXIV2_ENABLE_INIH)`), y por eso se apaga explícitamente.
26# ⇒ cero recetas nuevas.
27#
28# ══ LAS PERILLAS ═══════════════════════════════════════════════════════════════════════════════
29#   EXIV2_ENABLE_XMP=ON       XMP es lo que escriben Lightroom, darktable y el propio Android; sin
30#                             esto se leen EXIF e IPTC y se ignora la mitad de los metadatos reales
31#   EXIV2_ENABLE_BROTLI=ON    JPEG XL y algunos contenedores comprimen metadatos con brotli
32#   EXIV2_ENABLE_WEBREADY=OFF leer metadatos por HTTP desde la biblioteca. Una biblioteca de imagen
33#                             que abre sockets es superficie de ataque que nadie pidió
34#   EXIV2_ENABLE_NLS=OFF      evita la cadena de gettext, cuyo `msgfmt` en este lab tiene huecos
35#                             conocidos (ver `recipes/brltty.toml`)
36#   EXIV2_BUILD_EXIV2_COMMAND=ON  la CLI `exiv2`: es la única forma de comprobar a mano qué
37#                             metadatos lee un fichero, sin abrir un visor
38#   EXIV2_ENABLE_INIH=OFF     lo que se pierde es LEER UN `exiv2.ini` con nombres de objetivos
39#                             fotográficos personalizados — exiv2 sigue reconociendo los objetivos
40#                             que trae en sus tablas internas. Es proporcionado frente a meter dos
41#                             recetas (`inih` publica una biblioteca C y un `INIReader` de C++, y su
42#                             Find exige las dos)
43#
44# ══ ⚠⚠ LAS ESTÁTICAS SE PINEAN A MANO, Y ESTO ES EL HALLAZGO IMPORTANTE DE ESTA RECETA ═════════
45# La primera versión selló un artefacto que **no habría cargado**, y el motivo no se ve en ningún
46# log del build:
47#
48#     readelf -d libexiv2.so  ⇒  NEEDED libexpat.so.1 · NEEDED libz.so.1
49#     ls <artefacto expat>/usr/lib  ⇒  libexpat.a          (¡sólo estática!)
50#     ls <artefacto zlib>/usr/lib   ⇒  libz.a              (¡sólo estática!)
51#     ls .dev-fs/alpine/usr/lib     ⇒  libexpat.so.1 · libz.so.1      ← de acá salieron
52#
53# O sea que cmake enlazó contra las compartidas **DEL LAB**. El artefacto sella, reproduce, y en una
54# imagen el cargador no encontraría ni `libexpat.so.1` ni `libz.so.1`, porque el lab no viaja.
55# Es la familia del «NEEDED colgante»: sella, reproduce y no corre.
56#
57# ⇒ Se pinean las `.a` del corpus por ruta explícita. Se comprobó que **son PIC** (217 y 270
58# relocaciones GOT/PLT), que es lo que permite que entren en una `.so`. El repo también tiene
59# `expat-shared` y `zlib-shared` para este caso; se eligió la estática porque deja el artefacto
60# **autosuficiente y verificable desde dentro del sandbox**: con la compartida el `NEEDED` sería
61# legítimo, pero el lab está montado igual y desde dentro **no hay forma de distinguir** si la `.so`
62# vino del corpus o de la imagen.
63#
64# ⚠ Y `brotli` no hizo falta pinearlo: no hay `libbrotli*.so` en el lab, así que su `.a` entró sola.
65# **Eso es exactamente lo que hace al fallo tan difícil de ver** — la misma receta enlaza bien una
66# dep y mal otra, según lo que el lab tenga encima.
67#
68# ⚠ `-Wno-error=date-time`: `src/version.cpp:431-432` usa `__DATE__`/`__TIME__` y el lab compila
69# con `-Werror=date-time`, su política de reproducibilidad. Desactivarlo es seguro porque el sandbox
70# exporta `SOURCE_DATE_EPOCH=1` y con eso clang hace que esos macros rindan la fecha fija en vez de
71# la del reloj, así que el binario sigue determinista. Mismo razonamiento que `fastfetch`, `cairo`,
72# `appstream` y `brltty` — **cinco recetas con el mismo parche** ya empieza a parecer un caso para
73# decidir en el lab y no receta por receta, pero eso es su propia unidad de trabajo.
74#
75# ⚠ `BUILD_SHARED_LIBS=ON` y es SEGURO acá, a diferencia de `libime`: `swayimg` es un programa en C
76# que enlaza `libexiv2.so`, así que el runtime de C++ vive ENTERO dentro de la biblioteca y ningún
77# objeto de C++ cruza el límite de módulo. El problema de las cuatro copias de libc++ aparece cuando
78# un `std::` creado en un módulo se usa en otro — acá no pasa.
79name    = "exiv2"
80version = "0.28.9"
81license = "GPL-2.0-or-later"
82
83[source]
84tarball = "https://github.com/Exiv2/exiv2/archive/refs/tags/v0.28.9/exiv2-0.28.9.tar.gz"
85sha256  = "700b76b97695b2fab4ef8c79619c68ae57d09e0c130724791cafbd39e0eb4aef"
86
87[build]
88compiler = "zig-cc"
89target   = "x86_64-linux-musl"
90link     = "dynamic"
91flags    = []
92
93[build.phases]
94configure = '''
95ZW="$PWD/.zwrap"; mkdir -p "$ZW"
96printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec /opt/zig/zig cc -mcpu=baseline "$@"' > "$ZW/cc"
97printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec /opt/zig/zig c++ -mcpu=baseline "$@"' > "$ZW/cxx"
98chmod +x "$ZW/cc" "$ZW/cxx"
99# `rm -rf build`: un build/ fallido sobrevive en el árbol reutilizado y el caché de cmake hace que
100# el reintento repita el error viejo en dos segundos.
101rm -rf build
102cmake -B build -G Ninja -Wno-dev \
103  -DCMAKE_C_COMPILER="$ZW/cc" -DCMAKE_CXX_COMPILER="$ZW/cxx" \
104  -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
105  -DCMAKE_INSTALL_LIBDIR=lib \
106  -DPKG_CONFIG_ARGN=--static \
107  -DCMAKE_C_FLAGS=-Wno-error=date-time -DCMAKE_CXX_FLAGS=-Wno-error=date-time \
108  -DBUILD_SHARED_LIBS=ON \
109  -DEXIV2_ENABLE_XMP=ON -DEXIV2_ENABLE_BROTLI=ON \
110  -DEXIV2_ENABLE_WEBREADY=OFF -DEXIV2_ENABLE_NLS=OFF \
111  -DEXIV2_ENABLE_BMFF=ON \
112  -DEXIV2_ENABLE_INIH=OFF \
113  -DEXPAT_LIBRARY=/usr/lib/libexpat.a -DEXPAT_INCLUDE_DIR=/usr/include \
114  -DZLIB_LIBRARY=/usr/lib/libz.a -DZLIB_INCLUDE_DIR=/usr/include \
115  -DEXIV2_BUILD_EXIV2_COMMAND=ON -DEXIV2_BUILD_SAMPLES=OFF -DEXIV2_BUILD_UNIT_TESTS=OFF
116'''
117compile = "cmake --build build"
118# El guardián comprueba que las PERILLAS se hayan materializado, no sólo que haya ficheros. Una
119# exiv2 construida sin XMP o sin brotli se sella igual, exporta la misma API, y devuelve «no hay
120# metadatos» sobre ficheros que sí los tienen — un fallo que se lee como «la foto no trae EXIF».
121# La prueba es el NEEDED de la compartida, que es donde se ve si la dependencia entró de verdad.
122install = '''
123set -e
124DESTDIR=/out cmake --install build
125so=$(find /out/usr/lib -name 'libexiv2.so*' -type f 2>/dev/null | head -1)
126[ -n "$so" ] || { echo "exiv2: no quedó libexiv2.so — swayimg la enlaza" >&2; find /out -name 'libexiv2*' >&2; exit 1; }
127[ -f /out/usr/include/exiv2/exiv2.hpp ] || { echo "exiv2: falta include/exiv2/exiv2.hpp" >&2; exit 1; }
128pc=$(find /out/usr/lib -name 'exiv2.pc' 2>/dev/null | head -1)
129[ -n "$pc" ] || { echo "exiv2: falta exiv2.pc — swayimg la busca por pkg-config (dependency('exiv2'))" >&2; exit 1; }
130[ -x /out/usr/bin/exiv2 ] || { echo "exiv2: falta la CLI /usr/bin/exiv2 — es la única forma de inspeccionar metadatos a mano" >&2; exit 1; }
131
132# XMP necesita expat y brotli necesita libbrotlidec: si las perillas no se materializaron, no
133# aparecen en NEEDED. `.a` del corpus no declararían NEEDED, así que se admiten las dos formas:
134# o el soname en NEEDED, o el símbolo exportado si entró estático.
135# ⚠ **ESTE CONTROL ESTUVO MAL Y DEJÓ PASAR EL ARTEFACTO ROTO.** Mi versión anterior aceptaba
136# «`libexpat` aparece en NEEDED **o** el símbolo `XML_Parse` existe en la tabla», y las dos ramas
137# daban verde sobre el fallo: el NEEDED estaba ahí **y era colgante**, y `readelf -s | grep XML_Parse`
138# casa igual con una entrada **UND** (sin definir) que con una definida.
139#
140# ⇒ Ahora se exige que los símbolos estén **DEFINIDOS** —columna de sección distinta de `UND`— y
141# que NO quede ningún NEEDED hacia una biblioteca que el corpus sólo publica estática. Un símbolo
142# presente no es un símbolo resuelto.
143definido() {
144  readelf -sW "$1" 2>/dev/null | awk -v s="$2" '$8==s && $7!="UND" {found=1} END{exit !found}'
145}
146definido "$so" XML_Parse || {
147  echo "exiv2: XML_Parse no está DEFINIDO en la compartida — expat no entró (o entró como referencia colgante) y el XMP no funcionaría" >&2
148  readelf -sW "$so" 2>/dev/null | awk '/XML_Parse/{print "  "$7, $8}' >&2
149  exit 1
150}
151definido "$so" BrotliDecoderDecompress || definido "$so" BrotliDecoderCreateInstance || {
152  echo "exiv2: ningún símbolo BrotliDecoder* DEFINIDO — los metadatos comprimidos no se leerían" >&2
153  exit 1
154}
155# Y el control que faltaba: NINGÚN NEEDED hacia lo que el corpus sólo tiene estático. Si aparece,
156# salió del LAB y la imagen no lo tendrá.
157for mal in libexpat libz.so libbrotli; do
158  readelf -d "$so" 2>/dev/null | grep NEEDED | grep -q "$mal" && {
159    echo "exiv2: NEEDED hacia $mal — el corpus sólo publica esa dep ESTÁTICA, así que esa .so salió del lab y no viajaría en ninguna imagen" >&2
160    readelf -d "$so" 2>/dev/null | grep NEEDED >&2
161    exit 1
162  }
163done
164# Y que la CLI ARRANQUE: es C++ y una ruta mal resuelta la deja muerta aunque el fichero exista.
165# Ejecución y truncado SEPARADOS — el estado de una tubería es el del último comando, y eso ya me
166# selló un fallo hoy (ver `recipes/automake.toml`).
167sal=$(/out/usr/bin/exiv2 --version 2>&1) || { echo "exiv2: la CLI no arranca" >&2; printf '%s\n' "$sal" >&2; exit 1; }
168printf '%s\n' "$sal" | head -1 | grep -qE '^exiv2 0\.28\.9' || { echo "exiv2: --version inesperado: $(printf '%s\n' "$sal" | head -1)" >&2; exit 1; }
169echo "exiv2: $(basename "$so") + CLI + exiv2.pc · xmp y brotli presentes"
170'''
171
172[deps]
173build = ["cmake", "samurai", "pkgconf", "zlib", "brotli", "expat", "binutils"]