Skip to content

Build LLVM tools for RHEL 8 #2

Build LLVM tools for RHEL 8

Build LLVM tools for RHEL 8 #2

name: Build LLVM tools for RHEL 8
# Builds clang, clang++, clang-format, clang-tidy, lld, llvm-ar, and llvm-nm
# inside a UBI 8.10 container (glibc 2.28) and commits the result to the
# prebuilt/ submodule as 50 MB split parts — matching the existing archive format.
#
# Two-job design:
# build (~90 min) — compiles LLVM, uploads archive as a workflow artifact
# commit (~10 sec) — downloads artifact, clones prebuilt on main, commits, pushes
#
# If only the commit step fails, use "Re-run failed jobs" — it skips the build.
#
# Run manually once per LLVM version bump whenever VERSION changes in
# tools/toolchains/llvm/setup.sh.
#
# Requirements:
# PREBUILT_TOKEN secret — GitHub PAT (classic) with repo scope on
# airgap-devkit/prebuilt. Settings → Secrets and variables → Actions.
on:
workflow_dispatch:
inputs:
llvm_version:
description: 'LLVM version — must match VERSION in tools/toolchains/llvm/setup.sh'
required: true
default: '22.1.4'
# ── Job 1: Build ──────────────────────────────────────────────────────────────
jobs:
build:
name: Build LLVM ${{ inputs.llvm_version }} (UBI 8.10 / glibc 2.28)
runs-on: ubuntu-latest
permissions:
contents: read
env:
LLVM_VERSION: ${{ inputs.llvm_version }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: false
# Everything below runs inside the UBI 8.10 container via docker run so
# the resulting binaries are linked against glibc 2.28 and GCC 8's
# libstdc++, making them compatible with any RHEL 8+ system.
#
# BUILD_SHARED_LIBS=OFF → each tool is self-contained; no libLLVM.so
# shipped, no RPATH complexity. Runtime deps are the standard system libs
# already present on RHEL 8 (glibc, libstdc++, zlib).
#
# cmake 3.11 (UBI 8 default) is too old for LLVM 17+; we download a
# modern cmake binary from cmake.org.
#
# Estimated time: 80-100 minutes on a 2-CPU GitHub Actions runner.
- name: Build LLVM inside UBI 8.10 container
run: |
mkdir -p /tmp/llvm-output
docker run --rm \
--memory=6g \
--cpus="$(nproc)" \
-e LLVM_VERSION \
-v /tmp/llvm-output:/output \
registry.access.redhat.com/ubi8/ubi:8.10 \
bash -euo pipefail -c '
echo "==> System : $(cat /etc/redhat-release)"
echo "==> glibc : $(ldd --version | head -1)"
# ── system packages ────────────────────────────────────────────
dnf install -y --nodocs \
gcc gcc-c++ \
make \
python3 \
git \
xz \
curl \
zlib-devel \
ncurses-devel
echo "==> GCC : $(gcc --version | head -1)"
# Use ninja-build from AppStream if available (faster than make)
dnf install -y ninja-build 2>/dev/null \
&& GENERATOR="Ninja" BUILD_CMD="ninja -j$(nproc)" \
|| { GENERATOR="Unix Makefiles" BUILD_CMD="make -j$(nproc)"; }
echo "==> Build : ${GENERATOR}"
# ── modern cmake ───────────────────────────────────────────────
CMAKE_VER=3.28.6
echo "==> Installing cmake ${CMAKE_VER}..."
curl -fsSL \
"https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-linux-x86_64.sh" \
-o /tmp/cmake.sh
bash /tmp/cmake.sh --skip-license --prefix=/usr/local --exclude-subdir
echo "==> cmake : $(cmake --version | head -1)"
# ── LLVM source ────────────────────────────────────────────────
echo "==> Downloading llvm-project-${LLVM_VERSION}.src.tar.xz..."
curl -fL --retry 3 \
"https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz" \
-o /tmp/llvm-src.tar.xz
echo "==> Extracting..."
mkdir -p /tmp/llvm-src
tar -xJf /tmp/llvm-src.tar.xz --strip-components=1 -C /tmp/llvm-src
# ── configure ─────────────────────────────────────────────────
mkdir -p /tmp/llvm-build
cd /tmp/llvm-build
echo "==> Configuring..."
cmake -G "${GENERATOR}" \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX=/tmp/llvm-install \
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" \
-DLLVM_TARGETS_TO_BUILD="X86" \
-DBUILD_SHARED_LIBS=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_BUILD_DOCS=OFF \
-DLLVM_ENABLE_DOXYGEN=OFF \
-DLLVM_ENABLE_SPHINX=OFF \
-DCLANG_INCLUDE_DOCS=OFF \
-DCLANG_INCLUDE_TESTS=OFF \
/tmp/llvm-src/llvm
# ── build ──────────────────────────────────────────────────────
echo "==> Building (80-100 minutes on a 2-CPU runner)..."
${BUILD_CMD} \
clang \
clang-format \
clang-tidy \
lld \
llvm-ar \
llvm-nm
# ── stage install ─────────────────────────────────────────────
mkdir -p /tmp/llvm-install/bin
for tool in clang clang++ clang-format clang-tidy lld llvm-ar llvm-nm; do
[[ -f "bin/${tool}" ]] && cp "bin/${tool}" /tmp/llvm-install/bin/
done
[[ ! -f /tmp/llvm-install/bin/clang++ ]] \
&& ln -sf clang /tmp/llvm-install/bin/clang++
echo "==> Stripping binaries..."
strip /tmp/llvm-install/bin/* 2>/dev/null || true
echo "==> Binary sizes after strip:"
ls -lh /tmp/llvm-install/bin/
# ── verify ────────────────────────────────────────────────────
echo "==> Verifying binaries execute on glibc 2.28..."
/tmp/llvm-install/bin/clang-format --version
/tmp/llvm-install/bin/clang --version
# ── package ───────────────────────────────────────────────────
echo "==> Packaging..."
cd /tmp/llvm-install
tar -cJf "/output/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz" \
--transform "s|^\.|LLVM-${LLVM_VERSION}-Linux-X64-rhel8|" \
.
echo "==> Archive size: $(du -sh /output/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz | cut -f1)"
'
- name: Upload archive as workflow artifact
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: llvm-rhel8-${{ inputs.llvm_version }}
path: /tmp/llvm-output/LLVM-${{ inputs.llvm_version }}-Linux-X64-rhel8.tar.xz
retention-days: 3
# ── Job 2: Commit ─────────────────────────────────────────────────────────────
# Separate job so a commit/push failure can be re-run in seconds without
# repeating the 90-minute build.
#
# Clones prebuilt directly (not via git submodule update) so it is always
# on the main branch — no detached HEAD, no push ambiguity.
commit:
name: Commit to prebuilt/ and update submodule pointer
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
env:
LLVM_VERSION: ${{ inputs.llvm_version }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout main repo (no submodules)
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
persist-credentials: true
token: ${{ secrets.GITHUB_TOKEN }}
submodules: false
- name: Clone prebuilt on main branch
run: |
git clone \
"https://x-access-token:${{ secrets.PREBUILT_TOKEN }}@github.com/airgap-devkit/prebuilt.git" \
prebuilt-wr
echo "prebuilt HEAD: $(git -C prebuilt-wr rev-parse HEAD)"
echo "prebuilt branch: $(git -C prebuilt-wr branch --show-current)"
- name: Download LLVM archive artifact
uses: actions/download-artifact@v4
with:
name: llvm-rhel8-${{ inputs.llvm_version }}
path: /tmp/llvm-output/
- name: Split archive into 50 MB parts
run: |
DEST="prebuilt-wr/toolchains/llvm/${LLVM_VERSION}"
mkdir -p "${DEST}"
rm -f "${DEST}/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz.part-"*
split -b 50m \
/tmp/llvm-output/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz \
"${DEST}/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz.part-"
echo "Parts:"
ls -lh "${DEST}/LLVM-${LLVM_VERSION}-Linux-X64-rhel8.tar.xz.part-"*
- name: Commit and push prebuilt
working-directory: prebuilt-wr
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add "toolchains/llvm/${LLVM_VERSION}/"
git diff --cached --stat
git commit -m "build: LLVM ${LLVM_VERSION} tools for RHEL 8 (glibc 2.28 / UBI 8.10)"
git push
- name: Update submodule pointer in main repo
run: |
PREBUILT_SHA=$(git -C prebuilt-wr rev-parse HEAD)
echo "Pointing prebuilt submodule → ${PREBUILT_SHA}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git update-index --cacheinfo "160000,${PREBUILT_SHA},prebuilt"
git commit -m "chore: update prebuilt — LLVM ${LLVM_VERSION} RHEL 8 build"
git push