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…
libime: fuente git, porque kenlm es un SUBMÓDULO
1# libime 1.1.17 — el motor de conversión pinyin→hanzi. La pieza que faltaba para escribir chino.
2#
3# ══ QUÉ ES ═════════════════════════════════════════════════════════════════════════════════════
4# El chino se escribe tecleando la pronunciación en pinyin y eligiendo entre los caracteres que
5# suenan igual. Con decenas de miles de hanzi eso no es una tabla: es un modelo de lenguaje que
6# ordena los candidatos por probabilidad en contexto. libime es ese motor (un kenlm recortado más
7# los índices de pinyin y de tabla), y `fcitx5-chinese-addons` es su cara.
8#
9# ══ ⚠ `ENABLE_DATA=Off`, Y NO ES UN RECORTE: ES QUE LOS DATOS SE DESCARGAN ═════════════════════
10# `data/CMakeLists.txt` hace **dos descargas en tiempo de build** desde el servidor de fcitx:
11# lm_sc.arpa-20260629.tar.zst 75 MB → el modelo de lenguaje
12# dict-20260907.tar.zst → el diccionario de pinyin
13# y el sandbox de takana no tiene red, a propósito: una fuente que se baja al construir no es
14# reproducible ni auditable. ⇒ los datos salen a **recetas hermanas** (`libime-lm`,
15# `libime-dict`) con su tarball pineado por sha256, que es el modelo del repo — y upstream hasta
16# nos da los sha en ese mismo CMakeLists, así que no hay que confiar en nadie para pinearlos.
17#
18# ⚠ **Por eso `ENABLE_TOOLS=On` es obligatorio acá y no adorno.** Las recetas de datos necesitan
19# los ejecutables de ESTA para convertir el `.arpa` y los `.txt` en los binarios que el motor lee.
20# Si esta receta se construyera sin herramientas, las otras no tendrían con qué trabajar.
21#
22# ⚠ **SON CUATRO, Y MI GUARDIÁN PEDÍA TRES — corregido 2026-10-20.** Había escrito el control contra
23# las herramientas que iban a usar `libime-lm` y `libime-dict` (`libime_slm_build_binary`,
24# `libime_prediction`, `libime_pinyindict`) sin leer el `data/CMakeLists.txt` completo de upstream,
25# que usa también **`LibIME::tabledict`** para los ocho diccionarios de los métodos por TABLA. Es la
26# misma clase de defecto que los cuatro guardianes demasiado estrictos del mismo día, con el signo
27# invertido: **escribí el guardián contra lo que yo pensaba usar, no contra lo que el paquete
28# promete.** Un guardián que pide menos de lo que hace falta no rechaza nada — deja pasar el hueco
29# hasta que una receta de datos dos pasos más abajo no encuentra su binario.
30#
31# ⇒ Y hasta que esas dos existan, **libime NO se declara en ningún perfil**: un motor de conversión
32# sin modelo de lenguaje carga y no convierte nada. Es la figura de `crun` sin podman, la misma por
33# la que `fcitx5` esperó un día a tener un motor.
34#
35# ══ LO QUE PIDE, MEDIDO EN SU CMakeLists ═══════════════════════════════════════════════════════
36# ECM · PkgConfig · libzstd · Fcitx5Utils · **Boost 1.61 COMPONENTS iostreams**
37# El último obligó a cambiar `boost`, que en este corpus era headers-only desde hacía un año: ahora
38# compila `libboost_iostreams.a` y está promovido al corpus (radio 1 verificado, `kactivitymanagerd`
39# con el hash intacto).
40#
41# `BUILD_SHARED_LIBS` se deja en On (su default): `fcitx5-chinese-addons` es un addon `.so` y
42# enlazarlo contra una libime estática no-PIC sería el muro del 19-oct otra vez.
43name = "libime"
44version = "1.1.17"
45license = "LGPL-2.1-or-later"
46
47# ⚠ **FUENTE GIT Y NO TARBALL, Y ES OBLIGATORIO: `kenlm` ES UN SUBMÓDULO.**
48# El modelo de lenguaje de libime no es código propio — es un kenlm recortado que entra por
49# submódulo (`.gitmodules`: `src/libime/core/kenlm` ← `https://github.com/kpu/kenlm.git`). Un
50# archive de GitHub **no materializa submódulos**, así que con el tarball el árbol llegaba con ese
51# directorio VACÍO y cmake cortaba así:
52#
53# CMake Error at src/libime/core/CMakeLists.txt:12 (add_library):
54# No SOURCES given to target: kenlm
55# CMake Error at tools/CMakeLists.txt:1 (add_executable):
56# No SOURCES given to target: libime_slm_build_binary
57#
58# O sea: la biblioteca Y las herramientas de datos, las dos por el mismo hueco.
59#
60# ⇒ `takana` **sí** materializa submódulos, pero sólo con fuente `repo`+`commit`: lee `.gitmodules`
61# y los SHA de gitlink y archiva cada uno en su subruta (`fetch::materializar_submodulos`). Con
62# `tarball` no hay nada que leer. Es la MISMA trampa que `yoga` en fcitx5, resuelta del otro lado:
63# allá el submódulo es de un tercero con su propio release, así que se pineó su tarball en una
64# receta aparte; acá kenlm se compila DENTRO de libime (`add_library(kenlm …)` es de libime), así
65# que no hay receta que sacar — hay que traer el árbol completo.
66#
67# ⚠ **Y yo tenía el dato y lo leí mal.** Midiendo el pico de RAM de la campaña corrí
68# `ls …/libime-1.1.17/src/libime/core/kenlm/lm/*.cc | wc -l`, salió **0**, y lo interpreté como
69# «me equivoqué de ruta» en vez de «el directorio está vacío porque es un gitlink». El cero era la
70# respuesta correcta a la pregunta que no me hice. Es el defecto que este repo tiene documentado
71# como *leí hasta donde me daba la razón*, y costó una vuelta entera de la cadena.
72#
73# El commit es el del tag `1.1.17` (`git/refs/tags/1.1.17` ⇒ `50de2779…`), no una rama: la versión
74# de arriba y esto nombran lo mismo.
75[source]
76repo = "https://github.com/fcitx/libime"
77commit = "50de277992132ff59a6da64c88318f2a51a08f3d"
78
79[build]
80compiler = "zig-cc"
81target = "x86_64-linux-musl"
82link = "dynamic"
83flags = []
84
85[build.phases]
86configure = '''
87ZW="$PWD/.zwrap"; mkdir -p "$ZW"
88# El envoltorio reescribe `--target`: es el patrón canónico del repo (30 recetas lo traen) y sin él
89# zig rechaza los triples que algunos builds inventan («UnknownOperatingSystem»).
90printf '%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"
91printf '%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"
92chmod +x "$ZW/cc" "$ZW/cxx"
93rm -rf build
94cmake -B build -G Ninja -Wno-dev \
95 -DCMAKE_C_COMPILER="$ZW/cc" -DCMAKE_CXX_COMPILER="$ZW/cxx" \
96 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
97 -DPKG_CONFIG_ARGN=--static \
98 -DBoost_USE_STATIC_LIBS=ON -DBoost_USE_STATIC_RUNTIME=ON \
99 -DENABLE_DATA=Off -DENABLE_TOOLS=On -DENABLE_TEST=Off -DENABLE_DOC=Off
100'''
101compile = "cmake --build build"
102# El guardián pide las DOS cosas por las que esta receta existe: las bibliotecas y las TRES
103# herramientas. Sin las herramientas, `libime-lm` y `libime-dict` no pueden construirse — y eso se
104# descubriría dos recetas más tarde, con el artefacto ya sellado y pareciendo correcto.
105install = '''
106set -e
107DESTDIR=/out cmake --install build
108n=$(find /out/usr/lib -name 'libIME*.so*' 2>/dev/null | wc -l)
109[ "$n" -gt 0 ] || { echo "libime: no quedó ninguna libIME*.so — NO sello" >&2; ls -R /out/usr/lib 2>/dev/null | head -20 >&2; exit 1; }
110falta=""
111for t in libime_slm_build_binary libime_prediction libime_pinyindict libime_tabledict; do
112 [ -x "/out/usr/bin/$t" ] || falta="$falta $t"
113done
114if [ -n "$falta" ]; then
115 echo "libime: faltan herramientas:$falta — sin ellas las recetas de datos no pueden construirse" >&2
116 ls /out/usr/bin 2>/dev/null >&2
117 exit 1
118fi
119echo "libime: $n biblioteca(s) + las 4 herramientas de datos"
120'''
121
122[deps]
123build = ["cmake", "samurai", "extra-cmake-modules", "pkgconf", "fcitx5", "boost", "zstd",
124 "gettext-tiny", "glib", "pcre2", "libffi", "expat", "zlib", "dbus"]