recetas: luajit 2.1 (rolling) — el intérprete que swayimg 5.x exige y mpv prefiere
Tercer Lua del catálogo y hay razón para cada uno: `lua` 5.4 para wireplumber,
`lua5.2` para el OSC de mpv, y ésta porque hay consumidores que piden LuaJIT POR
NOMBRE — swayimg >=5.0 hace dependency('luajit') sin alternativa. No es la misma ABI:
LuaJIT es Lua 5.1 + FFI + JIT. Convive sin pisar: todas sus rutas ya vienen
versionadas (include/luajit-2.1, libluajit-5.1.so.2, /usr/bin/luajit-2.1.<relver>).
⚠ EL PIN DE COMMIT NO ALCANZA. Desde que 2.1 es rolling no hay tags: la versión sale
de `git show -s --format=%ct` sobre el árbol, y NO es cosmética — entra en un SÍMBOLO
EXPORTADO (luaJIT_version_2_1_ROLLING). Dos builds del mismo commit con distinto
estado de .git publican símbolos distintos y el consumidor muere con símbolo
indefinido. Se escribe .relver a mano Y se borra .git, porque la rama que consulta git
gana si el directorio existe. Verificado: nm -D da luaJIT_version_2_1_1787165859.
LIBS=-lunwind: la misma deuda que ya paga librsvg. Sin esto, nueve _Unwind_*
indefinidos desde lj_err.o — LuaJIT usa unwinding DWARF de verdad y en glibc ese
runtime vive en libgcc_s, que musl no tiene. Va por LIBS= y no LDFLAGS= porque
TARGET_ALIBS se expande DESPUÉS de los objetos, que es donde el linker resuelve.
TARGET_STRIP=true: su Makefile strippea como parte de la compilación, lo que pediría
binutils y borraría el .debug_* antes de que hammer lo parta (SDD 23).
Evidencia de que anda: NEEDED = libc.so y nada más; jit.status() da true con todas las
optimizaciones; FFI llama a C; y los cuatro caminos de unwinding responden — pcall,
error DENTRO de un trace compilado (err_unwind_jit, el que pedía _Unwind_GetIP),
metamétodo y xpcall+traceback.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo
Delta semántico
Ninguna definición cambió: el cambio es de texto, documentación o datos.
1 archivos · +89 −0
- recipes/luajit.toml +89 −0
diff --git a/recipes/luajit.toml b/recipes/luajit.toml
new file mode 100644
index 00000000..dd4309ce
--- /dev/null
+++ b/recipes/luajit.toml
@@ -0,0 +1,89 @@
+# LuaJIT 2.1 (rolling) — el TERCER intérprete Lua del catálogo, y hay razón para cada uno.
+#
+# ══ POR QUÉ TRES LUA ═══════════════════════════════════════════════════════════════════════════
+# `lua` 5.4.8 entró para wireplumber (su política está escrita en Lua). `lua5.2` entró para el OSC
+# de mpv, que sólo acepta 5.1/5.2/LuaJIT. Ésta entra porque hay consumidores que piden **LuaJIT por
+# nombre y no aceptan otra cosa**: `swayimg` ≥5.0 hace `dependency('luajit')` sin alternativa, y mpv
+# la prefiere sobre 5.2 (es la implementación que upstream prueba). No son la misma ABI ni de lejos:
+# LuaJIT es Lua 5.1 + FFI + JIT.
+#
+# ══ CÓMO CONVIVE SIN PISAR A LAS OTRAS DOS ═════════════════════════════════════════════════════
+# Sus rutas ya vienen versionadas de fábrica y no chocan con ninguna de las otras dos:
+# headers en `/usr/include/luajit-2.1/`, `libluajit-5.1.so.2`, `luajit.pc`, y el binario
+# `/usr/bin/luajit-2.1.<relver>` con el symlink `/usr/bin/luajit`. Ningún fichero se llama `lua`.
+#
+# ══ ⚠ EL PIN DE VERSIÓN NO ALCANZA: LuaJIT SE VERSIONA SOLO, DESDE `git` ═══════════════════════
+# Desde que 2.1 pasó a «rolling» no hay tags: la versión la saca del árbol. `src/Makefile:495` hace
+#
+# [ -e ../.git ] && git show -s --format=%ct >luajit_relver.txt || cat ../.relver >...
+#
+# y `src/host/genversion.lua` sustituye ese número dentro de `luajit_rolling.h`. **Eso no es
+# cosmético: entra en un SÍMBOLO EXPORTADO** — `LUAJIT_VERSION_SYM` es `luaJIT_version_2_1_ROLLING`
+# con ROLLING reemplazado. O sea que dos builds del mismo commit con distinto estado de `.git`
+# publican símbolos con nombres distintos, y un consumidor compilado contra un header y ligado
+# contra la otra librería muere con símbolo indefinido. Peor que un hash que no reproduce: rompe.
+#
+# ARREGLO, y por eso son DOS pasos y no uno: se escribe `.relver` a mano con el `%ct` del commit
+# pineado (1787165859 = 2026-08-19T18:57:39Z) **y** se borra `.git`, porque la rama del `&&` que
+# consulta git GANA si el directorio existe. Con `.relver` sólo, un fetch que dejara `.git` volvería
+# a mandar. Misma familia que el `-Dbuild-date` de mpv y el `-Dversion` de swayimg — la perilla que
+# hay que buscar en toda app nueva, con el agravante de que acá afecta al enlazado.
+#
+# ══ TARGET_STRIP=true, A PROPÓSITO ═════════════════════════════════════════════════════════════
+# El Makefile corre `$(CROSS)strip` sobre el binario y la `.so` como parte de la COMPILACIÓN
+# (src/Makefile:749,754). Dos problemas: `strip` es de binutils y el lab no lo trae salvo que la
+# receta lo pida, y además borra el `.debug_*` antes de que hammer pueda partirlo (SDD 23). Se
+# neutraliza con el no-op `true` en vez de sumar una dep para destruir información.
+#
+# ══ `-Wl,-E` EN Libs.private, QUE ES UNA TRAMPA CONOCIDA DE ESTE REPO ══════════════════════════
+# LuaJIT liga su binario con `-Wl,-E` (--export-dynamic) para que el FFI vea los símbolos del
+# ejecutable, y lo propaga en `Libs.private` de su `.pc`. El wrapper de zig de este repo tiene una
+# regla que QUITA `-static` cuando ve `--export-dynamic`: un consumidor que pida luajit con
+# `prefer_static` va a dejar de ser estático sin avisar. No es un bug de esta receta —es el
+# comportamiento correcto, `-E` y `-static` no se llevan— pero es exactamente el tipo de cosa que
+# se descubre seis meses después. Queda escrita acá.
+#
+# ══ `LIBS=-lunwind`: LA MISMA DEUDA DEL UNWINDER QUE YA PAGA librsvg ═══════════════════════════
+# Sin esto el link muere con NUEVE símbolos indefinidos —`_Unwind_RaiseException`, `_Unwind_SetGR`,
+# `__register_frame`…— todos desde `lj_err.o`. **No es un fallo de LuaJIT ni del pin**: es que su
+# manejo de errores usa unwinding DWARF de verdad (`LUAJIT_UNWIND_EXTERNAL`, que su propio Makefile
+# se activa solo al detectar que el compilador emite `.eh_frame`), y en glibc ese runtime vive en
+# `libgcc_s`, que musl no tiene. zig empaqueta la libunwind de LLVM y exporta esos símbolos.
+# Va por `LIBS=` y no por `LDFLAGS=` porque `TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS)`
+# se expande DESPUÉS de los objetos en la línea de link (src/Makefile:748,753), que es donde el
+# linker resuelve; en `LDFLAGS` iría antes y no resolvería nada. Cubre las dos ligaduras, la del
+# ejecutable y la de la `.so`.
+#
+# Sin CROSS: el sandbox es x86_64 y el target también, así que HOST_CC = TARGET_CC y no hace falta
+# el modo cross de LuaJIT (que necesitaría un segundo compilador para `minilua`/`buildvm`).
+name = "luajit"
+version = "2.1.1787165859"
+license = "MIT"
+
+[source]
+repo = "https://github.com/LuaJIT/LuaJIT.git"
+commit = "1ee778a4e37122d8ca7d5733c590a47dafd6b15c"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "dynamic"
+
+[build.phases]
+configure = '''
+set -e
+# Ver la cabecera: los DOS pasos hacen falta, `.relver` solo no alcanza si `.git` sobrevive al fetch.
+rm -rf .git
+echo 1787165859 > .relver
+'''
+compile = '''
+set -e
+make PREFIX=/usr CC="hammer-zig-cc" TARGET_STRIP=true LIBS=-lunwind
+'''
+install = '''
+set -e
+make install PREFIX=/usr DESTDIR=/out CC="hammer-zig-cc" TARGET_STRIP=true LIBS=-lunwind
+'''
+
+[deps]
+build = ["make", "pkgconf"]