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/waterfox.toml TOML · 341 siq'ikuna · 21.9 KB Ñawpa willay ch'uya

takana etapa 5a: los comentarios de las 741 recetas e852f4849 · Sergio ·

1# Waterfox 6.7.1.1 (Gecko 153.1.0) — el PRIMER FORK, y la prueba de la tesis del reúso.
2#
3# ══ QUÉ HEREDA, QUE ES CASI TODO ═══════════════════════════════════════════════════════════════
4# Waterfox es un fork de Gecko con árbol completo propio, así que consume EXACTAMENTE la misma
5# plataforma que Firefox —gtk3, nodejs, clang18, cbindgen, y las variantes `-shared` del corpus— y
6# los MISMOS seis muros ya resueltos. Ninguno se vuelve a pagar:
7#   1. sin `--enable-linker`     el sondeo de Mozilla no reconoce a zig (y con gcc ya no aplica).
8#   2. `compiler = "gcc"`        `flags.configure` EXIGE un `NEEDED …libc++`; zig enlaza libc++
9#                                estática para musl. Es negativa de upstream, no una perilla.
10#   3. `RUST_TARGET`             contrato del parche `fix-rust-target` de Alpine, no un bug.
11#   4. checksums vendorizados    cargo verifica cada fichero; los parches de musl los rompen.
12#   5. `strip_debug`             sin él el artefacto se va a varios GB.
13#   6. hermético                 `--disable-bootstrap` + `MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE`.
14# **Las diferencias reales con `recipes/firefox.toml` son TRES: la fuente, el branding y la versión.**
15# Si aparece una cuarta, conviene preguntarse si no debería estar en las dos.
16#
17# ══ LA BASE ES 153.1.0 Y LOS PARCHES SON DE 154 ════════════════════════════════════════════════
18# `config/milestone.txt` de este tag dice 153.1.0: una release POR DEBAJO de la de Firefox. Los once
19# parches de musl se reutilizan igual porque el código que tocan (LFS64, time64, prctl, el sandbox)
20# se mueve poco entre releases — pero es una apuesta explícita, no un hecho: si `patch` falla, falla
21# TEMPRANO, antes de compilar nada, y el arreglo es rebasar el parche que se queje.
22#
23# ══ ACÁ SÍ VA BRANDING OFICIAL, Y NO ES INCOHERENCIA ═══════════════════════════════════════════
24# `recipes/firefox.toml` va con branding SIN MARCA a propósito, porque poner el nombre y el logo de
25# Firefox sobre un binario parcheado entra en la política de marcas de Mozilla (la historia de
26# Iceweasel). Acá es al revés: `browser/branding/official` es **la marca del propio Waterfox**, que
27# BrowserWorks distribuye en su árbol para que se construya así. Usar su branding es lo que quieren;
28# lo que no se puede es usar el de Mozilla.
29
30# ══ APARCADA 2026-09-05 — Y NO POR LOS PARCHES ═════════════════════════════════════════════════
31# Primer intento en el worker: murió en CUATRO SEGUNDOS, no en las cuatro horas que temíamos, y por
32# algo que este comentario no preveía:
33#
34#     FATAL ERROR PROCESSING MOZBUILD FILE  /src/waterfox/browser/moz.build
35#     The underlying problem is we referenced a path that does not exist. That path is:
36#     /src/waterfox/browser/locales/moz.build
37#
38# O sea: los once parches de musl **sí agarraron** —la apuesta de reusar los de 154 sobre un árbol
39# 153.1.0 no es lo que falló— y lo que falta es un TROZO DEL ÁRBOL. `browser/locales` no está en el
40# clon. Sospechoso número uno: el `--filter=blob:none` + cómo BrowserWorks compone su repo (locales
41# como submódulo o generado), no la receta.
42#
43# Se aparca a propósito: la prioridad pasó a `firefox` con la cadena de optimización y a `atuq`
44# (SDD 26). Cuando se retome, el primer paso es mirar si `browser/locales` existe en el commit
45# pineado — un minuto de `git ls-tree`, no un build.
46# ══ ⚠ TRES COSAS MEDIDAS EN EL ARTEFACTO — SON DECISIÓN, NO BUG ═══════════════════════════════
47#
48# 0) ⚠ SALE A LA RED AL ARRANCAR, SOLO. Visto CORRIENDO el artefacto (2026-09-08), no leyendo:
49#
50#      [WaterfoxBlocker] Failed to update list: https://easylist.to/easylist/easylist.txt
51#      [WaterfoxBlocker] Failed to update list: https://easylist.to/easylist/easyprivacy.txt
52#
53#    Waterfox trae su propio bloqueador y **descarga sus listas al primer arranque, sin que nadie se
54#    lo pida**. Acá falla porque el sandbox no tiene red; en una imagen real sería una conexión no
55#    solicitada. Esta distro apagó la telemetría de firefox exactamente por esto, así que la
56#    coherencia pide decidirlo — no apagarlo de paso. Ninguna inspección estática lo habría mostrado.
57#
58# Del `application.ini` del artefacto `b3:e65c0221` (2026-09-06), leído del sellado, no de la doc:
59#
60#     Vendor=BrowserWorks      Name=Firefox      RemotingName=firefox-default
61#     Profile=Waterfox         ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
62#
63# 1) EL BRANDING «OFICIAL» DE ESTE COMMIT ES EL DE MOZILLA, NO EL DE WATERFOX. El comentario de
64#    más abajo dice que `browser/branding/official` es «la marca del propio Waterfox»; en ESTE
65#    commit no lo es. Comprobado en el árbol: `branding/official/configure.sh` pone
66#    `MOZ_APP_DISPLAYNAME=Firefox`, y `brand.ftl` da `-brand-short-name = Firefox` y
67#    `-brand-full-name = Mozilla Firefox`. El `ID` es además el ID canónico de Firefox. O sea que
68#    hoy esto sella un binario parcheado que se presenta como «Mozilla Firefox» — justo lo que
69#    `firefox.toml` evita a propósito yendo SIN marca (la historia de Iceweasel). La salida
70#    probable es `unofficial`, pero es una decisión de política de marcas, no una perilla técnica,
71#    y por eso se deja escrita en vez de cambiada.
72#
73# 2) ✅ RESUELTA 2026-09-08 — la colisión de rutas con `firefox`. El artefacto instalaba en
74#    `usr/lib/firefox/` y `usr/bin/firefox`, las MISMAS rutas que la receta `firefox`. Se renombra
75#    a `waterfox` en el install y se le pone lanzador: ver el bloque del install para por qué
76#    renombrar es seguro (Firefox es reubicable) y por qué NO se tocó `MOZ_APP_NAME` (arrastra el
77#    branding, que es la decisión 1, aún abierta). Verificado corriéndolo: arranca y renderiza.
78#
79name    = "waterfox"
80version = "6.7.1.1"
81license = "MPL-2.0"
82
83[source]
84# Por COMMIT y no por el `tarball_url` de GitHub: ése es un `/archive/`-style que la forja genera al
85# vuelo y cuyo sha256 cambia (ADR 0006). El tag `6.7.1.1` es LIGERO —una sola ref en `ls-remote`, sin
86# `^{}`— ⇒ este sha ES el commit. takana clona con `--filter=blob:none`, así que un árbol Gecko
87# entero no trae la historia: sólo los blobs de este commit.
88repo   = "https://github.com/BrowserWorks/Waterfox.git"
89commit = "fd6662f3abfe6161e88b1b9cb4c70a963bdb345a"
90# Los once de musl del APKBUILD de Alpine (community/firefox). NO se traen los suyos de ppc64le,
91# loongarch ni Android: no aplican a x86_64 y cada parche que no hace falta es una forma más de que
92# un rebase falle sin motivo.
93patches = [
94  "firefox-patches/lfs64.patch",
95  "firefox-patches/time64.patch",
96  "firefox-patches/musl-no-linux-prctl.patch",
97  "firefox-patches/fix-fortify-system-wrappers.patch",
98  "firefox-patches/fix-rust-target.patch",
99  "firefox-patches/sandbox-sched_setscheduler.patch",
100  "firefox-patches/abseil-cpp.patch",
101  "firefox-patches/glean-stub.patch",
102  "firefox-patches/rust-lto-thin.patch",
103  "firefox-patches/wasip1.patch",
104  "firefox-patches/widevine.patch",
105  # PROPIO de waterfox, no heredado de firefox: su `branding-common.mozbuild` instala cinco iconos
106  # y su `package-manifest.in` exige ocho. Los tres que faltan (22, 24, 256) están en el árbol; lo
107  # que falta es la línea que los instala. Rotura sólo de Linux — ver la cabecera del parche.
108  "waterfox-patches/branding-icons-gtk.patch",
109]
110
111[build]
112# ══ `gcc` Y NO `zig-cc` — TRES MUROS DE UNA VEZ ════════════════════════════════════════════════
113# El resto del corpus va con zig-cc y esta receta es una EXCEPCIÓN consciente, del mismo tipo que la
114# que el repo ya mantiene para el kernel y cmake (ADR 0011). Se llegó acá por eliminación, no por
115# comodidad — con zig-cc el configure de Firefox murió tres veces seguidas:
116#
117#   1. «Could not use lld as linker» — zig se identifica como `zig ld 0.16.0` y el sondeo de Mozilla
118#      clasifica por esa cadena buscando «LLD»/«GNU ld»/«GNU gold».
119#   2. «Cannot find ar» — zig trae `ar` como SUBCOMANDO y `check_prog` busca un ejecutable.
120#   3. **«Firefox does not support linking statically with libstdc++»** — y éste no es un flag:
121#      `flags.configure:79` compila un C++ mínimo, lo pasa por `llvm-objdump --private-headers` y
122#      exige encontrar un `NEEDED …libc++`. zig enlaza libc++ ESTÁTICA para musl, así que ese NEEDED
123#      no existe nunca. No hay perilla que apagar; es una negativa de upstream.
124#
125# El gcc del lab (15.2.0, con `libstdc++` COMPARTIDA) satisface los tres: `NEEDED libstdc++.so.6`,
126# un `ar` de verdad y un linker que se anuncia como «GNU ld».
127#
128# ⚠ EL PRECIO, ESCRITO: **el lab NO entra en `hash_inputs`**, así que un Firefox construido con el
129# gcc del lab queda más expuesto a la deriva del rootfs que uno con zig-cc — dos labs con gcc
130# distinto pueden sellar bytes distintos sin que el store lo note. Es la misma deuda que ya cargan el
131# kernel y las otras recetas `compiler=gcc`, y la razón por la que conviene no ampliar esta lista sin
132# haber agotado antes el camino de zig.
133compiler = "gcc"
134target   = "x86_64-linux-musl"
135link     = "dynamic"
136flags    = []
137# ══ `strip_debug`: SIN ESTO EL ARTEFACTO SERÍA INMANEJABLE ═════════════════════════════════════
138# Medido en el `nodejs` que acaba de sellar sin strip: su binario pesa **984 MB, de los cuales
139# `.debug_info` son 497 MB y `.debug_line` otros 43** — más de la mitad del artefacto es información
140# de depuración. Firefox es varias veces V8, así que sin strip su artefacto entraría en varios GB, y
141# el store no es sólo disco: se sincroniza hub↔worker y se respalda.
142#
143# Es además el hallazgo del SDD 23: el 79% del contenido binario del store era `.debug_*`, y quitarlo
144# hace que artefactos que no reproducían PASEN a reproducir, porque lo que difería eran las rutas del
145# árbol de build embebidas en esas secciones.
146#
147# Entra en `hash_inputs` A PROPÓSITO (cambia el contenido del artefacto), y usa `zig objcopy`, que
148# siempre está en el sandbox ⇒ no agrega una dep de build.
149strip_debug = true
150
151[build.phases]
152configure = '''
153export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
154export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"   # ver el guardián del install
155export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
156export MOZ_NOSPAM=1
157# ── RUST_TARGET: lo EXIGE el parche de Alpine, no Firefox ─────────────────────────────────────
158# `fix-rust-target.patch` REEMPLAZA la autodetección del triple de Rust por un
159# `os.environ['RUST_TARGET']` pelado. Sin la variable, configure muere con
160# `KeyError: 'RUST_TARGET'` — que se lee como un bug de Firefox y es en realidad el contrato del
161# parche: el APKBUILD de Alpine lo exporta como `$CTARGET` y acá hay que hacer lo propio.
162# El valor NO se adivina: sale de `ls .dev-fs/alpine/usr/lib/rustlib/` en el lab, que es
163# `x86_64-alpine-linux-musl` — el rustc del sandbox es el de Alpine, no el del host (el del host es
164# `x86_64-unknown-linux-gnu` y usarlo produciría un cross-compile silencioso).
165export RUST_TARGET=x86_64-alpine-linux-musl
166
167# ── LOS PARCHES ROMPEN EL CHECKSUM DE LOS CRATES VENDORIZADOS ─────────────────────────────────
168# `error: the listed checksum of third_party/rust/zeitstempel/src/unix.rs has changed`. Los crates
169# de Rust vienen VENDORIZADOS en el tarball y cargo verifica el sha de CADA fichero contra
170# `.cargo-checksum.json`; en cuanto un parche de musl toca uno, el build muere. Cargo no ofrece
171# ninguna forma de recalcularlo, así que la salida —la misma del APKBUILD de Alpine— es vaciar la
172# lista de ficheros del manifiesto, dejando el checksum del paquete.
173#
174# Son los CINCO crates que tocan los parches, no uno: el error los va destapando de a uno y buscarlos
175# por prueba y error costaría cinco vueltas de configure.
176for c in audio_thread_priority cc zeitstempel wgpu-hal alsa; do
177  f="third_party/rust/$c/.cargo-checksum.json"
178  [ -f "$f" ] || { echo "FALTA $f — ¿upstream movió el crate?" >&2; exit 1; }
179  sed -i 's/\("files":{\)[^}]*/\1/' "$f"
180done
181cat > .mozconfig <<'MOZ'
182ac_add_options --prefix=/usr
183ac_add_options --enable-application=browser
184ac_add_options --enable-default-toolkit=cairo-gtk3-wayland
185ac_add_options --enable-release
186ac_add_options --enable-optimize
187ac_add_options --enable-hardening
188ac_add_options --enable-official-branding
189ac_add_options --with-libclang-path=/usr/lib
190ac_add_options --disable-bootstrap
191ac_add_options --disable-jemalloc
192ac_add_options --disable-crashreporter
193ac_add_options --disable-updater
194ac_add_options --disable-tests
195ac_add_options --disable-debug
196ac_add_options --disable-debug-symbols
197ac_add_options --disable-strip
198ac_add_options --disable-install-strip
199ac_add_options --disable-cargo-incremental
200ac_add_options --without-wasm-sandboxed-libraries
201ac_add_options --enable-alsa
202ac_add_options --enable-pulseaudio
203ac_add_options --enable-dbus
204ac_add_options --enable-ffmpeg
205mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir
206MOZ
207./mach configure
208'''
209# `mach build` respeta -jN; la cuenta es la misma que en nodejs.toml y por el mismo motivo: un número
210# fijo ataría el ArtifactHash a la RAM de quien escribió la receta.
211# ⚠ OJO con el techo: dentro del sandbox `/proc/meminfo` NO muestra la memoria del contenedor sino la
212# del host (medido en el LXC: 186 GiB en vez de 16), así que en un contenedor esta cuenta se degrada
213# a `nproc`. Es un tope, no una garantía.
214compile = '''
215export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
216export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
217export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"   # ver el guardián del install
218export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
219export MOZ_NOSPAM=1
220gib=$(awk "/MemTotal/{printf \"%d\", \$2/1024/1024}" /proc/meminfo)
221j=$(( gib / 3 )); [ "$j" -lt 1 ] && j=1
222n=$(nproc); [ "$j" -gt "$n" ] && j=$n
223echo "mach build -j$j (MemTotal ${gib} GiB, nproc $n)"
224./mach build -j"$j" && exit 0
225
226# ── EL ENLACE FALLA Y `mach` SE TRAGA EL MENSAJE DEL LINKER ───────────────────────────────────
227# Medido dos veces (2026-09-06, 03:20 y 04:08): el build llega al minuto ~36 y muere enlazando
228# `libxul.so` con
229#
230#     E collect2: error: ld returned 1 exit status
231#
232# y NADA delante. Entre la línea que anuncia el enlace y el `collect2` no hay ni un renglón en el
233# log, ni `undefined reference`, ni `cannot find -l`, ni `DSO missing`. No es OOM (13 G libres, swap
234# sin tocar) ni disco (37%). El filtro de salida de `mach` clasifica y descarta, y lo que descarta es
235# justo el diagnóstico.
236#
237# Así que si `mach` falla se REHACE el mismo objetivo llamando a gmake directo, sin el filtro de por
238# medio, y se deja hablar al linker. No cambia lo que se construye: es el mismo target sobre el mismo
239# objdir, sólo que sin nadie tapándole la boca. Si el enlace ahora pasa (porque el fallo fuera del
240# propio `mach`), igual salimos con error: esta rama existe para diagnosticar, no para aprobar.
241echo "=== mach falló; rehago el enlace SIN su filtro para ver qué dice ld ==="
242gmake -C objdir/toolkit/library/build V=1 2>&1 | tail -60
243echo "=== fin del reintento ==="
244exit 1
245'''
246install = '''
247export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
248export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
249export MOZ_BUILD_DATE="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"   # ver el guardián del install
250export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
251DESTDIR=/out ./mach install
252
253# ── RENOMBRAR A `waterfox`: RESUELVE LA COLISIÓN **Y** EL ARRANQUE ────────────────────────────
254# `mach install` deja el árbol en `usr/lib/firefox` y un symlink `usr/bin/firefox`, o sea LAS
255# MISMAS RUTAS que la receta `firefox`. Hasta hoy no rompía nada porque ninguna imagen lleva las
256# dos, pero es una colisión esperando: overlayfs fusiona y gana una en silencio.
257#
258# Se renombra en vez de tocar `MOZ_APP_NAME` porque **Firefox es REUBICABLE**: localiza `omni.ja` y
259# compañía relativo a su propio binario, que es lo que hace que sus tarballs funcionen desde
260# cualquier directorio. Mover el árbol entero es seguro; cambiar `MOZ_APP_NAME` exigiría tocar
261# `confvars.sh` del árbol y arrastra el branding, que es la decisión que esta receta deja abierta
262# más arriba y que no se toma de paso.
263mv /out/usr/lib/firefox /out/usr/lib/waterfox
264rm -f /out/usr/bin/firefox
265
266# ── EL LANZADOR: SIN ESTO EL ARTEFACTO NO ARRANCA ────────────────────────────────────────────
267# Mismo fallo que tenía `firefox` hasta el 2026-09-07 y que se midió sobre el sellado: el binario
268# NO trae `RUNPATH` (verificado: 0 entradas) y el cierre no publica `ld-musl-x86_64.path`, así que
269# las librerías que viven JUNTO al binario —`libnspr4.so`, `libmozsandbox.so`— no las encuentra
270# nadie y muere con «Couldn't load XPCOM». El `LD_LIBRARY_PATH` es además lo que necesitan los
271# procesos HIJOS: el navegador lanza uno por pestaña.
272cat > /out/usr/bin/waterfox <<'LANZA'
273#!/bin/sh
274LD_LIBRARY_PATH="/usr/lib/waterfox${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
275export LD_LIBRARY_PATH
276exec /usr/lib/waterfox/waterfox "$@"
277LANZA
278chmod 755 /out/usr/bin/waterfox
279# El binario interno se llama `firefox` (viene del MOZ_APP_NAME del árbol, que no se toca): se le
280# pone un nombre propio al lado para que el lanzador no mienta sobre a qué apunta.
281[ -f /out/usr/lib/waterfox/waterfox ] || ln -s firefox /out/usr/lib/waterfox/waterfox
282
283# ── GUARDIÁN: QUE EL BuildID NO SEA UNA FECHA ────────────────────────────────────────────────
284# Copiado de `firefox.toml`, y no por simetría: SIN ESTO YA SELLÓ MAL. El artefacto
285# `b3:e65c0221` del 2026-09-06 salió con `BuildID=20260906062042` —la hora del build— o sea que
286# NO REPRODUCE. Y selló en VERDE: el ArtifactHash es input-addressed y no se mueve por esto, así
287# que `build-state.json` lo contaba como bueno. Es exactamente el modo de fallo que el comentario
288# de firefox describe, ocurriendo en la receta de al lado por no haber copiado las cuatro líneas.
289esperado="$(date -u -d "@${SOURCE_DATE_EPOCH:-1}" +%Y%m%d%H%M%S)"
290real="$(sed -n 's/^BuildID=//p' /out/usr/lib/waterfox/application.ini)"
291if [ "$real" != "$esperado" ]; then
292  echo "guardián: BuildID=$real y esperaba $esperado — el artefacto NO reproduce." >&2
293  echo "          Revisá que MOZ_BUILD_DATE se exporte en TODAS las fases." >&2
294  exit 1
295fi
296echo "guardián: BuildID = $real (determinista, de SOURCE_DATE_EPOCH)"
297
298# ── GUARDIÁN: NI UNA RUTA `firefox` EN EL ARTEFACTO ──────────────────────────────────────────
299# Si `mach install` cambia de layout y algo vuelve a aterrizar en `usr/lib/firefox`, la colisión
300# regresa en silencio: dos artefactos distintos publicando el mismo fichero, y en la imagen gana
301# uno sin que nada lo diga.
302test ! -e /out/usr/lib/firefox && test ! -e /out/usr/bin/firefox || {
303  echo "!! quedaron rutas 'firefox' en el artefacto — colisiona con la receta firefox" >&2
304  ls -la /out/usr/lib /out/usr/bin >&2; exit 1; }
305test -x /out/usr/bin/waterfox || { echo "!! no se creó el lanzador" >&2; exit 1; }
306echo "guardián: instalado como waterfox, sin rutas 'firefox' que colisionen"
307'''
308
309[deps]
310# ⚠ DEP DE EJECUCIÓN, NO DE BUILD — Y NO ES DECORACIÓN (2026-09-06).
311# Este artefacto declara `NEEDED libstdc++.so.6` / `libgcc_s.so.1`, y hasta hoy NADIE en el corpus
312# las proveía: sólo existían en `.dev-fs/alpine`, o sea en el LAB. El binario sellaba, REPRODUCÍA
313# bit a bit, pasaba todos los guardianes… y no arrancaba fuera del lab. Se descubrió corriendo
314# `atuq` bajo un sway headless, no leyendo el grafo.
315# `runtime` NO entra en el ArtifactHash (igual que `license`), así que declararlo NO re-hashea:
316# lo que cambia es la CLAUSURA, que es lo que se hidrata en la imagen. Lo vigila
317# `scripts/vigia-sonames.py`, que corre en el latido y deja `docs/state/sonames.txt`.
318runtime = ["gcc-libs"]
319build = [
320  "python3", "pkgconf", "nasm", "make", "cmake",
321  # la plataforma Gecko, compartida con Waterfox y Zen
322  "nodejs", "cbindgen", "clang18", "llvm18",
323  # ⚠ LAS VARIANTES `-shared`, Y NO ES SIMETRÍA CON FIREFOX: ES EL FALLO QUE MATABA EL ENLACE.
324  # Con las estáticas, `libcairo.a` entra al enlace de `libxul.so` por el `-lcairo` que arrastra el
325  # `.pc` de gtk3, y sus objetos CHOCAN con el cairo BUNDLEADO del propio árbol de Gecko:
326  #
327  #     ld: /usr/lib/libcairo.a(cairo-ft-font.c.o): multiple definition of `_cairo_ft_to_cairo_error';
328  #         gfx/cairo/cairo/src/cairo-ft-font.o: first defined here
329  #
330  # …y así decenas de símbolos. Con la variante compartida, `-lcairo` resuelve a `libcairo.so` y los
331  # símbolos bundleados se quedan donde tienen que quedarse. Las cuatro van juntas por lo que ya
332  # dice `firefox.toml`: declarar las ESTÁTICAS junto a un `gtk3` que trae las compartidas son dos
333  # artefactos peleando por el mismo `.pc`. Van las dos listas al mismo sitio o ninguna.
334  "gtk3", "atk", "gdk-pixbuf-shared", "pango-shared", "cairo-shared", "libepoxy",
335  "glib-shared", "pcre2-shared", "libffi-shared", "zlib-shared",
336  "harfbuzz-shared", "fribidi", "freetype-shared", "fontconfig-shared", "pixman",
337  "libpng-shared", "libjpeg-turbo-shared",
338  "wayland", "wayland-protocols", "libxkbcommon", "mesa", "libdrm",
339  "dbus", "pipewire", "pulseaudio", "alsa-lib",
340  "libxml2-shared", "linux-headers",
341]