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 1351Shell 97Rust 63Markdown 571650 Sutichaykuna

⎇ ba1fb2d3a8ada748984dceeb0f58e6b3e67772b8 recipes/firefox.toml TOML · 322 siq'ikuna · 22.5 KB Ñawpa willay ch'uya

firefox: `MOZ_REQUIRE_SIGNING` se desactiva con el valor VACÍO, no con cero 37afec380 · Sergio ·

1# Firefox 154.0 — el navegador. Tercera app de usuario final, y **la cabeza de la familia Gecko**:
2# Waterfox y Zen son forks suyos y reutilizan todo lo de abajo.
3#
4# ══ POR QUÉ 154 Y NO 155, QUE ES LA QUE SIGUE ZEN ══════════════════════════════════════════════
5# Zen declara `"version": "155.0.1"` en su `surfer.json` y Waterfox 6.7 también va por el canal
6# release, así que 155 parecía la base obvia. Se eligió **154** por una razón concreta: **los parches
7# de musl de Alpine son para 154.0**, y son once. Ese es el trabajo de portabilidad que un import de
8# nix pierde y que no conviene rehacer a mano — sin ellos Firefox no compila contra musl.
9#
10# Un Firefox que compila con el set probado vale más que uno con el número correcto que no compila.
11# Y como Firefox se mueve cada cuatro semanas, la paridad exacta con Zen es una cinta de correr: lo
12# que de verdad se reutiliza entre los tres es LA PLATAFORMA (gtk3/nodejs/clang/cbindgen, todo ya en
13# el corpus) y ESTE SET DE PARCHES. Subir a 155 después es un rebase, no un port.
14#
15# ══ TODO BUNDLEADO SALVO GTK3 ══════════════════════════════════════════════════════════════════
16# Alpine usa `--with-system-{icu,nspr,nss,av1,libvpx,webp,libevent,...}`; de ésas el corpus tiene
17# CERO. Firefox trae todas en el árbol, así que se dejan bundleadas: son menos piezas móviles para el
18# primer build, que es exactamente cuando conviene minimizar variables. Cuando alguna de esas
19# librerías tenga receta propia y valga compartirla, el flag se enciende y se re-mide.
20#
21# ══ SIN BRANDING OFICIAL, Y NO ES UN DESCUIDO ══════════════════════════════════════════════════
22# Alpine pone `--enable-official-branding`. Acá NO: el binario lleva once parches, y usar el nombre y
23# el logo de Firefox sobre un build modificado entra en la política de marcas de Mozilla — es
24# literalmente la historia de Iceweasel en Debian. Con el branding `unofficial` el navegador es el
25# mismo software y nadie tiene que pedir permiso a nadie. Es la misma cautela que la receta de
26# `dejavu-fonts-nerd` ya aplicó con la licencia de Bitstream Vera: **una fuente modificada no puede
27# llamarse como la original, y un navegador parcheado tampoco.**
28#
29# ══ WAYLAND-ONLY, HEREDADO DE GTK3 ═════════════════════════════════════════════════════════════
30# `--enable-default-toolkit=cairo-gtk3-wayland`. Nuestro GTK3 se construyó con `-Dx11_backend=false`
31# ⇒ este Firefox **no puede correr como cliente X11 ni bajo Xwayland**. Bajo Wayland nativo sí. Está
32# escrito en `recipes/gtk3.toml` y se repite acá porque es lo primero que alguien va a preguntar.
33#
34# ══ HERMÉTICO: NADA DE RED DURANTE EL BUILD ════════════════════════════════════════════════════
35#   --disable-bootstrap                      su trabajo ES descargar toolchains. Prohibido.
36#   MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE  sin esto `mach` arma un virtualenv con `pip` y sale a
37#     =system                                la red. Con `system` usa lo que ya está.
38#   MOZBUILD_STATE_PATH                      por defecto escribe en `$HOME`, que en el sandbox no es
39#                                            suyo; se apunta al árbol de build.
40#   Los crates de Rust vienen VENDORIZADOS en el tarball (`third_party/rust`) ⇒ cargo no baja nada.
41#
42# ══ LO DEMÁS QUE SE APAGA ══════════════════════════════════════════════════════════════════════
43#   --disable-jemalloc            musl trae su propio allocator; jemalloc encima es la receta del
44#                                 cuelgue clásico de Firefox en Alpine.
45#   --without-wasm-sandboxed-      exige el SDK de WASI, que no está en ninguna cola. Apaga el
46#     libraries                    sandbox wasm de algunas librerías de medios, no el sandbox del
47#                                 proceso de contenido.
48#   --disable-crashreporter       manda telemetría a Mozilla; además pide breakpad.
49#   --disable-updater             la distro actualiza por hammer, no por un updater propio.
50#   --disable-tests               no entran al artefacto.
51#   (SIN --enable-linker)         Se deja que Firefox detecte solo. Con `zig-cc` esto era
52#                                 OBLIGATORIO —zig se anuncia como `zig ld 0.16.0` y el sondeo de
53#                                 `toolchain.configure` clasifica por esa cadena buscando
54#                                 «mold»/«GNU ld»/«GNU gold»/«LLD»— y **pedir el linker por nombre
55#                                 vuelve FATAL cualquier fallo del sondeo**. Con gcc el linker sí se
56#                                 anuncia como «GNU ld», así que el flag ya no haría daño; se deja
57#                                 fuera igual porque no aporta nada y una perilla menos es una
58#                                 diferencia menos entre esta receta y las de Waterfox y Zen.
59name    = "firefox"
60version = "154.0"
61license = "MPL-2.0"
62
63[source]
64# Tarball de RELEASE de Mozilla: fichero SUBIDO por upstream, sha256 estable y publicado en su
65# SHA256SUMS. No es un `/archive/<tag>` de forja, que se genera al vuelo (ver `recipes/mbedtls.toml`).
66tarball = "https://ftp.mozilla.org/pub/firefox/releases/154.0/source/firefox-154.0.source.tar.xz"
67sha256  = "36cec5b3688a60f78a6d20dcaee15b598f84e03c66f6587056aead6cb498b99a"
68# Los once de musl del APKBUILD de Alpine (community/firefox). NO se traen los suyos de ppc64le,
69# loongarch ni Android: no aplican a x86_64 y cada parche que no hace falta es una forma más de que
70# un rebase falle sin motivo.
71patches = [
72  "firefox-patches/lfs64.patch",
73  "firefox-patches/time64.patch",
74  "firefox-patches/musl-no-linux-prctl.patch",
75  "firefox-patches/fix-fortify-system-wrappers.patch",
76  "firefox-patches/fix-rust-target.patch",
77  "firefox-patches/sandbox-sched_setscheduler.patch",
78  "firefox-patches/abseil-cpp.patch",
79  "firefox-patches/glean-stub.patch",
80  "firefox-patches/rust-lto-thin.patch",
81  "firefox-patches/wasip1.patch",
82  "firefox-patches/widevine.patch",
83]
84
85[build]
86# ══ `clang` — Y POR QUÉ YA NO ES `gcc` (2026-09-05) ════════════════════════════════════════════
87# HISTORIA: esta receta nació con `compiler = "gcc"` porque con zig-cc el configure de Firefox murió
88# tres veces seguidas. Los tres muros eran REALES y siguen siéndolo para zig:
89#
90#   1. «Could not use lld as linker» — zig se identifica como `zig ld 0.16.0` y el sondeo de Mozilla
91#      clasifica por esa cadena buscando «LLD»/«GNU ld»/«GNU gold».
92#   2. «Cannot find ar» — zig trae `ar` como SUBCOMANDO y `check_prog` busca un ejecutable.
93#   3. «Firefox does not support linking statically with libstdc++» — `flags.configure:79` compila un
94#      C++ mínimo, lo pasa por `llvm-objdump --private-headers` y exige un `NEEDED` de la stdlib de
95#      C++. zig enlaza libc++ ESTÁTICA para musl, así que ese NEEDED no existe nunca.
96#
97# gcc satisfacía los tres, pero al precio de quedar FUERA de la cadena de optimización de Gecko:
98# **PGO, LTO y BOLT son cadena de clang en Firefox** (`--enable-lto=cross` quiere clang+lld,
99# `--enable-profile-use=cross` espera `-fprofile-instr-use`). Con gcc no hay perilla que encender.
100# Medido contra el APKBUILD de `community/firefox` de aports —nuestro propio upstream, de donde
101# salen los once parches de musl— y contra el PKGBUILD de Arch: **las dos hacen LTO y PGO**. No
102# estábamos adelante de nadie; estábamos atrás, y la puerta era el compilador.
103#
104# clang RESUELVE LOS TRES MUROS SIN PERDER NADA:
105#   1. `--enable-linker=lld` + `lld` en el lab (añadido a `bootstrap-devfs.sh` el 2026-09-05).
106#   2. `AR=llvm-ar` lo pone hammer solo con `compiler="clang"` (hammer-build/src/lib.rs), y la suite
107#      `llvm-*` está expuesta en `/usr/bin` por el paso 3a-ter del bootstrap.
108#   3. clang++ de Alpine usa la `libstdc++` COMPARTIDA de gcc ⇒ el `NEEDED` existe. La prueba no es
109#      teórica: Alpine construye este mismo Firefox con clang22 y **sin** `libcxx` en sus makedepends.
110#
111# El lab ya traía `clang22`/`llvm22` 22.1.8 (la MISMA major que usa Alpine: `_llvmver=22`), así que
112# esto no costó una receta de LLVM: costó `apk add lld`.
113#
114# ⚠ EL PRECIO, ESCRITO: el toolchain del lab SÍ entra en `hash_inputs` desde el 2026-08-10
115# (`hammer-core/src/lab.rs`), pero **`lld` no casa ningún prefijo de `TOOLCHAIN_PREFIXES`** ⇒ su
116# versión NO está en la huella. Eso es lo que permitió añadirlo sin re-hashear los 837 artefactos
117# sellados —medido: la huella no se movió—, y a la vez deja un agujero que sólo expone a las recetas
118# `compiler="clang"`, que hoy es ésta. Cerrarlo cuesta un re-hasheo del corpus entero.
119compiler = "clang"
120target   = "x86_64-linux-musl"
121link     = "dynamic"
122flags    = []
123# ══ `strip_debug`: SIN ESTO EL ARTEFACTO SERÍA INMANEJABLE ═════════════════════════════════════
124# Medido en el `nodejs` que acaba de sellar sin strip: su binario pesa **984 MB, de los cuales
125# `.debug_info` son 497 MB y `.debug_line` otros 43** — más de la mitad del artefacto es información
126# de depuración. Firefox es varias veces V8, así que sin strip su artefacto entraría en varios GB, y
127# el store no es sólo disco: se sincroniza hub↔worker y se respalda.
128#
129# Es además el hallazgo del SDD 23: el 79% del contenido binario del store era `.debug_*`, y quitarlo
130# hace que artefactos que no reproducían PASEN a reproducir, porque lo que difería eran las rutas del
131# árbol de build embebidas en esas secciones.
132#
133# Entra en `hash_inputs` A PROPÓSITO (cambia el contenido del artefacto), y usa `zig objcopy`, que
134# siempre está en el sandbox ⇒ no agrega una dep de build.
135strip_debug = true
136# ⚠ DEUDA MEDIDA EL 2026-09-05: ESTE ARTEFACTO NO REPRODUCE BIT A BIT ═══════════════════════════
137# `application.ini` del artefacto sellado trae `BuildID=20260905035306`: la FECHA Y HORA del build.
138# Dos construcciones de esta misma receta, con el mismo hash, producen bytes distintos — que es
139# exactamente el agujero que el invariante de reproducibilidad existe para cerrar, y no se ve en
140# `build-state.json` porque el ArtifactHash es input-addressed y no se mueve.
141# El arreglo es el que ya usa Alpine: exportar `MOZ_BUILD_DATE` con un valor determinista en las
142# fases (su APKBUILD lo deriva de `SOURCE_DATE_EPOCH`; acá el candidato natural es un valor fijo,
143# que al vivir en el texto de la fase entra en `hash_inputs` y queda declarado).
144# NO se aplica de paso: cambiar una fase re-hashea la receta y cuesta otro build de cuatro horas
145# más el re-sellado de `atuq`, que cuelga de ella. Es su propia unidad de trabajo.
146
147[build.phases]
148configure = '''
149export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
150export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
151export MOZ_NOSPAM=1
152# ── RUST_TARGET: lo EXIGE el parche de Alpine, no Firefox ─────────────────────────────────────
153# `fix-rust-target.patch` REEMPLAZA la autodetección del triple de Rust por un
154# `os.environ['RUST_TARGET']` pelado. Sin la variable, configure muere con
155# `KeyError: 'RUST_TARGET'` — que se lee como un bug de Firefox y es en realidad el contrato del
156# parche: el APKBUILD de Alpine lo exporta como `$CTARGET` y acá hay que hacer lo propio.
157# El valor NO se adivina: sale de `ls .dev-fs/alpine/usr/lib/rustlib/` en el lab, que es
158# `x86_64-alpine-linux-musl` — el rustc del sandbox es el de Alpine, no el del host (el del host es
159# `x86_64-unknown-linux-gnu` y usarlo produciría un cross-compile silencioso).
160export RUST_TARGET=x86_64-alpine-linux-musl
161
162# ── `MOZ_REQUIRE_SIGNING=0`: SIN ESTO LA DISTRO NO PUEDE SHIPEAR NI UNA EXTENSIÓN PROPIA ──────
163# Firefox exige que todo XPI esté firmado por Mozilla. La pref `xpinstall.signatures.required` NO
164# alcanza: se ignora cuando la exigencia viene COMPILADA, y viene compilada porque el default de
165# `MOZ_REQUIRE_SIGNING` sale del MILESTONE (154.0 es release), no del canal de actualización — el
166# nuestro es `default`, y aun así estaba activa.
167#
168# Medido, no deducido: con el autoconfig ejecutándose de verdad (testigo `atuq.autoconfig.ok=true`
169# en el perfil) y `lockPref("xpinstall.signatures.required", false)` puesta, la instalación por
170# política seguía muriendo con `ERROR_SIGNEDSTATE_REQUIRED`.
171#
172# ⚠ EL PRECIO, ESCRITO: este Firefox —y por herencia `atuq`— deja de exigir la firma de Mozilla
173# para CUALQUIER complemento, no sólo los nuestros. La alternativa real es firmar en AMO, que pide
174# cuenta y revisión de Mozilla por cada versión: exactamente la dependencia externa que esta distro
175# existe para no tener. Es la misma postura que ya tomaba `--with-unsigned-addon-scopes`, llevada
176# hasta donde de verdad hace falta.
177# ⚠ SE DESACTIVA CON EL VALOR VACÍO, NO CON CERO. `MOZ_REQUIRE_SIGNING=0` muere con
178# «InvalidOptionError: MOZ_REQUIRE_SIGNING takes 0 values», porque es una bandera booleana y el `0`
179# es un VALOR que no acepta. La semántica está en el propio parser de mozbuild
180# (`python/mozbuild/mozbuild/configure/options.py`), y no se dedujo: se leyó del tarball —
181#     if values == ("",):  return NegativeOptionValue()   ⇒ VAR=   (vacío) DESACTIVA
182#     if values == ("1",): return PositiveOptionValue()   ⇒ VAR=1  activa
183# El `export` con valor vacío es lo que hace llegar la variable PRESENTE y vacía; desexportarla la
184# dejaría sin definir y volvería al default, que acá es «exigir firma».
185export MOZ_REQUIRE_SIGNING=
186
187# ── LOS PARCHES ROMPEN EL CHECKSUM DE LOS CRATES VENDORIZADOS ─────────────────────────────────
188# `error: the listed checksum of third_party/rust/zeitstempel/src/unix.rs has changed`. Los crates
189# de Rust vienen VENDORIZADOS en el tarball y cargo verifica el sha de CADA fichero contra
190# `.cargo-checksum.json`; en cuanto un parche de musl toca uno, el build muere. Cargo no ofrece
191# ninguna forma de recalcularlo, así que la salida —la misma del APKBUILD de Alpine— es vaciar la
192# lista de ficheros del manifiesto, dejando el checksum del paquete.
193#
194# Son los CINCO crates que tocan los parches, no uno: el error los va destapando de a uno y buscarlos
195# por prueba y error costaría cinco vueltas de configure.
196for c in audio_thread_priority cc zeitstempel wgpu-hal alsa; do
197  f="third_party/rust/$c/.cargo-checksum.json"
198  [ -f "$f" ] || { echo "FALTA $f — ¿upstream movió el crate?" >&2; exit 1; }
199  sed -i 's/\("files":{\)[^}]*/\1/' "$f"
200done
201cat > .mozconfig <<'MOZ'
202ac_add_options --prefix=/usr
203ac_add_options --enable-application=browser
204ac_add_options --enable-default-toolkit=cairo-gtk3-wayland
205ac_add_options --enable-release
206ac_add_options --enable-optimize
207ac_add_options --enable-hardening
208# ── LA CADENA DE OPTIMIZACIÓN (2026-09-05) ────────────────────────────────────────────────────
209# Las tres primeras son lo que Alpine y Arch ya hacían y nosotros no. `--enable-linker=lld` además
210# resuelve el muro 1 del sondeo de linker (ver el bloque [build]).
211ac_add_options --enable-linker=lld
212ac_add_options --enable-lto=cross
213ac_add_options --enable-packed-relative-relocs
214# ── LO QUE HABILITA A `atuq` (SDD 26) ─────────────────────────────────────────────────────────
215# Sin esto un Firefox de release rechaza las extensiones que la distro deja en
216# `distribution/extensions/`. Es flag de CONFIGURE, o sea que NO se puede resolver en el overlay del
217# artefacto derivado: la capacidad de `atuq` de shipear sus propias extensiones (el `sct` v1) se
218# decide acá, en la base. Alpine pasa exactamente este flag y por el mismo motivo.
219ac_add_options --with-unsigned-addon-scopes=app,system
220ac_add_options --with-branding=browser/branding/unofficial
221ac_add_options --with-libclang-path=/usr/lib
222ac_add_options --disable-bootstrap
223ac_add_options --disable-jemalloc
224ac_add_options --disable-crashreporter
225ac_add_options --disable-updater
226ac_add_options --disable-tests
227ac_add_options --disable-debug
228ac_add_options --disable-debug-symbols
229ac_add_options --disable-strip
230ac_add_options --disable-install-strip
231ac_add_options --disable-cargo-incremental
232ac_add_options --without-wasm-sandboxed-libraries
233ac_add_options --enable-alsa
234ac_add_options --enable-pulseaudio
235ac_add_options --enable-dbus
236ac_add_options --enable-ffmpeg
237mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir
238MOZ
239# ── POR QUÉ AQUÍ NO HAY PGO TODAVÍA ───────────────────────────────────────────────────────────
240# PGO es la otra mitad de la ganancia (LTO solo rinde menos que LTO+PGO) y NO entra en esta pasada
241# por dos muros que las distros no tienen, porque no persiguen lo que nosotros perseguimos:
242#   1. El perfil se junta CORRIENDO el navegador. Alpine y Arch lo hacen bajo `xvfb-run`; nosotros
243#      NO tenemos X11 en el corpus (la distro es Wayland-only) ⇒ el camino es un sway headless.
244#   2. El `profdata` NO es determinista (los contadores dependen del timing) ⇒ si se generara aquí,
245#      cada build sellaría bytes distintos. La salida es generarlo UNA vez, sellarlo como artefacto
246#      propio y consumirlo por hash desde `[deps]`.
247# Es su propia unidad de trabajo (SDD 26 §3.ter). Cuando llegue, además trae el `jarlog`, que ordena
248# el `omni.ja` para el arranque — y eso condiciona cómo `atuq` puede re-empacarlo.
249#
250# ── ThinLTO Y LA MEMORIA ──────────────────────────────────────────────────────────────────────
251# El enlace ThinLTO abre un trabajo por hilo y cada uno carga bitcode: sin capar, el link es lo
252# primero que muere por OOM en una máquina de 16 GiB. Se capa con la MISMA cuenta que `-j` y por la
253# misma razón que ella no es un número fijo: un literal ataría el ArtifactHash a la RAM de quien
254# escribió la receta (ver la nota de la fase compile).
255# ⚠ Si algún día dos máquinas con distinto `nproc` sellan bytes distintos, el primer sospechoso es
256# este flag — ThinLTO se diseñó determinista respecto al número de trabajos, pero es una promesa
257# de upstream, no algo que hayamos medido acá.
258gib=$(awk "/MemTotal/{printf \"%d\", \$2/1024/1024}" /proc/meminfo)
259jl=$(( gib / 3 )); [ "$jl" -lt 1 ] && jl=1
260n=$(nproc); [ "$jl" -gt "$n" ] && jl=$n
261export LDFLAGS="${LDFLAGS:-} -Wl,--thinlto-jobs=$jl"
262echo "ThinLTO con --thinlto-jobs=$jl (MemTotal ${gib} GiB, nproc $n)"
263./mach configure
264'''
265# `mach build` respeta -jN; la cuenta es la misma que en nodejs.toml y por el mismo motivo: un número
266# fijo ataría el ArtifactHash a la RAM de quien escribió la receta.
267# ⚠ OJO con el techo: dentro del sandbox `/proc/meminfo` NO muestra la memoria del contenedor sino la
268# del host (medido en el LXC: 186 GiB en vez de 16), así que en un contenedor esta cuenta se degrada
269# a `nproc`. Es un tope, no una garantía.
270compile = '''
271export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
272export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
273export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
274export MOZ_NOSPAM=1
275gib=$(awk "/MemTotal/{printf \"%d\", \$2/1024/1024}" /proc/meminfo)
276j=$(( gib / 3 )); [ "$j" -lt 1 ] && j=1
277n=$(nproc); [ "$j" -gt "$n" ] && j=$n
278echo "mach build -j$j (MemTotal ${gib} GiB, nproc $n)"
279./mach build -j"$j"
280'''
281install = '''
282export RUST_TARGET=x86_64-alpine-linux-musl   # ver la nota en la fase configure
283export MOZBUILD_STATE_PATH="$PWD/.mozbuild"
284export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
285DESTDIR=/out ./mach install
286'''
287
288[deps]
289build = [
290  "python3", "pkgconf", "nasm", "make", "cmake",
291  # la plataforma Gecko, compartida con los forks (SDD 26)
292  #
293  # ⚠ NI `clang18` NI `llvm18` — Y NO ES UN OLVIDO (2026-09-05) ─────────────────────────────────
294  # Estaban aquí cuando esta receta compilaba con gcc: `clang18` aportaba el `libclang.so` que
295  # bindgen carga en runtime y `llvm18` la suite `llvm-ar`/`llvm-objdump` que Mozilla exige. Con
296  # `compiler = "clang"` se volvieron ACTIVAMENTE DAÑINAS: una dep del corpus se materializa en el
297  # sandbox y **tapa** el binario del lab, así que `/usr/bin/llvm-ar` pasaba a ser el de LLVM 18
298  # mientras el compilador era clang 22. Con ThinLTO encendido los `.o` ya no son objetos sino
299  # BITCODE, y el bitcode no es compatible hacia atrás:
300  #
301  #     /usr/bin/llvm-ar: error: …log.o: 'Unknown attribute kind (102)'
302  #                              (Producer: 'LLVM22.1.8' Reader: 'LLVM 18.1.8')
303  #
304  # El error nombra las DOS versiones, que es lo que lo hace diagnosticable de un vistazo. Es la
305  # misma forma que la lección de los headers UAPI: una dep tapa al lab y el fallo aparece lejos.
306  # El lab ya trae todo lo necesario en la versión correcta —`clang22-libclang` deja
307  # `/usr/lib/libclang.so` y el paso 3a-ter del bootstrap expone la suite `llvm-*` de llvm22 en
308  # `/usr/bin`—, así que la respuesta no es apuntar el AR a mano: es NO traer el 18.
309  # Es además lo que hace Alpine: clang22 + clang22-libclang, sin un segundo LLVM en la mesa.
310  "nodejs", "cbindgen",
311  # Las mismas variantes `-shared` que usa gtk3 (2026-09-05). No es simetría: declarar acá las
312  # ESTÁTICAS junto a un gtk3 que trae las compartidas son dos artefactos peleando por el mismo
313  # `lib/pkgconfig/pango.pc` y las mismas cabeceras, que es la otra forma conocida de este mismo
314  # fallo. Van las dos listas al mismo sitio o ninguna.
315  "gtk3", "atk", "gdk-pixbuf-shared", "pango-shared", "cairo-shared", "libepoxy",
316  "glib-shared", "pcre2-shared", "libffi-shared", "zlib-shared",
317  "harfbuzz-shared", "fribidi", "freetype-shared", "fontconfig-shared", "pixman",
318  "libpng-shared", "libjpeg-turbo-shared",
319  "wayland", "wayland-protocols", "libxkbcommon", "mesa", "libdrm",
320  "dbus", "pipewire", "pulseaudio", "alsa-lib",
321  "libxml2-shared", "linux-headers",
322]