Packaging Chromium Embedded Framework for Debian: A Technical Deep Dive

Resolving ITP #915400 after seven years—the complete technical breakdown

Introduction

The Chromium Embedded Framework (CEF) has been sitting in Debian’s packaging queue since December 2018. Bug #915400 documented the need: obs-studio wanted browser sources, casparcg-server needed HTTP support, and various applications required a lighter alternative to Electron.

Previously I packaged Stremio (QT5 based) for Debian and Wolfi, but QT5 is EOL (end of life), so I went on and decided to package the next generation of Stremio (GTK based) but this package depends on chromium-embedded-framework that did not exist en Debian.

This article documents the technical approach that finally produced working Debian packages.

Why CEF Is Different

Most C/C++ projects follow a predictable pattern: download tarball, run configure, make, install. CEF breaks every assumption.

The Upstream Build Process

CEF’s official build uses automate-git.py, which:

  1. Clones depot_tools from Google
  2. Runs gclient sync to fetch ~1GB of Chromium sources
  3. Downloads prebuilt toolchains from Google Cloud Storage
  4. Optionally uses reclient for distributed compilation
  5. Builds both Debug and Release configurations
  6. Creates binary distribution packages

This process assumes internet access, Google infrastructure, and a ~90GB working directory.

Debian Requirements

Debian builds must be:

  • Network-isolated during compilation
  • Reproducible from source
  • Using system toolchains where possible
  • Compliant with the Filesystem Hierarchy Standard

The gap between these requirements and upstream assumptions drove most of the packaging complexity.

Architecture: The Dual-Source Approach

Problem: Chromium Integration

CEF doesn’t bundle Chromium in its tarball. It expects to download it during build. Including Chromium sources in the CEF orig tarball would:

  • Create a ~1.5GB source package
  • Duplicate Debian’s existing chromium sources
  • Create maintenance burden tracking two projects

Debian Chromium doesn’t provide a Source package that we can use as a dependency. To overcome that, we are going to create an experimental Debian package that will get the Debian Chromium Sources and will add it as a dependency in a subfolder. This approach will allow other Debian Developers to weigh in and see that this solution works and when the Debian Chromium Team eventually publishes the sources, we just need to add it as a regular dependency.


Solution: Build Dependency Model

The packaging treats Chromium as a build dependency rather than bundled source:

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)

The debian/rules file extracts Chromium sources into chromium_src/ before the build begins. This happens in the clean target to ensure sources exist before any build steps.

Benefits

  1. Reuse Debian Chromium work: When the chromium team patches a vulnerability, CEF can rebase
  2. Smaller source package: Only CEF-specific sources in the orig tarball
  3. Clear separation: CEF patches vs Chromium patches are distinct

Future: chromium-source Package

Bug #893448 proposes a chromium-source binary package that would provide extracted Chromium sources. When resolved, CEF could simply Build-Depends: chromium-source and the manual tarball extraction disappears.

The Patch Stack

CEF Patches (16 total)

Build System Decoupling

0001-skip-gclient-revert.patch

CEF’s gclient_hook.py reverts all files to git checkout HEAD state before building. This destroys any Debian patches applied during the build. The patch removes the revert logic.

0002-skip-chromium-checkout.patch

CEF expects to run git clone for Chromium. This patch skips the checkout and uses pre-extracted sources.

0003-use-system-clang.patch

CEF downloads LLVM toolchains from Google Cloud Storage. This patch configures the build to use Debian’s clang-19 package.

0004-create-reclient-stub.patch

Google’s reclient provides distributed compilation. Rather than removing all references, a stub script satisfies the build system without network access.

0005-add-rust-toolchain-stub.patch

Similar to reclient—a stub for the Rust toolchain downloader that delegates to system rustc.

Path Configuration

0010-use-debian-paths.patch
0011-resource-paths.patch
0012-library-output-paths.patch

CEF assumes resources live alongside binaries. These patches configure FHS-compliant paths:

  • Libraries: /usr/lib/x86_64-linux-gnu/
  • Resources: /usr/share/cef/
  • Locale data: /usr/share/cef/locales/

Chromium Patches (42 total)

Network Isolation (12 patches)

disable-gcs-downloads.patch
skip-test-fonts-download.patch
offline-build-config.patch
...

Chromium’s build fetches resources at multiple points. Each download point needs a patch to either:

  • Use pre-packaged alternatives
  • Skip optional components
  • Error clearly rather than hang

C++23 / libc++ Compatibility (8 patches)

Debian sid uses libc++-19 with strict C++23 enforcement. The unique_ptr destructor now requires complete types:

// 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

