gioser

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 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ main recipes/fcitx5-chinese-addons.toml TOML · 195 siq'ikuna · 12.9 KB Ñawpa willay ch'uya

chino: la distro escribe los TRES grandes, y sin tocar nada de GNOME 1bf4a9406 · Sergio ·

1# fcitx5-chinese-addons 5.1.13 — el TERCER método de entrada. Con esto se escribe chino.
2#
3# ══ LA VERSIÓN ES EL RESULTADO DE UNA MEDICIÓN, NO LA ÚLTIMA ═══════════════════════════════════
4# Upstream va por **5.1.15**, y su `CMakeLists.txt` fija `REQUIRED_FCITX_VERSION 5.1.22`. Pero
5# fcitx5 ≥5.1.22 exige `librsvg-2.0`, que sólo existe en `incoming-gnome` enredado con la pila de
6# introspección de esa cola, y promoverla cuesta **35 recetas de GNOME rearmando** (medido
7# promoviendo y revirtiendo de verdad; la nota larga está en `recipes/fcitx5.toml`).
8#
9# Entonces se buscó hacia atrás, tag por tag, en vez de dar el techo por bueno:
10#
11#     addons    pide fcitx5   pide libime
12#     5.1.15      5.1.22        1.1.14
13#     5.1.14      5.1.22        1.1.14
14#     5.1.13      **5.1.20**    1.1.14      ← acá
15#     5.1.12      5.1.13        1.1.13
16#
17# Y `librsvg` entró en fcitx5 **5.1.22**, así que `5.1.21` lo cumple sin pedirla. Nuestro `libime`
18# es 1.1.17 ≥ 1.1.14. **Dos rangos que se solapan en un punto**, y ese punto da el chino sin tocar
19# ni una receta de GNOME. La lección de método: *un techo de versión se mide en los dos extremos
20# antes de aceptarlo como coste.*
21#
22# ══ ⚠ EL PARCHE DE `pinyinhelper`: DOS DESCARGAS SIN PERILLA ════════════════════════════════════
23# `modules/pinyinhelper/CMakeLists.txt` baja dos tarballs con `fcitx5_download`, y el módulo entra
24# **incondicionalmente** desde `modules/CMakeLists.txt` — no hay `option()` que lo evite. El sandbox
25# no tiene red, así que eso mata el build.
26#
27# Lo que decide la forma del parche es **cuándo** se usa el dato, y se comprobó en la fuente:
28# `stroke.cpp:47` y `pinyinlookup.cpp:138` lo buscan como `"pinyinhelper/py_stroke.mb"` por la vía
29# de `StandardPath` — o sea **en EJECUCIÓN, no al compilar**. ⇒ el addon no necesita el fichero para
30# construirse: le basta encontrarlo en la imagen.
31#
32# ⇒ Las descargas, las extracciones y los `install(FILES …)` de esos dos `.mb` se borran del
33# CMakeLists, y los datos llegan por `recipes/fcitx5-pinyin-stroke.toml` y
34# `fcitx5-pinyin-table.toml`, declaradas en `[deps] runtime`. Si en vez de eso hubiera dejado el
35# `install`, el mismo fichero viajaría en DOS artefactos a la misma ruta.
36#
37# Cada `sed` se COMPRUEBA antes: si upstream mueve una de esas líneas, el configure falla diciendo
38# cuál, en vez de volver a intentar una descarga que no puede funcionar.
39#
40# ══ LAS PERILLAS, Y QUÉ SE PIERDE CON CADA UNA ═════════════════════════════════════════════════
41#   ENABLE_OPENCC=On       simplificado ↔ tradicional. **Encendida**: `recipes/opencc.toml` es
42#                          autocontenida (vendoriza sus 4 deps) así que el tradicional sale por una
43#                          sola receta. Apagarla dejaría la mitad del chino escrito afuera.
44#   ENABLE_DATA=On         compila `chaizi.txt`, que viene EN el árbol — no baja nada. Es el
45#                          diccionario de descomposición de caracteres.
46#   ENABLE_TOOLS=On        `scel2org5`: convierte diccionarios de Sogou a texto. Pequeño, sin deps
47#                          nuevas, y es cómo alguien importa su propio vocabulario.
48#   ENABLE_GUI=Off         la ventana de configuración, que es Qt (`Qt Concurrent` +
49#                          `Fcitx5QtWidgetsAddons`). No tenemos `fcitx5-qt` en el corpus; la
50#                          configuración se edita por fichero. **Esto es una pérdida real y queda
51#                          anotada**, no disimulada: el usuario no tiene diálogo para elegir
52#                          disposición de teclado ni diccionarios.
53#   ENABLE_BROWSER=Off     necesita `QtWebEngineWidgets`, que es Chromium entero. No.
54#   ENABLE_CLOUDPINYIN=Off **decisión, no falta de dep** (sólo pedía `libcurl`, que está): el
55#                          «pinyin en la nube» manda lo que teclea el usuario a un servidor remoto
56#                          para mejorar los candidatos. En una distro que se construye entera desde
57#                          fuente para ser auditable, eso no se enciende por defecto.
58#   ENABLE_TEST=Off        su `option()` viene en On y no se corre la suite acá.
59#
60# `Boost 1.61 OPTIONAL_COMPONENTS json`: nuestro boost compila `iostreams` y no `json`. Es
61# OPCIONAL —`if (TARGET Boost::json)`— así que no falla; lo que se pierde es una ruta de importación
62# de diccionarios en formato json.
63name    = "fcitx5-chinese-addons"
64version = "5.1.13"
65license = "GPL-2.0-or-later"
66
67[source]
68tarball = "https://github.com/fcitx/fcitx5-chinese-addons/archive/refs/tags/5.1.13/fcitx5-chinese-addons-5.1.13.tar.gz"
69sha256  = "ee056e7ac31f5f9c242cee43f7ad8605e2cc188a7bbcc46f869332a49058244a"
70
71[build]
72compiler = "zig-cc"
73target   = "x86_64-linux-musl"
74link     = "dynamic"
75flags    = []
76
77[build.phases]
78configure = '''
79set -e
80ZW="$PWD/.zwrap"; mkdir -p "$ZW"
81printf '#!/bin/sh\nexec /opt/zig/zig cc -mcpu=baseline "$@"\n'  > "$ZW/cc"
82printf '#!/bin/sh\nexec /opt/zig/zig c++ -mcpu=baseline "$@"\n' > "$ZW/cxx"
83chmod +x "$ZW/cc" "$ZW/cxx"
84
85# ── El parche de pinyinhelper. Se comprueba ANTES de aplicar, por fichero y por marcador.
86PH=modules/pinyinhelper/CMakeLists.txt
87[ -f "$PH" ] || { echo "fcitx5-chinese-addons: no existe $PH — upstream reorganizó el árbol" >&2; exit 1; }
88for marca in fcitx5_download fcitx5_extract; do
89  grep -q "$marca" "$PH" || { echo "fcitx5-chinese-addons: $PH ya no usa $marca — el parche de red sobra o cambió de forma; REVISAR antes de seguir" >&2; exit 1; }
90done
91# Las llamadas ocupan varias líneas (abren paréntesis y lo cierran más abajo), así que se borran por
92# RANGO desde la llamada hasta su línea de cierre `)`, no por línea suelta.
93python3 - "$PH" <<'PYEOF'
94import sys, re
95p = sys.argv[1]
96s = open(p, encoding='utf-8').read()
97# Fuera download/extract (bajan por red) y los install de los .mb (los entregan las recetas de datos)
98# ⚠ Los `set(PY_*_URL …)` TAMBIÉN se van, y eso lo descubrió el propio control de salida de esta
99# receta: quité las llamadas y dejé las URLs, así que el `grep` final denunció que el fichero
100# «todavía referencia la red tras el parche» y abortó. Hizo exactamente lo que tenía que hacer —
101# un parche a medias habría sellado un artefacto que no baja nada por casualidad, no por diseño.
102patrones = [
103    r'fcitx5_download\([^)]*\)\n',
104    r'fcitx5_extract\([^)]*\)\n',
105    r'set\(PY_(?:STROKE|TABLE)_URL\s*[^)]*\)\n',
106    r'install\(FILES "\$\{PY_(?:STROKE|TABLE)_TGT\}"[^)]*\)\n',
107]
108for pat in patrones:
109    s, n = re.subn(pat, '', s)
110    if n == 0:
111        sys.exit(f"fcitx5-chinese-addons: el patrón {pat!r} no coincidió con nada en {p}")
112open(p, 'w', encoding='utf-8').write(s)
113PYEOF
114# Comprobación de salida: que no quede NI UNA referencia a la red en ese fichero.
115grep -qE 'fcitx5_download|fcitx5_extract|download\.fcitx-im\.org' "$PH" && { echo "fcitx5-chinese-addons: $PH todavía referencia la red tras el parche" >&2; grep -nE 'fcitx5_download|fcitx5_extract|download' "$PH" >&2; exit 1; }
116echo "fcitx5-chinese-addons: pinyinhelper sin descargas"
117
118# ⚠ **PARCHE 2: una libime ESTÁTICA exige que el consumidor provea su interfaz de enlace.**
119# `recipes/libime.toml` construye con `BUILD_SHARED_LIBS=Off` (por el segfault de las cuatro copias
120# de libc++; la nota larga está allá). Consecuencia directa: en una biblioteca estática cmake NO
121# resuelve las deps, así que **incluso las `PRIVATE` quedan en la interfaz exportada** como
122# `$<LINK_ONLY:…>` — y los `*Targets.cmake` instalados nombran targets importados que ESTA receta
123# tiene que definir antes de buscarlos:
124#
125#     CMake Error at /usr/lib/cmake/LibIMECore/LibIMECoreTargets.cmake:70 (set_target_properties):
126#       The link interface of target "LibIME::kenlm" contains: PkgConfig::ZSTD
127#       but the target was not found.
128#
129# libime nombra `PkgConfig::ZSTD` en cuatro targets (`kenlm`, `IMECore`, `IMEPinyin`, `IMETable`) y
130# `Boost::iostreams` en tres. Con `.so` nada de eso salía al exterior; con `.a` sí.
131#
132# ⇒ Se parchea ACÁ y no en libime a propósito: que una estática pida su interfaz es correcto, no un
133# defecto. Lo que faltaba es que el consumidor la declare. Dos cambios, cada uno comprobado:
134#   · `pkg_check_modules(ZSTD … IMPORTED_TARGET libzstd)` ANTES del `find_package(LibIMEPinyin…)`
135#   · el `find_package(Boost …)` ya existente pasa a pedir el componente `iostreams`
136CA=CMakeLists.txt
137grep -q 'find_package(LibIMEPinyin' "$CA" || { echo "fcitx5-chinese-addons: no encuentro find_package(LibIMEPinyin …) donde insertar el target de zstd" >&2; exit 1; }
138sed -i 's|^find_package(LibIMEPinyin|pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET "libzstd")\nfind_package(LibIMEPinyin|' "$CA"
139grep -q 'pkg_check_modules(ZSTD REQUIRED IMPORTED_TARGET' "$CA" || { echo "fcitx5-chinese-addons: el parche del target PkgConfig::ZSTD no se aplicó" >&2; exit 1; }
140
141grep -q 'find_package(Boost 1.61 OPTIONAL_COMPONENTS json)' "$CA" || { echo "fcitx5-chinese-addons: el find_package(Boost …) no tiene la forma esperada; REVISAR" >&2; grep -n 'find_package(Boost' "$CA" >&2; exit 1; }
142sed -i 's|find_package(Boost 1.61 OPTIONAL_COMPONENTS json)|find_package(Boost 1.61 REQUIRED CONFIG COMPONENTS iostreams OPTIONAL_COMPONENTS json)|' "$CA"
143grep -q 'COMPONENTS iostreams' "$CA" || { echo "fcitx5-chinese-addons: el parche del componente iostreams no se aplicó" >&2; exit 1; }
144echo "fcitx5-chinese-addons: interfaz de enlace de la libime estática declarada (zstd + boost iostreams)"
145
146rm -rf build
147cmake -B build -G Ninja -Wno-dev \
148  -DCMAKE_C_COMPILER="$ZW/cc" -DCMAKE_CXX_COMPILER="$ZW/cxx" \
149  -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
150  -DCMAKE_INSTALL_LIBDIR=lib \
151  -DPKG_CONFIG_ARGN=--static \
152  -DBoost_USE_STATIC_LIBS=ON -DBoost_USE_STATIC_RUNTIME=ON \
153  -DENABLE_OPENCC=On -DENABLE_DATA=On -DENABLE_TOOLS=On \
154  -DENABLE_GUI=Off -DENABLE_BROWSER=Off -DENABLE_CLOUDPINYIN=Off \
155  -DENABLE_TEST=Off
156'''
157compile = "cmake --build build"
158# Guardián por SUBCADENA y no por nombre exacto: la convención de fcitx5 es `lib<nombre>.so`, y
159# pedir `pinyin.so` daría un falso negativo que rechaza un build bueno — pasó con `fcitx5-hangul` y
160# costó una vuelta entera.
161#
162# Se exigen los DOS métodos de entrada (`pinyin` y `table`) y sus `.conf`, que es lo que hace que el
163# daemon sepa que existen. Un addon sin su `.conf` en `share/fcitx5/addon/` está en el disco y es
164# invisible: el daemon no lo carga nunca y no hay ningún error.
165install = '''
166set -e
167DESTDIR=/out cmake --install build
168for m in pinyin table pinyinhelper chttrans punctuation fullwidth; do
169  so=$(find /out/usr/lib/fcitx5 -name "*$m*.so" 2>/dev/null | head -1)
170  [ -n "$so" ] || { echo "fcitx5-chinese-addons: no hay ningún *$m*.so en /usr/lib/fcitx5" >&2; ls -R /out/usr/lib/fcitx5 2>/dev/null | head -30 >&2; exit 1; }
171  [ -f "/out/usr/share/fcitx5/addon/$m.conf" ] || { echo "fcitx5-chinese-addons: falta share/fcitx5/addon/$m.conf — el addon existiría en disco y el daemon no lo cargaría nunca" >&2; ls /out/usr/share/fcitx5/addon 2>/dev/null >&2; exit 1; }
172done
173# `chttrans` es el que depende de opencc. Que su `.so` enlace la compartida es lo que separa «el
174# addon está» de «el addon convierte»: sin ese NEEDED, cargaría y devolvería el texto sin tocar.
175ch=$(find /out/usr/lib/fcitx5 -name '*chttrans*.so' | head -1)
176readelf -d "$ch" 2>/dev/null | grep -q 'libopencc' || { echo "fcitx5-chinese-addons: chttrans no enlaza libopencc — el tradicional NO funcionaría y no habría error" >&2; readelf -d "$ch" 2>/dev/null | grep NEEDED >&2; exit 1; }
177# El diccionario de descomposición que produce ENABLE_DATA.
178[ -s /out/usr/share/fcitx5/pinyin/chaizi.dict ] || { echo "fcitx5-chinese-addons: falta o está vacío share/fcitx5/pinyin/chaizi.dict — lo produce ENABLE_DATA con libime_pinyindict" >&2; exit 1; }
179n=$(find /out/usr/lib/fcitx5 -name '*.so' | wc -l)
180echo "fcitx5-chinese-addons: $n addon(s), chttrans enlazado a opencc, chaizi.dict presente"
181'''
182
183[deps]
184build = ["cmake", "samurai", "extra-cmake-modules", "pkgconf", "fcitx5", "libime", "boost",
185         "opencc", "zstd", "gettext-tiny", "libxkbcommon", "wayland", "wayland-protocols",
186         # `python3`: lo usa el PARCHE de pinyinhelper de la fase configure, no el build de upstream.
187         # Sin declararlo la fase muere con exit 127 y el error no nombra a python.
188         "python3",
189         "glib", "pcre2", "libffi", "expat", "zlib", "dbus", "binutils"]
190# ⚠ Datos que el addon busca EN EJECUCIÓN, no al construir. Van en `runtime` precisamente por eso:
191# no entran en el hash del build —`runtime` no re-hashea— pero sí en la clausura de la imagen, que
192# es lo que hace que los ficheros estén cuando el daemon los busque. Sin esto, los addons cargan y
193# no encuentran sus tablas: ni trazos, ni pronunciación, ni conversión, ni candidatos.
194runtime = ["libime-lm", "libime-dict", "libime-table",
195           "fcitx5-pinyin-stroke", "fcitx5-pinyin-table", "opencc"]