# libime-lm — el MODELO DE LENGUAJE del chino. Sin esto, el pinyin no convierte. # # ══ POR QUÉ ES UNA RECETA APARTE Y NO UN `ENABLE_DATA=On` ══════════════════════════════════════ # El `data/CMakeLists.txt` de libime hace **tres descargas en tiempo de build** desde el servidor de # fcitx, y el sandbox de takana no tiene red a propósito: una fuente que se baja al construir no es # reproducible ni auditable. Por eso `recipes/libime.toml` va con `ENABLE_DATA=Off` y los datos # salen a recetas hermanas — ésta, `libime-dict` y `libime-table` — con su tarball pineado. # # Y el `[source]` de una receta admite **UNA sola** fuente (`crates/takana-core/src/recipe.rs`: # repo+commit, tarball+sha256 o dir, excluyentes). Tres tarballs ⇒ tres recetas. No es una # limitación molesta: cada dato se sella por separado y se puede rehacer sin tocar los otros. # # **El sha256 lo declara upstream en su propio CMakeLists**, así que no hay que confiar en el # servidor para pinearlo — se compara contra lo que el proyecto afirma. Comprobado: el fichero que # baja hoy de `download.fcitx-im.org` tiene exactamente ese sha. # # ══ QUÉ HACE: COMPILAR, NO COPIAR ══════════════════════════════════════════════════════════════ # El tarball trae `lm_sc.arpa` — un modelo n-grama en texto, 77 MB comprimidos. De ahí salen DOS # binarios con las herramientas de `libime`, y los comandos son los de upstream, literales: # # libime_slm_build_binary -s -a 22 -q 4 trie lm_sc.arpa sc.lm # libime_prediction sc.lm lm_sc.arpa sc.lm.predict # # Las banderas no se tocan: `-a 22` son los bits de compresión de punteros y `-q 4` los de # cuantización del trie de kenlm, o sea que **cambian el tamaño y la precisión del modelo**. Copiar # los valores de upstream es la única forma de que esto rinda como rinde en cualquier otra distro. # # ⇒ Por eso `libime` necesita `ENABLE_TOOLS=On`: sin esos dos ejecutables esta receta no tiene con # qué trabajar. Su guardián los exige por nombre, precisamente para que el fallo salga allá y no acá. # # ══ DÓNDE CAEN, Y POR QUÉ CON OTRO NOMBRE ══════════════════════════════════════════════════════ # `LIBIME_INSTALL_LIBDATADIR` = `${CMAKE_INSTALL_FULL_LIBDIR}/libime` ⇒ `/usr/lib/libime/`, y # upstream los instala **renombrados** a `zh_CN.lm` y `zh_CN.lm.predict`: el motor los busca por # locale, no por el nombre del fichero de origen. Instalarlos como `sc.lm` daría un artefacto con # contenido que el motor no encuentra — sellado, lleno y mudo. name = "libime-lm" version = "20260629" license = "LGPL-2.1-or-later" [source] tarball = "https://download.fcitx-im.org/data/lm_sc.arpa-20260629.tar.zst" sha256 = "06808333b9173e5374cf2cb5afc12d08f5625bf9abb536489cac376fc05f2e7f" # El tarball deja `lm_sc.arpa` en la RAÍZ, sin directorio contenedor (comprobado con `tar -tf`), así # que no hay ningún componente que recortar. El default de takana es 1, pensado para los releases # GNU; dejarlo recortaría el único fichero que hay. strip_components = 0 [build] compiler = "zig-cc" target = "x86_64-linux-musl" link = "static" flags = [] [build.phases] configure = ''' set -e [ -f lm_sc.arpa ] || { echo "libime-lm: el tarball no dejó lm_sc.arpa en la raíz — ¿cambió strip_components?" >&2; ls -la >&2; exit 1; } echo "libime-lm: lm_sc.arpa = $(stat -c%s lm_sc.arpa) bytes" ''' # Las banderas son las de upstream, literales. `-s` deja fuera los avisos de frontera de frase. compile = ''' set -e libime_slm_build_binary -s -a 22 -q 4 trie lm_sc.arpa sc.lm libime_prediction sc.lm lm_sc.arpa sc.lm.predict ''' # Guardián: los dos ficheros, con un piso de tamaño. Un `.lm` de cero bytes es la figura del # artefacto VACÍO que este repo tiene documentada — se sella, es un cache-hit, y el motor carga sin # convertir nada. El piso no es un número inventado: el `.arpa` de entrada son decenas de MB y el # trie cuantizado de kenlm no baja de unos pocos; 1 MB es holgado y detecta el caso real (fichero # creado y no escrito). install = ''' set -e mkdir -p /out/usr/lib/libime cp sc.lm /out/usr/lib/libime/zh_CN.lm cp sc.lm.predict /out/usr/lib/libime/zh_CN.lm.predict for f in zh_CN.lm zh_CN.lm.predict; do t=/out/usr/lib/libime/$f [ -s "$t" ] || { echo "libime-lm: $f quedó vacío o ausente — NO sello un modelo que no convierte" >&2; exit 1; } n=$(stat -c%s "$t") [ "$n" -gt 1048576 ] || { echo "libime-lm: $f son sólo $n bytes — esperaba >1 MiB; el modelo no se escribió" >&2; exit 1; } done echo "libime-lm: zh_CN.lm $(stat -c%s /out/usr/lib/libime/zh_CN.lm) B + zh_CN.lm.predict $(stat -c%s /out/usr/lib/libime/zh_CN.lm.predict) B" ''' [deps] # ⚠ **`fcitx5` y `zstd` NO son opcionales acá, y el fallo NO dice que falten.** Las herramientas de # libime son binarios DINÁMICOS: `libime_pinyindict` declara # `NEEDED libIMEPinyin.so.0 libIMECore.so.0 libFcitx5Utils.so.2`. El sandbox sólo monta lo # declarado, así que con `libime` a secas el cargador no encuentra `libFcitx5Utils.so.2` y la fase # muere con **`exit 127`** — que se lee como «no existe el ejecutable» y es «no existe una de sus # bibliotecas». Cuesta un rato porque el ejecutable SÍ está. # # La lista no se adivinó: se recorrió el `NEEDED` recursivamente desde los tres binarios contra los # artefactos del store. Clausura completa = `libime` + `fcitx5` + `zstd`, cero sonames huérfanos. # Es la misma figura que la cadena de tres saltos de `espeak-ng`, con una diferencia: allá el `.a` # del medio CORTABA la propagación; acá todo es `.so` y propaga, pero **declarar sigue siendo # manual** porque el sandbox es hermético a propósito. build = ["libime", "fcitx5", "zstd"]