Skip to content

uniffi_bindgen code generator and CLI dependencies compiled into liblivekit_uniffi.a static library (+11.5 MB object code) #955

@siddarth-byte

Description

@siddarth-byte

Describe the bug

The uniffi dependency in livekit-uniffi/Cargo.toml (livekit/rust-sdks) unconditionally enables the "cli" feature under [dependencies]. This causes uniffi_bindgen — a build-time code generator — and all its transitive dependencies (reqwest, hyper, ring, clap, serde_json, askama) to be compiled into liblivekit_uniffi.a inside RustLiveKitUniFFI.xcframework.

~11.5 MB of build-tool object code ends up in the static library (26% of the 45 MB .a). These are tools for generating Swift/Kotlin/Python/Ruby bindings from Rust UDL — they have no runtime purpose.

Current Cargo.toml:

[dependencies]
uniffi = { version = "0.30.0", features = ["cli", "scaffolding-ffi-buffer-fns"] }

Because "cli" is unconditional, Cargo's feature unification compiles uniffi_bindgen and its full dependency tree into the static library — even when only the [lib] target is built. UniFFI's own docs recommend running the bindgen binary with --features=uniffi/cli (ref), implying cli should be opt-in — but the current Cargo.toml enables it unconditionally. UniFFI #1482 documents the same underlying issue.

A possible fix is to gate "cli" behind an optional feature with required-features on the [[bin]] target — this is exactly how UniFFI's own Cargo.toml is structured upstream:

[features]
bindgen = ["uniffi/cli"]

[dependencies]
uniffi = { version = "0.30.0", features = ["scaffolding-ffi-buffer-fns"] }

[[bin]]
name = "uniffi-bindgen"
path = "bindgen.rs"
required-features = ["bindgen"]

This way cargo build --lib skips the binary and excludes all CLI dependencies from the static library. The bindgen binary remains available via cargo run --features bindgen --bin uniffi-bindgen.

Introduced in v2.11.0 via PR #822. All versions from v2.11.0 onwards (including v2.12.1) are affected. Root cause is in livekit/rust-sdkslivekit-uniffi/Cargo.toml.

SDK Version

  • livekit/client-sdk-swift v2.12.1 (latest)
  • livekit/livekit-uniffi-xcframework v0.0.5
  • Affected since v2.11.0

iOS/macOS Version

All — build-time packaging issue, not OS-specific.

Xcode Version

Xcode 26.2 / Swift 6.2.3. Issue is Xcode-independent (originates in the Rust Cargo build).

Steps to Reproduce

  1. Download RustLiveKitUniFFI.xcframework from the v0.0.5 release:

    curl -L -O "https://github.com/livekit/livekit-uniffi-xcframework/releases/download/0.0.5/RustLiveKitUniFFI.xcframework.zip"
    unzip RustLiveKitUniFFI.xcframework.zip
  2. Check the static library size:

    BINARY="RustLiveKitUniFFI.xcframework/ios-arm64/liblivekit_uniffi.a"
    ls -lh "$BINARY"   # → 45 MB
  3. Count build-tool symbols that should not be present:

    nm "$BINARY" | grep -c "uniffi_bindgen"    # → 5,163
    nm "$BINARY" | grep -c "reqwest"           # → 566
    nm "$BINARY" | grep -c "clap"              # → 1,041
  4. Measure build-tool object file bloat:

    mkdir objfiles && cd objfiles && ar x "../$BINARY"
    ls -l *.o | grep -iE "(uniffi_bindgen|uniffi_pipeline|reqwest|hyper|ring-|askama|serde_json|clap)" \
      | awk '{sum+=$5} END {printf "%.1f MB\n", sum/1048576}'
    # → 11.5 MB

Expected behavior

The static library should contain only runtime code (livekit_protocol, tokio, jsonwebtoken, prost). Build-time code generators and their dependencies should not be shipped. Gating the "cli" feature should reduce the .a by ~11.5 MB.

Screenshots

N/A — binary analysis issue.

Logs

Sample nm output — note Python and Ruby code generators inside the iOS binary:

__ZN...uniffi_bindgen..bindings..python..pipeline..nodes..Field...
__ZN...uniffi_bindgen..bindings..ruby..gen_ruby..Config...
__ZN...uniffi_bindgen..bindings..swift..gen_swift..Config...

Of the 35,701 executable text symbols, 8,948 (25.1%) come from build-tool dependencies.

Full bloat breakdown (click to expand)
Crate Symbols Object size Purpose
uniffi_bindgen 5,163 7.9 MB Multi-language code generator
uniffi_pipeline 3,474 0.1 MB Binding AST pipeline
ring 2,740 0.5 MB TLS crypto (bindgen dependency)
serde_json 1,278 0.2 MB JSON parsing (bindgen dependency)
hyper 1,170 0.6 MB HTTP implementation (bindgen dep)
clap 1,041 0.9 MB CLI argument parser (bindgen dep)
reqwest 566 1.3 MB HTTP client (bindgen dependency)
askama 146 ~0 MB Template engine (bindgen dependency)
Total ~15,578 11.5 MB None of these are runtime deps

Legitimate runtime dependencies (expected, should remain):

Component Symbols Purpose
livekit_protocol 2,625 Signaling protocol
tokio 2,286 Async runtime
jsonwebtoken 283 Access token validation
prost 211 Protobuf serialization
livekit_api 175 API types

Discovered during binary analysis while integrating LiveKit v2.12.1 into an iOS monorepo. Happy to provide additional data or test a fixed build.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions