en debian

Empaquetar Chromium Embedded Framework para Debian: un análisis técnico en profundidad

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:

  1. Clona depot_tools de Google
  2. Ejecuta gclient sync para descargar ~1GB de fuentes de Chromium
  3. Descarga cadenas de herramientas precompiladas de Google Cloud Storage
  4. Opcionalmente usa reclient para la compilación distribuida
  5. Compila las configuraciones Debug y Release
  6. 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

  1. Reutilizar el trabajo del Chromium de Debian: cuando el equipo de chromium parchea una vulnerabilidad, CEF puede hacer rebase
  2. Paquete fuente más pequeño: solo las fuentes específicas de CEF en el tarball orig
  3. 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 basura
  • media/gpu/ – Aceleración de vídeo
  • ui/gfx/ – Primitivas gráficas
  • components/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

RecursoRequisito
Espacio en disco~40GB durante la compilación
RAM16GB mínimo, 32GB+ recomendado
Tiempo de CPU2-24 horas según el hardware
ParalelismoEscala 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:

  1. Hacían falta enlaces simbólicos de recursos en el directorio de librerías
  2. Las librerías de ANGLE eran obligatorias (no solo preferibles)
  3. Las rutas del ICD de SwiftShader deben ser absolutas
  4. 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:

  1. Obtener la rama de CEF que corresponda a la nueva versión de Chromium
  2. Hacer rebase de debian/patches/chromium/ sobre las fuentes nuevas
  3. Probar la compilación y resolver los nuevos conflictos
  4. 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.

Etiquetas

  • #chromium-embedded-framework
  • #debian
  • #stremio
  • #stremio-gtk
  • #wolfi