Resolver el ITP #915400 tras siete años: el desglose técnico completo
Introducción
El Chromium Embedded Framework (CEF) lleva en la cola de empaquetado de Debian desde diciembre de 2018. El bug #915400 documentaba la necesidad: obs-studio quería fuentes de navegador, casparcg-server necesitaba soporte HTTP y varias aplicaciones requerían una alternativa más ligera a Electron.
Anteriormente empaqueté Stremio (basado en QT5) para Debian y Wolfi, pero QT5 está EOL (fin de vida), así que seguí adelante y decidí empaquetar la siguiente generación de Stremio (basada en GTK), pero este paquete depende de chromium-embedded-framework, que no existía en Debian.
Este artículo documenta el enfoque técnico que por fin produjo paquetes de Debian que funcionan.
Por qué CEF es distinto
La mayoría de los proyectos C/C++ siguen un patrón predecible: descargar el tarball, ejecutar configure, make, install. CEF rompe todas las suposiciones.
El proceso de compilación upstream
La compilación oficial de CEF usa automate-git.py, que:
- Clona depot_tools de Google
- Ejecuta
gclient syncpara descargar ~1GB de fuentes de Chromium - Descarga cadenas de herramientas precompiladas de Google Cloud Storage
- Opcionalmente usa
reclientpara la compilación distribuida - Compila las configuraciones Debug y Release
- Crea paquetes de distribución binaria
Este proceso asume acceso a internet, infraestructura de Google y un directorio de trabajo de ~90GB.
Requisitos de Debian
Las compilaciones de Debian deben:
- Estar aisladas de la red durante la compilación
- Ser reproducibles desde el fuente
- Usar las cadenas de herramientas del sistema cuando sea posible
- Cumplir el Filesystem Hierarchy Standard
La brecha entre estos requisitos y las suposiciones del fuente original provocó la mayor parte de la complejidad del empaquetado.
Arquitectura: el enfoque de doble fuente
Problema: la integración de Chromium
CEF no incluye Chromium en su tarball. Espera descargarlo durante la compilación. Incluir las fuentes de Chromium en el tarball orig de CEF:
- Crearía un paquete fuente de ~1.5GB
- Duplicaría las fuentes de chromium que ya existen en Debian
- Crearía una carga de mantenimiento al seguir dos proyectos
El Chromium de Debian no proporciona un paquete de fuentes que podamos usar como dependencia. Para superar eso, vamos a crear un paquete de Debian experimental que tomará las fuentes del Chromium de Debian y las añadirá como dependencia en una subcarpeta. Este enfoque permitirá a otros desarrolladores de Debian opinar y comprobar que esta solución funciona y, cuando el equipo de Chromium de Debian publique por fin las fuentes, solo tendremos que añadirlas como una dependencia normal.
Solución: modelo de dependencia de compilación
El empaquetado trata Chromium como una dependencia de compilación en lugar de como fuente incluido:
debian/ # Version controlled
├── rules
├── control
├── patches/
│ ├── cef/ # 16 patches
│ └── chromium/ # 42 patches
└── ...
cef/ # CEF upstream sources (~450MB)
../chromium_143.0.7499.169.orig.tar.xz # Build dep (~714MB)
../rust-toolchain.tar.xz # Rust stdlib (~142MB)
El fichero debian/rules extrae las fuentes de Chromium en chromium_src/ antes de que empiece la compilación. Esto ocurre en el target clean para asegurar que las fuentes existen antes de cualquier paso de compilación.
Ventajas
- Reutilizar el trabajo del Chromium de Debian: cuando el equipo de chromium parchea una vulnerabilidad, CEF puede hacer rebase
- Paquete fuente más pequeño: solo las fuentes específicas de CEF en el tarball orig
- Separación clara: los parches de CEF y los de Chromium son distintos
Futuro: paquete chromium-source
El bug #893448 propone un paquete binario chromium-source que proporcionaría las fuentes de Chromium extraídas. Cuando se resuelva, CEF podría simplemente Build-Depends: chromium-source y la extracción manual del tarball desaparecería.
La pila de parches
Parches de CEF (16 en total)
Desacoplar el sistema de compilación
0001-skip-gclient-revert.patch
El gclient_hook.py de CEF revierte todos los ficheros al estado de git checkout HEAD antes de compilar. Esto destruye cualquier parche de Debian aplicado durante la compilación. El parche elimina la lógica de reversión.
0002-skip-chromium-checkout.patch
CEF espera ejecutar git clone para Chromium. Este parche se salta el checkout y usa fuentes preextraídas.
0003-use-system-clang.patch
CEF descarga cadenas de herramientas LLVM de Google Cloud Storage. Este parche configura la compilación para usar el paquete clang-19 de Debian.
0004-create-reclient-stub.patch
El reclient de Google proporciona compilación distribuida. En lugar de eliminar todas las referencias, un script stub satisface al sistema de compilación sin acceso a la red.
0005-add-rust-toolchain-stub.patch
Parecido a reclient: un stub para el descargador de la cadena de herramientas de Rust que delega en el rustc del sistema.
Configuración de rutas
0010-use-debian-paths.patch
0011-resource-paths.patch
0012-library-output-paths.patch
CEF asume que los recursos viven junto a los binarios. Estos parches configuran rutas que cumplen el FHS:
- Librerías:
/usr/lib/x86_64-linux-gnu/ - Recursos:
/usr/share/cef/ - Datos de locale:
/usr/share/cef/locales/
Parches de Chromium (42 en total)
Aislamiento de red (12 parches)
disable-gcs-downloads.patch
skip-test-fonts-download.patch
offline-build-config.patch
...
La compilación de Chromium obtiene recursos en varios puntos. Cada punto de descarga necesita un parche para:
- Usar alternativas preempaquetadas
- Saltarse componentes opcionales
- Fallar con un error claro en lugar de colgarse
Compatibilidad con C++23 / libc++ (8 parches)
Debian sid usa libc++-19 con una aplicación estricta de C++23. El destructor de unique_ptr ahora requiere tipos completos:
// Old code (worked in C++17/20):
class RenderFrame;
std::unique_ptr<RenderFrame> frame_; // OK: RenderFrame forward-declared
// C++23 libc++:
// Error: RenderFrame must be complete for ~unique_ptr
Los parches añaden declaraciones anticipadas y reordenan los includes en:
v8/src/heap/– Internos del recolector de basuramedia/gpu/– Aceleración de vídeoui/gfx/– Primitivas gráficascomponents/viz/– Compositor
Corrección de ejemplo en v8/src/heap/marking-state.h:
// Before patch:
class HeapObject;
std::unique_ptr<HeapObject> obj_;
// After patch (add include):
#include "src/objects/heap-object.h"
std::unique_ptr<HeapObject> obj_;
Actualizaciones del compilador (6 parches)
GCC 15 y Clang 19 marcaron como obsoletas varias construcciones:
fix-aggregate-optional-emplace.patch
El comportamiento de std::optional::emplace con inicialización por agregados cambió. Código afectado en la serialización de IPC.
remove-deprecated-warning-flags.patch
Varias opciones -W ya no existen en clang-19.
fix-libclang-paths.patch
Las rutas internas de cabeceras de Clang cambiaron entre versiones.
Rust estable (3 parches)
Chromium usa características de Rust nightly. Los parches eliminan:
- Opciones
-Z(inestables) - Características de crates solo de nightly
- Funciones de librería inestables
Librerías del sistema (8 parches)
Parches para preferir las librerías del sistema cuando son compatibles a nivel de ABI:
- libxcb
- fontconfig
- minizip
- zstd
- harfbuzz (parcial)
Algunas librerías no pueden usar las versiones del sistema por diferencias de ABI (V8, Skia, ANGLE).
Configuración de la compilación
Argumentos de GN
La compilación usa GN (Generate Ninja) con una configuración extensa:
gn_args = [
'is_official_build=true',
'is_debug=false',
'symbol_level=0',
# Toolchain
'clang_use_chrome_plugins=false',
'use_lld=true',
'use_custom_libcxx=false', # System libc++
# Disable Google services
'use_official_google_api_keys=false',
'enable_nacl=false',
'enable_widevine=false',
# Hardware acceleration
'use_vaapi=true',
'use_v4l2_codec=false',
# System libraries
'use_system_libffi=true',
'use_system_zlib=false', # ABI issues
...
]
La decisión sobre use_custom_libcxx
CEF incluye por defecto su propia libc++ (use_custom_libcxx=true). Esto evita problemas de compatibilidad de ABI, pero:
- Duplica una librería del sistema
- Puede entrar en conflicto con aplicaciones que usan la libc++ del sistema
- Aumenta el tamaño del binario
Después de muchas pruebas (compilaciones 108-140), use_custom_libcxx=false funciona con los parches de compatibilidad con C++23. Esta es la configuración preferida para la integración con Debian.
Requisitos de recursos de la compilación
| Recurso | Requisito |
|---|---|
| Espacio en disco | ~40GB durante la compilación |
| RAM | 16GB mínimo, 32GB+ recomendado |
| Tiempo de CPU | 2-24 horas según el hardware |
| Paralelismo | Escala bien a más de 16 núcleos |
Estructura de salida
Paquetes binarios
libcef138_138.0.7+chromium143.0.7499.169-1_amd64.deb
├── /usr/lib/x86_64-linux-gnu/
│ ├── libcef.so.138
│ ├── cef/
│ │ ├── libEGL.so.1 # ANGLE
│ │ ├── libGLESv2.so.2 # ANGLE
│ │ └── libvk_swiftshader.so # Software Vulkan
│ └── ...
libcef-dev_138.0.7+chromium143.0.7499.169-1_amd64.deb
├── /usr/include/cef/
│ ├── include/
│ │ ├── cef_app.h
│ │ ├── cef_browser.h
│ │ └── ...
│ └── ...
├── /usr/lib/x86_64-linux-gnu/
│ ├── libcef.so -> libcef.so.138
│ └── cmake/cef/
│ └── cef-config.cmake
cef-resources_138.0.7+chromium143.0.7499.169-1_all.deb
├── /usr/share/cef/
│ ├── icudtl.dat # ICU data
│ ├── v8_context_snapshot.bin # V8 snapshot
│ ├── chrome_100_percent.pak
│ ├── chrome_200_percent.pak
│ └── locales/
│ ├── en-US.pak
│ ├── es.pak
│ └── ...
ANGLE y SwiftShader
CEF requiere librerías de abstracción de GPU específicas:
ANGLE: implementación de OpenGL ES sobre Vulkan/DirectX. No es intercambiable con el EGL de Mesa: la API es parecida pero los internos difieren. Se instala en /usr/lib/x86_64-linux-gnu/cef/ con configuración de RPATH.
SwiftShader: implementación de Vulkan por software para sistemas sin aceleración de GPU. El JSON del ICD debe usar rutas absolutas:
{
"file_format_version": "1.0.0",
"ICD": {
"library_path": "/usr/lib/x86_64-linux-gnu/cef/libvk_swiftshader.so",
"api_version": "1.1.0"
}
}
Descubrimiento de las rutas de recursos
CEF carga los recursos al principio de la inicialización, antes de la mayoría de los callbacks de la aplicación. La librería busca de forma relativa a libcef.so, no al binario de la aplicación.
La solución con enlaces simbólicos
# In /usr/lib/x86_64-linux-gnu/:
icudtl.dat -> ../../share/cef/icudtl.dat
v8_context_snapshot.bin -> ../../share/cef/v8_context_snapshot.bin
Esto permite que los subprocesos (renderer, GPU, utility) encuentren los recursos cuando se lanzan con cef_execute_process(), antes de que se aplique ninguna configuración de la aplicación.
Pruebas y validación
Pruebas unitarias
CEF incluye ceftests, pero muchas pruebas requieren acceso a la red o una pantalla gráfica. El empaquetado ejecuta un subconjunto de las pruebas que funcionan sin conexión.
Pruebas de integración
La prueba definitiva es compilar una aplicación real. stremio-gtk ejercita:
- Renderizado fuera de pantalla
- Varios tipos de proceso
- Protocolos de IPC
- Aceleración de GPU
- Carga de recursos
Problemas descubiertos a través de stremio-gtk que pasaron las pruebas unitarias:
- Hacían falta enlaces simbólicos de recursos en el directorio de librerías
- Las librerías de ANGLE eran obligatorias (no solo preferibles)
- Las rutas del ICD de SwiftShader deben ser absolutas
- El manejo de las opciones de línea de comandos de los subprocesos
Consideraciones de mantenimiento
Actualizaciones de Chromium
Cuando Debian actualiza Chromium, CEF debería seguirlo:
- Obtener la rama de CEF que corresponda a la nueva versión de Chromium
- Hacer rebase de debian/patches/chromium/ sobre las fuentes nuevas
- Probar la compilación y resolver los nuevos conflictos
- Actualizar los números de versión en todos los sitios
Actualizaciones de seguridad
CEF hereda la superficie de ataque de Chromium. Las actualizaciones de seguridad de Chromium deberían llegar a CEF con rapidez. La arquitectura de doble fuente ayuda: actualizar chromium_*.orig.tar.xz y recompilar cubre la mayoría de los problemas.
Coordinación con el fuente original
El fuente original de CEF responde bien a las preocupaciones de empaquetado. Varios parches desarrollados para Debian se han enviado al fuente original o han influido en sus decisiones.
Conclusión
Empaquetar CEF requiere tratar un motor de navegador como una librería, con toda la complejidad que eso implica. La arquitectura de doble fuente, la extensa pila de parches y la cuidadosa configuración de rutas producen paquetes que se integran con el ecosistema de Debian en lugar de luchar contra él.
El enfoque documentado aquí debería transferirse a otras distribuciones con políticas parecidas. Los parches están organizados por propósito (sistema de compilación, compatibilidad, rutas) para facilitar el porting.
Paquetes disponibles en salsa.debian.org/mendezr/chromium-embedded-framework. ITP #915400.