Skip to content

Build LLVM tools (Linux, per-distro) #1

Build LLVM tools (Linux, per-distro)

Build LLVM tools (Linux, per-distro) #1

name: Build LLVM tools (Linux, per-distro)
# Builds clang, clang++, clang-format, clang-tidy, lld, llvm-ar, and llvm-nm
# inside RHEL / Rocky UBI containers — one variant per major (8/9/10) — and
# commits the result to the prebuilt/ submodule as 50 MB split parts, matching
# the existing archive format. Each variant is linked against its own glibc
# (8 → 2.28, 9 → 2.34, 10 → 2.39), so setup.sh can pick the closest match.
#
# Two-stage design:
# build (~90 min each, run in parallel) — compiles LLVM per major, uploads
# each archive as a workflow artifact
# commit (~30 sec) — downloads all artifacts, splits, commits, pushes
#
# If only the commit step fails, use "Re-run failed jobs" — it skips the builds.
#
# 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.8'
# ── Job 1: Build (matrix: one variant per RHEL/Rocky major) ───────────────────
jobs:
build:
name: Build LLVM ${{ inputs.llvm_version }} (UBI ${{ matrix.major }} / glibc ${{ matrix.glibc }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- { major: "8", glibc: "2.28", image: "registry.access.redhat.com/ubi8/ubi:8.10" }
- { major: "9", glibc: "2.34", image: "registry.access.redhat.com/ubi9/ubi:9.5" }
- { major: "10", glibc: "2.39", image: "registry.access.redhat.com/ubi10/ubi:10.0" }
env:
LLVM_VERSION: ${{ inputs.llvm_version }}
RHEL_TAG: rhel${{ matrix.major }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# Everything below runs inside the matrix UBI container via docker run so
# the resulting binaries are linked against that major's glibc and
# libstdc++, making them compatible with that RHEL/Rocky major and newer.
#
# 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 the target (glibc, libstdc++, zlib).
#
# The UBI default cmake is too old for LLVM 17+; we download a modern
# cmake binary from cmake.org inside the container.
#
# Estimated time: 80-100 minutes on a 2-CPU GitHub Actions runner.
- name: Build LLVM inside UBI ${{ matrix.major }} container
run: |
mkdir -p /tmp/llvm-output
docker run --rm \
--memory=6g \
--cpus="$(nproc)" \
-e LLVM_VERSION \
-e RHEL_TAG \
-v /tmp/llvm-output:/output \
${{ matrix.image }} \
bash -euo pipefail -c '
echo "==> System : $(cat /etc/redhat-release 2>/dev/null || cat /etc/os-release | head -1)"
echo "==> glibc : $(ldd --version | head -1)"
echo "==> Variant: ${RHEL_TAG}"
# ── 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 this glibc..."
/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-${RHEL_TAG}.tar.xz" \
--transform "s|^\.|LLVM-${LLVM_VERSION}-Linux-X64-${RHEL_TAG}|" \
.
echo "==> Archive size: $(du -sh /output/LLVM-${LLVM_VERSION}-Linux-X64-${RHEL_TAG}.tar.xz | cut -f1)"
'
- name: Upload archive as workflow artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: llvm-rhel${{ matrix.major }}-${{ inputs.llvm_version }}
path: /tmp/llvm-output/LLVM-${{ inputs.llvm_version }}-Linux-X64-rhel${{ matrix.major }}.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 builds.
#
# 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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 all LLVM archive artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: llvm-rhel*-${{ inputs.llvm_version }}
merge-multiple: true
path: /tmp/llvm-output/
- name: Split each variant into 50 MB parts
run: |
DEST="prebuilt-wr/toolchains/llvm/${LLVM_VERSION}"
mkdir -p "${DEST}"
for tag in rhel8 rhel9 rhel10; do
SRC="/tmp/llvm-output/LLVM-${LLVM_VERSION}-Linux-X64-${tag}.tar.xz"
if [[ ! -f "${SRC}" ]]; then
echo " [--] ${tag}: no artifact (build skipped or failed) — leaving existing parts untouched."
continue
fi
echo " [OK] ${tag}: splitting $(du -sh "${SRC}" | cut -f1)"
rm -f "${DEST}/LLVM-${LLVM_VERSION}-Linux-X64-${tag}.tar.xz.part-"*
split -b 50m "${SRC}" \
"${DEST}/LLVM-${LLVM_VERSION}-Linux-X64-${tag}.tar.xz.part-"
done
echo "Parts staged:"
ls -lh "${DEST}/" | grep -E 'part-aa|\.json' || true
- 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/Rocky 8/9/10 (per-distro glibc builds)"
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 pull --rebase origin main
git update-index --cacheinfo "160000,${PREBUILT_SHA},prebuilt"
git commit -m "chore: update prebuilt — LLVM ${LLVM_VERSION} RHEL/Rocky 8/9/10 builds"
git push