Skip to content

Build libLiteRtLmC

Build libLiteRtLmC #6

name: Build libLiteRtLmC
# Builds the LiteRT-LM C engine as a shared library using Bazel (the
# upstream's primary build system). CMake was too fragile with its
# ExternalProject dep chain; Bazel handles everything natively.
#
# Strategy: build //c:engine as a static archive via Bazel, then manually
# link it (plus deps) into a shared library with the system linker.
on:
workflow_dispatch:
inputs:
litert_lm_tag:
description: "LiteRT-LM upstream tag to build from"
default: "v0.10.2"
type: string
push:
tags: ["lm-build-*"]
env:
LITERT_LM_TAG: ${{ inputs.litert_lm_tag || 'v0.10.2' }}
jobs:
build-linux-x86_64:
name: linux x86_64
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- name: clone litert-lm
run: |
git clone --depth=1 --branch=${{ env.LITERT_LM_TAG }} \
https://github.com/google-ai-edge/litert-lm.git /tmp/litert-lm
# Build the full engine binary to ensure ALL transitive deps are
# materialized as .a files under bazel-bin, then also build the C API
# static archive that we need to whole-archive into our .so.
- name: bazel build
working-directory: /tmp/litert-lm
run: |
bazelisk build //c:engine //runtime/engine:litert_lm_main \
--compilation_mode=opt --copt=-fPIC \
--action_env=CC=gcc --action_env=CXX=g++ \
2>&1 | tail -30
- name: locate built artifacts
id: artifacts
working-directory: /tmp/litert-lm
run: |
echo "=== engine .a ==="
ENGINE_A=$(find -L bazel-bin/c -name 'libengine.a' -o -name 'libc_engine.a' 2>/dev/null | head -1)
if [ -z "$ENGINE_A" ]; then
ENGINE_A=$(find -L bazel-bin/c -name '*.a' 2>/dev/null | head -1)
fi
echo "engine_a=$ENGINE_A" >> $GITHUB_OUTPUT
echo "engine: $ENGINE_A"
ls -la "$ENGINE_A" 2>/dev/null || echo "WARNING: No engine .a found"
echo "=== all .a under bazel-bin (following symlinks) ==="
ALL_COUNT=$(find -L bazel-bin -name '*.a' 2>/dev/null | wc -l)
echo "total: $ALL_COUNT"
find -L bazel-bin -name '*.a' 2>/dev/null | head -30
- name: create version script
run: |
cat > /tmp/litert_lm_exports.map <<'EOF'
VERS_1.0 {
global:
litert_lm_*;
local:
*;
};
EOF
- name: post-link shared library
working-directory: /tmp/litert-lm
run: |
ENGINE_A="${{ steps.artifacts.outputs.engine_a }}"
if [ -z "$ENGINE_A" ] || [ ! -f "$ENGINE_A" ]; then
echo "::error::Engine .a not found — listing what's in bazel-bin/c:"
find -L bazel-bin/c -type f 2>/dev/null | head -20
exit 1
fi
# Collect all static deps from bazel-bin (follow symlinks)
ALL_A=$(find -L bazel-bin -name '*.a' ! -path "$ENGINE_A" 2>/dev/null | sort)
echo "total dep .a files: $(echo "$ALL_A" | wc -l)"
g++ -shared -o /tmp/libLiteRtLmC.so \
-Wl,--version-script=/tmp/litert_lm_exports.map \
-Wl,--whole-archive "$ENGINE_A" -Wl,--no-whole-archive \
$ALL_A \
-Wl,--allow-multiple-definition \
-lstdc++ -lpthread -ldl -lm
echo "=== output ==="
ls -la /tmp/libLiteRtLmC.so
echo "litert_lm_* symbols:"
nm -D --defined-only /tmp/libLiteRtLmC.so | grep 'litert_lm_' | head -10
echo "total litert_lm_* symbols:"
nm -D --defined-only /tmp/libLiteRtLmC.so | grep -c 'litert_lm_'
- uses: actions/upload-artifact@v4
with:
name: libLiteRtLmC-linux-x86_64
path: /tmp/libLiteRtLmC.so
if-no-files-found: error
build-macos-arm64:
name: macos arm64
runs-on: macos-latest
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- name: clone litert-lm
run: |
git clone --depth=1 --branch=${{ env.LITERT_LM_TAG }} \
https://github.com/google-ai-edge/litert-lm.git /tmp/litert-lm
- name: bazel build
working-directory: /tmp/litert-lm
run: |
bazelisk build //c:engine //runtime/engine:litert_lm_main \
--compilation_mode=opt \
2>&1 | tail -30
- name: locate built artifacts
id: artifacts
working-directory: /tmp/litert-lm
run: |
ENGINE_A=$(find -L bazel-bin/c -name 'libengine.a' -o -name 'libc_engine.a' 2>/dev/null | head -1)
if [ -z "$ENGINE_A" ]; then
ENGINE_A=$(find -L bazel-bin/c -name '*.a' 2>/dev/null | head -1)
fi
echo "engine_a=$ENGINE_A" >> $GITHUB_OUTPUT
echo "engine: $ENGINE_A"
ALL_COUNT=$(find -L bazel-bin -name '*.a' 2>/dev/null | wc -l)
echo "total .a: $ALL_COUNT"
- name: create export list
run: |
grep -E 'LITERT_LM_C_API_EXPORT' /tmp/litert-lm/c/engine.h -A1 | \
grep 'litert_lm_' | sed 's/.*\(litert_lm_[a-z_]*\).*/\1/' | \
sed 's/^/_/' | sort -u > /tmp/litert_lm_exports.txt
echo "exports: $(wc -l < /tmp/litert_lm_exports.txt)"
- name: post-link shared library
working-directory: /tmp/litert-lm
run: |
ENGINE_A="${{ steps.artifacts.outputs.engine_a }}"
if [ -z "$ENGINE_A" ] || [ ! -f "$ENGINE_A" ]; then
echo "::error::Engine .a not found"
find -L bazel-bin/c -type f 2>/dev/null | head -20
exit 1
fi
ALL_A=$(find -L bazel-bin -name '*.a' ! -path "$ENGINE_A" 2>/dev/null | sort)
echo "total dep .a files: $(echo "$ALL_A" | wc -l)"
clang++ -dynamiclib -o /tmp/libLiteRtLmC.dylib \
-Wl,-exported_symbols_list,/tmp/litert_lm_exports.txt \
-Wl,-all_load "$ENGINE_A" \
$ALL_A \
-framework Foundation -framework Metal -framework CoreFoundation \
-lstdc++ -lpthread -ldl -lm \
2>&1 | tail -20
echo "=== output ==="
ls -la /tmp/libLiteRtLmC.dylib
echo "litert_lm_* symbols:"
nm -gU /tmp/libLiteRtLmC.dylib | grep -c 'litert_lm_'
- uses: actions/upload-artifact@v4
with:
name: libLiteRtLmC-macos-arm64
path: /tmp/libLiteRtLmC.dylib
if-no-files-found: error