Patches add forward declarations and reorder includes in:

  • v8/src/heap/ – Garbage collector internals
  • media/gpu/ – Video acceleration
  • ui/gfx/ – Graphics primitives
  • components/viz/ – Compositor

Example fix in 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_;

Compiler Updates (6 patches)

GCC 15 and Clang 19 deprecated various constructs:

fix-aggregate-optional-emplace.patch

std::optional::emplace with aggregate initialization changed behavior. Affected code in IPC serialization.

remove-deprecated-warning-flags.patch

Several -W flags no longer exist in clang-19.

fix-libclang-paths.patch

Clang’s internal header paths changed between versions.

Rust Stable (3 patches)

Chromium uses Rust nightly features. Patches remove:

  • -Z flags (unstable options)
  • Nightly-only crate features
  • Unstable library functions

System Libraries (8 patches)

Patches to prefer system libraries where ABI-compatible:

  • libxcb
  • fontconfig
  • minizip
  • zstd
  • harfbuzz (partial)

Some libraries cannot use system versions due to ABI differences (V8, Skia, ANGLE).

Build Configuration

GN Arguments

The build uses GN (Generate Ninja) with extensive configuration:

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
    ...
]

The use_custom_libcxx Decision

CEF defaults to bundling its own libc++ (use_custom_libcxx=true). This avoids ABI compatibility issues but:

  • Duplicates system library
  • May conflict with applications using system libc++
  • Increases binary size

After extensive testing (builds 108-140), use_custom_libcxx=false works with the C++23 compatibility patches. This is the preferred configuration for Debian integration.

Build Resource Requirements

ResourceRequirement
Disk space~40GB during build
RAM16GB minimum, 32GB+ recommended
CPU time2-24 hours depending on hardware
ParallelismScales well to 16+ cores

Output Structure

Binary Packages

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 and SwiftShader

CEF requires specific GPU abstraction libraries:

ANGLE: OpenGL ES implementation over Vulkan/DirectX. Not interchangeable with Mesa’s EGL—the API is similar but internals differ. Installed in /usr/lib/x86_64-linux-gnu/cef/ with RPATH configuration.

SwiftShader: Software Vulkan implementation for systems without GPU acceleration. The ICD JSON must use absolute paths:

{
    "file_format_version": "1.0.0",
    "ICD": {
        "library_path": "/usr/lib/x86_64-linux-gnu/cef/libvk_swiftshader.so",
        "api_version": "1.1.0"
    }
}

Resource Path Discovery

CEF loads resources early in initialization—before most application callbacks. The library searches relative to libcef.so, not the application binary.

The Symlink Solution

# In /usr/lib/x86_64-linux-gnu/:
icudtl.dat -> ../../share/cef/icudtl.dat
v8_context_snapshot.bin -> ../../share/cef/v8_context_snapshot.bin

This allows subprocesses (renderer, GPU, utility) to find resources when spawned with cef_execute_process(), before any application configuration is applied.

Testing and Validation

Unit Tests

CEF includes ceftests but many tests require network access or graphical display. The packaging runs a subset of offline-capable tests.

Integration Testing

The definitive test is building a real application. stremio-gtk exercises:

  • Offscreen rendering
  • Multiple process types
  • IPC protocols
  • GPU acceleration
  • Resource loading

Issues discovered through stremio-gtk that passed unit tests:

  1. Resource symlinks needed in library directory
  2. ANGLE libraries required (not just preferred)
  3. SwiftShader ICD paths must be absolute
  4. Subprocess command-line switch handling

Maintenance Considerations

Chromium Updates

When Debian updates Chromium, CEF should track:

  1. Obtain matching CEF branch for new Chromium version
  2. Rebase debian/patches/chromium/ onto new sources
  3. Test build and resolve new conflicts
  4. Update version numbers throughout

Security Updates

CEF inherits Chromium’s attack surface. Security updates to Chromium should flow to CEF promptly. The dual-source architecture helps: updating chromium_*.orig.tar.xz and rebuilding catches most issues.

Upstream Coordination

CEF upstream is responsive to packaging concerns. Several patches developed for Debian have been submitted upstream or informed upstream decisions.

Conclusion

CEF packaging requires treating a browser engine as a library—with all the complexity that implies. The dual-source architecture, extensive patch stack, and careful path configuration produce packages that integrate with Debian’s ecosystem rather than fighting it.

The approach documented here should transfer to other distributions with similar policies. The patches are organized by purpose (build system, compatibility, paths) to aid porting.


Packages available at salsa.debian.org/mendezr/chromium-embedded-framework. ITP #915400.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Creative Commons License
Except where otherwise noted, the content on this site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.