Con la cadena GTK3 arreglada, atuq arrancó de verdad: parent vivo, CERO procesos de contenido muertos, y pintando una página local. Quedaban dos cosas. 1. SE ANUNCIABA COMO `firefox-default`. El application.ini decía RemotingName=atuq y aun así el proceso se presentaba así — porque esa cadena es un MOZ_APP_REMOTINGNAME horneado en el ELF, y es la que Gecko pasa a g_set_prgname(), o sea el **app_id de Wayland**. Consecuencia visible: el escritorio no casaba la ventana con nuestro .desktop (StartupWMClass=atuq) ⇒ icono genérico y sin agrupar. Se parchea EN SITIO, no recompilando: cambiarlo en recipes/firefox.toml brandearía la BASE como atuq, y firefox tiene que seguir siendo firefox para los otros forks. Es seguro porque es una cadena C terminada en NUL en el pool de .rodata —`firefox-default\0` son 16 bytes exactos y se escriben 16—, y se EXIGE una única ocurrencia: si upstream la duplica o la renombra, falla en vez de parchear el sitio equivocado. Verificado después: 0 ocurrencias de la vieja, y el log dice `(atuq:26423)`. 2. UN BUG QUE SÓLO SE VE FUERA DE ROOT. `cp -a` preserva los modos y los ficheros del store están sellados sin permiso de escritura; todo lo que viene después los modifica. En el worker pasaba inadvertido porque corre como root, que ignora los bits. El primer build local murió con `PermissionError: /out/usr/lib/atuq/application.ini`. Se arregla con `chmod -R u+w` en la receta y no en el entorno: un artefacto no debe depender de con qué uid lo construiste. Y el runner pasa a hidratar las variantes `-shared`: en runtime una `.a` no sirve de nada, y son las que gtk3 declara NEEDED desde el arreglo del cuadro de las dos Pango. Este atuq se construyó EN LOCAL en segundos, que era la promesa entera del diseño derivado del SDD 26: iterar el envoltorio sin volver a pagar un build de Gecko.
121 lines
8.5 KiB
TOML
121 lines
8.5 KiB
TOML
# atuq 0.1.0 — el navegador de la distro, como ARTEFACTO DERIVADO de firefox.
|
|
#
|
|
# ══ POR QUÉ DERIVADO Y NO UN FORK DE FUENTE ════════════════════════════════════════════════════
|
|
# Zen —el fork de Firefox al que esto reemplaza en nuestro catálogo— parchea el árbol de Gecko y
|
|
# recompila. Nosotros no distribuimos un binario: sellamos artefactos. Entonces atuq puede DEPENDER
|
|
# de `firefox` y poner su capa encima, y eso cambia el precio de todo:
|
|
# · una iteración de chrome cuesta SEGUNDOS, no las cuatro horas de un build de Gecko;
|
|
# · no mueve el `ArtifactHash` de `firefox` ⇒ el corpus no se invalida;
|
|
# · las CVE se heredan gratis: cuando sube `firefox`, atuq se reconstruye solo.
|
|
# El razonamiento completo, con lo que se promete y lo que NO (spoiler: no habrá «modo Tor»), está
|
|
# en `docs/26-atuq-envoltorio-gecko.md`.
|
|
#
|
|
# ══ SU FUENTE ES ESTE REPO ═════════════════════════════════════════════════════════════════════
|
|
# `source.dir` es el modo de fuente que se agregó para esto (hammer-core/src/recipe.rs): el árbol
|
|
# vive en `recipes/atuq/` y se hashea por CONTENIDO con `ArtifactHash::of_tree`, igual que ya se
|
|
# hacía con los `patches`. No hay commit que pinear y no hay fetch: editar un CSS mueve el hash.
|
|
#
|
|
# ══ v0.2: EL BRANDING VIVE DENTRO DEL ZIP ══════════════════════════════════════════════════════
|
|
# La v0.1 dejó el branding fuera porque había que mirar el árbol real antes de adivinar. Ya se miró,
|
|
# y lo que se encontró decide la forma de esta versión:
|
|
#
|
|
# · `application.ini` del appdir arranca con el comentario «This file is not used». Es HERENCIA
|
|
# del árbol de desarrollo: en un build empaquetado el launcher SÍ lo lee. `rebrand.py` no le
|
|
# cree al comentario, comprueba que las claves estén y falla si no.
|
|
# · Las cadenas que la gente VE están dentro de `browser/omni.ja` (`brand.ftl`), así que sin
|
|
# re-empacar el zip no hay branding posible. Y el original dice **Nightly**, no «Firefox»,
|
|
# porque construimos con `--with-branding=browser/branding/unofficial`.
|
|
# · Los ICONOS, en cambio, están FUERA del zip (`browser/chrome/icons/default/`) ⇒ se reemplazan
|
|
# sin tocarlo.
|
|
# · `browser/omni.ja` son 56 MB con TODAS sus entradas sin comprimir y con `date_time` ya
|
|
# normalizado a 2010-01-01: Mozilla también persigue reproducibilidad. El re-empaque preserva
|
|
# orden, método y fecha — el orden porque es el que Gecko lee al arrancar, y es lo que el
|
|
# `jarlog` del PGO refinará cuando exista.
|
|
name = "atuq"
|
|
version = "0.2.0"
|
|
# El artefacto CONTIENE Firefox, así que hereda su licencia. El overlay de `recipes/atuq/` es
|
|
# nuestro, pero eso no cambia lo que se distribuye.
|
|
license = "MPL-2.0"
|
|
|
|
[source]
|
|
dir = "atuq"
|
|
|
|
[build]
|
|
# No se compila nada: esta receta copia y edita ficheros. `compiler` y `link` entran igual en el
|
|
# hash, así que se dejan en el default del corpus para no inventar una identidad que no significa
|
|
# nada.
|
|
target = "x86_64-linux-musl"
|
|
|
|
[build.phases]
|
|
configure = "echo 'atuq: nada que configurar — es un artefacto derivado'"
|
|
compile = "echo 'atuq: nada que compilar — el motor lo pone la dep firefox'"
|
|
install = '''
|
|
set -e
|
|
SRC=/usr/lib/firefox
|
|
DST=/out/usr/lib/atuq
|
|
|
|
# ── LAS ASERCIONES VAN PRIMERO, Y FALLAN RUIDOSAS ─────────────────────────────────────────────
|
|
# Un derivado que no encuentra su base no debe producir un artefacto flaco: produciría un
|
|
# directorio con cuatro ficheros de configuración, `Store::has` lo daría por presente y el fallo
|
|
# aparecería el día que alguien intente abrir el navegador. Un ausente falla fuerte; un vacío llega
|
|
# hasta el final diciendo que todo fue bien (regla 3 del CLAUDE.md).
|
|
[ -d "$SRC" ] || { echo "atuq: no está el árbol de firefox en $SRC — ¿la dep se materializó?" >&2; exit 1; }
|
|
[ -x "$SRC/firefox" ] || { echo "atuq: $SRC/firefox no existe o no es ejecutable" >&2; exit 1; }
|
|
[ -f "$SRC/browser/omni.ja" ] || { echo "atuq: falta $SRC/browser/omni.ja" >&2; exit 1; }
|
|
|
|
mkdir -p "$DST"
|
|
cp -a "$SRC"/. "$DST"/
|
|
# ── EL ÁRBOL COPIADO LLEGA DE SÓLO LECTURA ────────────────────────────────────────────────────
|
|
# `cp -a` preserva los modos, y los ficheros del store están sellados sin permiso de escritura. Todo
|
|
# lo que viene después —el `.ini`, el `omni.ja`, los iconos, el parche del app_id— los MODIFICA.
|
|
# En el worker esto pasaba inadvertido porque corre como root, que ignora los bits de permiso; el
|
|
# primer build fuera de root murió con `PermissionError: /out/usr/lib/atuq/application.ini`.
|
|
# Es la clase de bug que sólo aparece en la máquina de otro, así que se arregla acá y no en el
|
|
# entorno: un artefacto no debe depender de con qué uid lo construiste.
|
|
chmod -R u+w "$DST"
|
|
|
|
# ── LA CAPA DE CONFIGURACIÓN ──────────────────────────────────────────────────────────────────
|
|
# `defaults/pref/` es el único sitio desde el que se puede pedir el autoconfig ANTES de que exista
|
|
# un perfil; el resto cuelga de ahí.
|
|
mkdir -p "$DST/defaults/pref" "$DST/chrome" "$DST/distribution"
|
|
cp /src/prefs/autoconfig.js "$DST/defaults/pref/autoconfig.js"
|
|
cp /src/atuq.cfg "$DST/atuq.cfg"
|
|
cp /src/chrome/atuq.css "$DST/chrome/atuq.css"
|
|
cp /src/distribution/policies.json "$DST/distribution/policies.json"
|
|
|
|
# ── EL BRANDING ───────────────────────────────────────────────────────────────────────────────
|
|
# Todo lo que exige entrar al zip, editar el .ini o dibujar un PNG está en una herramienta aparte:
|
|
# el shell no tiene `zip` en este lab, y `zipfile` además deja fijar orden, fecha y permisos, que
|
|
# es de lo que depende que el artefacto reproduzca. La herramienta VERIFICA cada cambio y falla si
|
|
# la entrada que iba a reemplazar no estaba.
|
|
python3 /src/tools/rebrand.py "$DST" /src /out
|
|
|
|
# ── INTEGRACIÓN CON EL ESCRITORIO ─────────────────────────────────────────────────────────────
|
|
mkdir -p /out/usr/bin /out/usr/share/applications
|
|
cp /src/atuq.desktop /out/usr/share/applications/atuq.desktop
|
|
# El lanzador es un script y no un symlink: ni el binario ni sus `.so` traen RPATH (comprobado con
|
|
# `readelf -d`), así que sin `LD_LIBRARY_PATH` el motor no encuentra su propio libnspr4 y muere en
|
|
# XPCOMGlueLoad antes de pintar nada. El porqué completo está en el propio fichero.
|
|
cp /src/bin/atuq /out/usr/bin/atuq
|
|
chmod 0755 /out/usr/bin/atuq
|
|
|
|
# ── INVENTARIO ────────────────────────────────────────────────────────────────────────────────
|
|
# Lo que distingue este artefacto de su base, listado con tamaños, para que el log del build
|
|
# responda solo la primera pregunta del diagnóstico cuando «atuq se ve como Firefox».
|
|
echo "atuq: capa aplicada —"
|
|
for f in defaults/pref/autoconfig.js atuq.cfg chrome/atuq.css distribution/policies.json; do
|
|
[ -s "$DST/$f" ] || { echo "atuq: $f quedó VACÍO o ausente" >&2; exit 1; }
|
|
echo " $f ($(wc -c < "$DST/$f") bytes)"
|
|
done
|
|
[ -x "$DST/atuq" ] || { echo "atuq: el binario renombrado no quedó ejecutable" >&2; exit 1; }
|
|
[ -x /out/usr/bin/atuq ] || { echo "atuq: el lanzador no quedó ejecutable" >&2; exit 1; }
|
|
grep -q '^Name=atuq' "$DST/application.ini" || { echo "atuq: application.ini no quedó rebrandeado" >&2; exit 1; }
|
|
'''
|
|
|
|
[deps]
|
|
# `firefox` como dep de BUILD y no de runtime: su árbol se materializa en el sandbox, se copia
|
|
# dentro del artefacto de atuq y a partir de ahí atuq no lo necesita más. El precio es que el
|
|
# artefacto pesa lo que pesa Firefox otra vez; la salida barata (una granja de symlinks) rompe la
|
|
# resolución del appdir por /proc/self/exe, así que la v0.1 paga el disco y lo dice.
|
|
build = ["firefox", "python3"]
|