Add ARM64 Linux build infrastructure for Shardok (#4990)

* Add ARM64 Linux build infrastructure for Shardok

Infrastructure to support cross-compiling Shardok for ARM64 Linux,
enabling deployment to Hetzner ARM instances (CAX41) for on-demand
compute.

Changes:
- Add linux_arm64 platform definition
- Add LLVM toolchain for ARM64 cross-compilation
- Add ARM64 sysroot placeholder (needs workflow run to populate)
- Add ARM64 busybox for container health checks
- Add Ubuntu 24.04 ARM64 base image
- Add Shardok ARM64 container image targets
- Update sysroot build workflow to support ARM64

Next steps:
1. Run "Build Linux Sysroot" workflow with architecture=arm64
2. Update MODULE.bazel with generated sysroot SHA
3. Build and push ARM64 container

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update ARM64 sysroot SHA from workflow build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix ARM64 container build issues

- Use linux/arm64/v8 platform string (rules_oci requires variant suffix)
- Remove busybox_layer_arm64 due to busybox.net SSL certificate issues
- Health checks can be added later using an alternative busybox source

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add GitHub Actions workflow for ARM64 Shardok build

Builds and pushes ARM64 container image to GitHub Container Registry
for deployment on Hetzner ARM instances (CAX41).

Triggered on:
- Push to main (when Shardok-related files change)
- Manual workflow_dispatch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-02 15:08:51 -08:00
committed by GitHub
co-authored by Claude Opus 4.5
parent 06f6ba8340
commit 584ccbadcb
8 changed files with 502 additions and 11 deletions
+75 -4
View File
@@ -8,12 +8,22 @@ on:
required: true
default: 'v2'
type: string
architecture:
description: 'Target architecture'
required: true
default: 'amd64'
type: choice
options:
- amd64
- arm64
- both
permissions:
contents: read
jobs:
build-sysroot:
build-sysroot-amd64:
if: ${{ inputs.architecture == 'amd64' || inputs.architecture == 'both' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@@ -25,7 +35,7 @@ jobs:
- name: Upload sysroot artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu-noble-sysroot
name: ubuntu-noble-sysroot-amd64
path: tools/sysroot/output/
- name: Install AWS CLI
@@ -41,7 +51,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.SECRET_KEY }}
run: |
# Upload sysroot tarball to DO Spaces (using eagle0-windows bucket, same as other workflows)
# Upload sysroot tarball to DO Spaces
aws s3 cp tools/sysroot/output/ubuntu_noble_amd64_sysroot.tar.xz \
s3://eagle0-windows/sysroot/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz \
--endpoint-url https://sfo3.digitaloceanspaces.com \
@@ -54,7 +64,7 @@ jobs:
--acl public-read
echo ""
echo "=== Sysroot uploaded ==="
echo "=== AMD64 Sysroot uploaded ==="
echo "URL: https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz"
echo "SHA256: $(cat tools/sysroot/output/ubuntu_noble_amd64_sysroot.sha256)"
echo ""
@@ -64,3 +74,64 @@ jobs:
echo " sha256 = \"$(cat tools/sysroot/output/ubuntu_noble_amd64_sysroot.sha256)\","
echo " urls = [\"https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/${{ inputs.version }}/ubuntu_noble_amd64_sysroot.tar.xz\"],"
echo ")"
build-sysroot-arm64:
if: ${{ inputs.architecture == 'arm64' || inputs.architecture == 'both' }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU for ARM64 emulation
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build ARM64 sysroot
run: ./tools/sysroot/build_sysroot_arm64.sh
- name: Upload sysroot artifact
uses: actions/upload-artifact@v4
with:
name: ubuntu-noble-sysroot-arm64
path: tools/sysroot/output/
- name: Install AWS CLI
run: |
if ! command -v aws &> /dev/null; then
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -q awscliv2.zip
sudo ./aws/install
fi
- name: Upload to DigitalOcean Spaces
env:
AWS_ACCESS_KEY_ID: ${{ secrets.ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.SECRET_KEY }}
run: |
# Upload sysroot tarball to DO Spaces
aws s3 cp tools/sysroot/output/ubuntu_noble_arm64_sysroot.tar.xz \
s3://eagle0-windows/sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
# Upload sha256 file
aws s3 cp tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256 \
s3://eagle0-windows/sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.sha256 \
--endpoint-url https://sfo3.digitaloceanspaces.com \
--acl public-read
echo ""
echo "=== ARM64 Sysroot uploaded ==="
echo "URL: https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz"
echo "SHA256: $(cat tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256)"
echo ""
echo "Update MODULE.bazel with:"
echo "sysroot("
echo " name = \"linux_sysroot_arm64\","
echo " sha256 = \"$(cat tools/sysroot/output/ubuntu_noble_arm64_sysroot.sha256)\","
echo " urls = [\"https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/${{ inputs.version }}/ubuntu_noble_arm64_sysroot.tar.xz\"],"
echo ")"
+159
View File
@@ -0,0 +1,159 @@
name: Shardok ARM64 Build and Push
on:
push:
branches: [ "main" ]
paths:
- 'src/main/cpp/**'
- 'src/main/protobuf/net/eagle0/shardok/**'
- 'src/main/protobuf/net/eagle0/common/**'
- 'src/main/resources/net/eagle0/shardok/**'
- 'ci/BUILD.bazel'
- 'MODULE.bazel'
- '.github/workflows/shardok_arm64_build.yml'
workflow_dispatch:
inputs:
push_images:
description: 'Push images to container registry'
required: true
default: 'true'
type: boolean
permissions:
contents: read
packages: write
jobs:
build-shardok-arm64:
runs-on: self-hosted
outputs:
image_tag: ${{ steps.push-shardok.outputs.image_tag }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
lfs: false
- name: Build Shardok ARM64 binary (cross-compile for Linux ARM64)
run: |
set -ex
echo "=== Building shardok-server binary for linux-aarch64 ==="
bazel build \
--platforms=//:linux_arm64 \
--extra_toolchains=@llvm_toolchain_linux_arm64//:all \
//src/main/cpp/net/eagle0/shardok:shardok-server
LINUX_BIN="bazel-bin/src/main/cpp/net/eagle0/shardok/shardok-server"
echo "=== Checking binary at: $LINUX_BIN ==="
if [ ! -f "$LINUX_BIN" ]; then
echo "ERROR: Binary not found at $LINUX_BIN"
exit 1
fi
# Verify it's ELF (Linux) not Mach-O (macOS)
echo "=== Verifying binary format ==="
MAGIC=$(head -c 4 "$LINUX_BIN" | xxd -p)
echo "Binary magic bytes: $MAGIC"
if [ "$MAGIC" = "7f454c46" ]; then
echo "SUCCESS: Binary is ELF format (Linux)"
# Check if it's ARM64 (e_machine = 0xB7 = 183 for aarch64)
E_MACHINE=$(od -An -j18 -N2 -tx2 "$LINUX_BIN" | tr -d ' ')
echo "ELF e_machine: $E_MACHINE"
if [ "$E_MACHINE" = "b700" ]; then
echo "SUCCESS: Binary is ARM64 (aarch64)"
else
echo "WARNING: Binary e_machine is $E_MACHINE (expected b700 for aarch64)"
fi
elif [ "$MAGIC" = "cfaeedfe" ] || [ "$MAGIC" = "cffaedfe" ]; then
echo "ERROR: Binary is Mach-O format (macOS) - cross-compilation failed!"
exit 1
else
echo "WARNING: Unknown binary format: $MAGIC"
file "$LINUX_BIN" || true
fi
- name: Build Shardok ARM64 Docker image
id: build-shardok
run: |
set -ex
bazel build \
--platforms=//:linux_arm64 \
--extra_toolchains=@llvm_toolchain_linux_arm64//:all \
//ci:shardok_server_image_arm64
IMAGE_PATH=$(readlink -f bazel-bin/ci/shardok_server_image_arm64)
echo "Image path: $IMAGE_PATH"
echo "image_path=$IMAGE_PATH" >> $GITHUB_OUTPUT
# Verify the binary inside the tar layer is ARM64 ELF
echo "=== Verifying binary in image tar ==="
BINARY_TAR="bazel-bin/ci/shardok_binary_layer_arm64.tar"
if [ -f "$BINARY_TAR" ]; then
echo "Checking binary in $BINARY_TAR"
MAGIC=$(tar -xOf "$BINARY_TAR" app/shardok-server 2>/dev/null | head -c 4 | xxd -p)
echo "Binary magic in tar: $MAGIC"
if [ "$MAGIC" = "7f454c46" ]; then
echo "SUCCESS: Binary in tar is ELF format (Linux)"
else
echo "ERROR: Binary in tar is NOT ELF format!"
exit 1
fi
else
echo "WARNING: Could not find $BINARY_TAR"
fi
- name: Login to GitHub Container Registry
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "$GHCR_TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push Shardok ARM64 image to GHCR
id: push-shardok
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.push_images == 'true')
run: |
set -ex
CROSS_IMAGE="${{ steps.build-shardok.outputs.image_path }}"
echo "Using cross-compiled image: $CROSS_IMAGE"
if [ -z "$CROSS_IMAGE" ] || [ ! -d "$CROSS_IMAGE" ]; then
echo "ERROR: Cross-compiled image not found at: $CROSS_IMAGE"
exit 1
fi
# Build a push target to get crane in runfiles
bazel build //ci:eagle_server_push
# Find the Darwin crane binary
RUNFILES="bazel-bin/ci/push_eagle_server_push.sh.runfiles"
CRANE=$(find "$RUNFILES" -path "*darwin*" -name crane 2>/dev/null | head -1)
if [ -z "$CRANE" ]; then
CRANE=$(find "$RUNFILES" -name crane 2>/dev/null | head -1)
fi
if [ -z "$CRANE" ] || [ ! -e "$CRANE" ]; then
echo "ERROR: crane not found"
exit 1
fi
echo "Using crane: $CRANE"
# Push with SHA tag
GIT_SHA=$(git rev-parse --short=8 HEAD)
IMAGE_TAG="ghcr.io/nolen777/shardok-server:${GIT_SHA}"
echo "Pushing shardok ARM64 image: $IMAGE_TAG"
$CRANE push "$CROSS_IMAGE" "$IMAGE_TAG"
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
# Also update :latest-arm64 for convenience
echo "Copying to :latest-arm64 tag"
$CRANE copy "$IMAGE_TAG" "ghcr.io/nolen777/shardok-server:latest-arm64"
echo "=== Push complete ==="
echo "Image: $IMAGE_TAG"
echo "Also tagged as: ghcr.io/nolen777/shardok-server:latest-arm64"
+9
View File
@@ -12,6 +12,15 @@ platform(
],
)
# Platform for cross-compiling to Linux ARM64
platform(
name = "linux_arm64",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
],
)
gazelle(name = "gazelle")
# gazelle:proto file
+39 -5
View File
@@ -57,25 +57,47 @@ llvm.toolchain(
llvm_version = "20.1.2",
)
# Linux sysroot for cross-compilation (Chromium's Debian sysroot)
# Linux x86_64 sysroot for cross-compilation
llvm.sysroot(
name = "llvm_toolchain_linux",
label = "@linux_sysroot//sysroot",
targets = ["linux-x86_64"],
)
use_repo(llvm, "llvm_toolchain", "llvm_toolchain_linux")
# Cross-compilation toolchain (macOS -> Linux ARM64)
llvm.toolchain(
name = "llvm_toolchain_linux_arm64",
llvm_version = "20.1.2",
)
# Download the Linux sysroot (Ubuntu 24.04 Noble for C++23 support)
# Linux ARM64 sysroot for cross-compilation
llvm.sysroot(
name = "llvm_toolchain_linux_arm64",
label = "@linux_sysroot_arm64//sysroot",
targets = ["linux-aarch64"],
)
use_repo(llvm, "llvm_toolchain", "llvm_toolchain_linux", "llvm_toolchain_linux_arm64")
# Download the Linux sysroots (Ubuntu 24.04 Noble for C++23 support)
# Built by: .github/workflows/build_sysroot.yml
# To rebuild: Run the "Build Linux Sysroot" workflow with a new version, then update sha256 and URL
sysroot = use_repo_rule("@toolchains_llvm//toolchain:sysroot.bzl", "sysroot")
# x86_64 sysroot
sysroot(
name = "linux_sysroot",
sha256 = "a06475004fe8003ae7ccb4fe1d5511feb9b27cce4a8826eb1dfd686ed83f3dba",
urls = ["https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/v3/ubuntu_noble_amd64_sysroot.tar.xz"],
)
# ARM64 sysroot
sysroot(
name = "linux_sysroot_arm64",
sha256 = "87469137737e09bc73855007dab835477eb10a7b3ce3f725f93f64e25747f3f9",
urls = ["https://eagle0-windows.sfo3.digitaloceanspaces.com/sysroot/v4/ubuntu_noble_arm64_sysroot.tar.xz"],
)
#
# Language Support - Go
#
@@ -144,7 +166,10 @@ oci.pull(
oci.pull(
name = "ubuntu_24_04",
image = "docker.io/library/ubuntu",
platforms = ["linux/amd64"],
platforms = [
"linux/amd64",
"linux/arm64/v8",
],
tag = "24.04",
)
@@ -155,7 +180,7 @@ oci.pull(
platforms = ["linux/amd64"],
tag = "3.21",
)
use_repo(oci, "alpine_linux", "alpine_linux_linux_amd64", "eclipse_temurin_17", "eclipse_temurin_17_linux_amd64", "ubuntu_24_04", "ubuntu_24_04_linux_amd64")
use_repo(oci, "alpine_linux", "alpine_linux_linux_amd64", "eclipse_temurin_17", "eclipse_temurin_17_linux_amd64", "ubuntu_24_04", "ubuntu_24_04_linux_amd64", "ubuntu_24_04_linux_arm64_v8")
#
# Java/Scala Dependencies
@@ -275,6 +300,14 @@ http_file(
executable = True,
)
http_file(
name = "busybox_aarch64",
sha256 = "141adb1b625a6f44c4b114f76b4387b4ea4f7ab802b88eb40e0d2f6adcccb1c3",
urls = ["https://busybox.net/downloads/binaries/1.35.0-aarch64-linux-musl/busybox"],
downloaded_file_path = "busybox",
executable = True,
)
#
# Toolchain Registration
#
@@ -288,5 +321,6 @@ register_toolchains(
register_toolchains(
"@llvm_toolchain//:all",
"@llvm_toolchain_linux//:all",
"@llvm_toolchain_linux_arm64//:all",
dev_dependency = True,
)
+18 -2
View File
@@ -1293,7 +1293,7 @@
"@@rules_oci~//oci:extensions.bzl%oci": {
"general": {
"bzlTransitiveDigest": "FaY+7xb13bB3hmxqwAWaGp3Tf3Q4Nfdlr+F38CP5mcg=",
"usagesDigest": "uqsqMpE+yaVuTByR2rIA2v1Vkm1JwwuN6nbXOo1W9G0=",
"usagesDigest": "39yHQmifPoGf+JSYpnQSnJGugxbrFVge/+ENmUiqZ6M=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -1343,6 +1343,20 @@
"bazel_tags": []
}
},
"ubuntu_24_04_linux_arm64_v8": {
"bzlFile": "@@rules_oci~//oci/private:pull.bzl",
"ruleClassName": "oci_pull",
"attributes": {
"www_authenticate_challenges": {},
"scheme": "https",
"registry": "index.docker.io",
"repository": "library/ubuntu",
"identifier": "24.04",
"platform": "linux/arm64/v8",
"target_name": "ubuntu_24_04_linux_arm64_v8",
"bazel_tags": []
}
},
"ubuntu_24_04": {
"bzlFile": "@@rules_oci~//oci/private:pull.bzl",
"ruleClassName": "oci_alias",
@@ -1354,7 +1368,8 @@
"repository": "library/ubuntu",
"identifier": "24.04",
"platforms": {
"@@platforms//cpu:x86_64": "@ubuntu_24_04_linux_amd64"
"@@platforms//cpu:x86_64": "@ubuntu_24_04_linux_amd64",
"@@platforms//cpu:arm64": "@ubuntu_24_04_linux_arm64_v8"
},
"bzlmod_repository": "ubuntu_24_04",
"reproducible": true
@@ -1528,6 +1543,7 @@
"eclipse_temurin_17_linux_amd64",
"ubuntu_24_04",
"ubuntu_24_04_linux_amd64",
"ubuntu_24_04_linux_arm64_v8",
"alpine_linux",
"alpine_linux_linux_amd64"
],
+59
View File
@@ -17,6 +17,18 @@ pkg_tar(
},
)
pkg_tar(
name = "busybox_layer_arm64",
srcs = ["@busybox_aarch64//file"],
package_dir = "/usr/local/bin",
remap_paths = {
"file/busybox": "busybox",
},
symlinks = {
"/usr/local/bin/nc": "busybox",
},
)
#
# Eagle Server Docker Image
#
@@ -155,6 +167,53 @@ oci_push(
repository = "registry.digitalocean.com/eagle0/shardok-server",
)
#
# Shardok Server ARM64 Docker Image (for Hetzner on-demand compute)
#
# Build: bazel build //ci:shardok_server_image_arm64 --platforms=//:linux_arm64 --extra_toolchains=@llvm_toolchain_linux_arm64//:all
# Load: bazel run //ci:shardok_server_load_arm64
# Push: bazel run //ci:shardok_server_push_arm64
#
# Package the Shardok binary (ARM64 version - must be built with --platforms=//:linux_arm64)
pkg_tar(
name = "shardok_binary_layer_arm64",
srcs = ["//src/main/cpp/net/eagle0/shardok:shardok-server"],
package_dir = "/app",
)
oci_image(
name = "shardok_server_image_arm64",
base = "@ubuntu_24_04_linux_arm64_v8",
entrypoint = ["/app/shardok-server"],
exposed_ports = [
"40042/tcp",
"40052/tcp",
],
tars = [
# Note: busybox_layer_arm64 omitted - busybox.net has SSL issues
# Health checks can use the shardok-server binary itself or be added later
":shardok_binary_layer_arm64",
":shardok_resources_layer",
":shardok_maps_layer",
],
workdir = "/app",
)
# Load into Docker locally (ARM64): bazel run //ci:shardok_server_load_arm64
oci_load(
name = "shardok_server_load_arm64",
image = ":shardok_server_image_arm64",
repo_tags = ["eagle0/shardok-server:latest-arm64"],
)
# Push to GitHub Container Registry (for Hetzner deployment)
oci_push(
name = "shardok_server_push_arm64",
image = ":shardok_server_image_arm64",
repository = "ghcr.io/nolen777/shardok-server",
)
#
# Admin Server Docker Image (Go)
#
+48
View File
@@ -0,0 +1,48 @@
# Dockerfile to create Ubuntu 24.04 (Noble) sysroot for cross-compilation to ARM64
#
# This creates a minimal sysroot containing headers and libraries needed
# to cross-compile C++ code targeting Linux ARM64 (aarch64).
#
# Key requirement: Clang needs to find GCC's installation to locate libstdc++
# headers. It looks for /usr/lib/gcc/<triple>/<version>/ in the sysroot.
FROM ubuntu:24.04
# Install the packages we need in the sysroot
# These provide headers and libraries for linking
RUN apt-get update && apt-get install -y --no-install-recommends \
# C++ standard library with C++23 support (GCC 13)
libstdc++-13-dev \
# GCC 13 (needed for Clang to find C++ headers via GCC detection)
gcc-13 \
g++-13 \
# C library
libc6-dev \
# Linux kernel headers
linux-libc-dev \
# Common dependencies
zlib1g-dev \
libssl-dev \
# Clean up
&& rm -rf /var/lib/apt/lists/*
# Create sysroot directory
RUN mkdir -p /sysroot
# Copy the essential directories for cross-compilation
# Note: We preserve the exact directory structure that clang expects
RUN cp -a /usr/include /sysroot/ && \
cp -a /usr/lib/aarch64-linux-gnu /sysroot/usr_lib && \
mkdir -p /sysroot/lib && \
cp -a /lib/aarch64-linux-gnu /sysroot/lib/ && \
# Copy GCC installation directory - Clang uses this to find libstdc++ headers
# Copy everything except .so files (Bazel can't handle them as inputs)
mkdir -p /sysroot/usr/lib/gcc && \
cp -a /usr/lib/gcc/aarch64-linux-gnu /sysroot/usr/lib/gcc/ && \
find /sysroot/usr/lib/gcc -name "*.so" -delete && \
find /sysroot/usr/lib/gcc -name "*.so.*" -delete && \
# Create symlinks for clang's target triple (aarch64-unknown-linux-gnu -> aarch64-linux-gnu)
# Clang uses --target=aarch64-unknown-linux-gnu but Ubuntu uses aarch64-linux-gnu
ln -s aarch64-linux-gnu /sysroot/usr/lib/gcc/aarch64-unknown-linux-gnu && \
ln -s aarch64-linux-gnu /sysroot/lib/aarch64-unknown-linux-gnu && \
ln -s aarch64-linux-gnu /sysroot/include/aarch64-unknown-linux-gnu
+95
View File
@@ -0,0 +1,95 @@
#!/bin/bash
#
# Build an Ubuntu 24.04 (Noble) ARM64 sysroot for cross-compilation
#
# This script:
# 1. Builds a Docker image with the required packages (using QEMU for ARM64 emulation)
# 2. Extracts the sysroot files
# 3. Creates a tarball with proper structure for toolchains_llvm
# 4. Computes the SHA256 hash
#
# Output: ubuntu_noble_arm64_sysroot.tar.xz and its SHA256
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUTPUT_DIR="${SCRIPT_DIR}/output"
SYSROOT_NAME="ubuntu_noble_arm64_sysroot"
echo "=== Building Ubuntu 24.04 ARM64 sysroot ==="
# Create output directory
mkdir -p "${OUTPUT_DIR}"
# Enable Docker buildx for ARM64 emulation
docker buildx create --use --name arm64builder 2>/dev/null || docker buildx use arm64builder
# Build the Docker image for ARM64
echo "Building Docker image for ARM64 (this may take a while with QEMU emulation)..."
docker buildx build --platform linux/arm64 -t sysroot-builder-arm64 -f "${SCRIPT_DIR}/Dockerfile.arm64" --load "${SCRIPT_DIR}"
# Create a container and extract the sysroot
echo "Extracting sysroot..."
CONTAINER_ID=$(docker create --platform linux/arm64 sysroot-builder-arm64)
# Create the sysroot structure expected by toolchains_llvm
TEMP_DIR=$(mktemp -d)
SYSROOT_DIR="${TEMP_DIR}/sysroot"
mkdir -p "${SYSROOT_DIR}/usr/lib" "${SYSROOT_DIR}/lib"
# Extract files from container
docker cp "${CONTAINER_ID}:/sysroot/include" "${SYSROOT_DIR}/usr/"
docker cp "${CONTAINER_ID}:/sysroot/usr_lib" "${SYSROOT_DIR}/usr/lib/aarch64-linux-gnu"
docker cp "${CONTAINER_ID}:/sysroot/lib/aarch64-linux-gnu" "${SYSROOT_DIR}/lib/"
# Copy GCC installation directory - Clang uses this to find libstdc++ headers
# The path /usr/lib/gcc/aarch64-linux-gnu/13 is where clang looks for GCC
docker cp "${CONTAINER_ID}:/sysroot/usr/lib/gcc" "${SYSROOT_DIR}/usr/lib/"
# Create symlinks for clang's target triple (aarch64-unknown-linux-gnu -> aarch64-linux-gnu)
# Note: docker cp preserves symlinks within directories, but we need to explicitly handle
# symlinks at the top level of copied directories
ln -sf aarch64-linux-gnu "${SYSROOT_DIR}/lib/aarch64-unknown-linux-gnu"
# Also need lib64 symlink for some builds
ln -sf lib "${SYSROOT_DIR}/lib64"
# Create .so symlinks for shared libraries that only have versioned names
# The linker looks for libfoo.so but Ubuntu only has libfoo.so.1
ln -sf libgcc_s.so.1 "${SYSROOT_DIR}/lib/aarch64-linux-gnu/libgcc_s.so"
# Add dynamic linker symlink in lib/ since libc.so linker script
# references /lib/ld-linux-aarch64.so.1 as an absolute path
ln -sf aarch64-linux-gnu/ld-linux-aarch64.so.1 "${SYSROOT_DIR}/lib/ld-linux-aarch64.so.1"
# Create the tarball
# Note: We create the tarball from inside the sysroot directory so the contents
# are at the root level (usr/, lib/, etc.) rather than wrapped in sysroot/
echo "Creating tarball..."
cd "${SYSROOT_DIR}"
tar -cJf "${OUTPUT_DIR}/${SYSROOT_NAME}.tar.xz" .
echo "Verifying tarball structure..."
tar -tf "${OUTPUT_DIR}/${SYSROOT_NAME}.tar.xz" | head -20 || true
# Compute SHA256
echo "Computing SHA256..."
SHA256=$(shasum -a 256 "${OUTPUT_DIR}/${SYSROOT_NAME}.tar.xz" | cut -d' ' -f1)
echo "${SHA256}" > "${OUTPUT_DIR}/${SYSROOT_NAME}.sha256"
# Cleanup
docker rm "${CONTAINER_ID}" > /dev/null
rm -rf "${TEMP_DIR}"
echo ""
echo "=== ARM64 Sysroot build complete ==="
echo "Tarball: ${OUTPUT_DIR}/${SYSROOT_NAME}.tar.xz"
echo "SHA256: ${SHA256}"
echo ""
echo "To use this sysroot, upload it somewhere accessible and update MODULE.bazel:"
echo ""
echo "sysroot("
echo " name = \"linux_sysroot_arm64\","
echo " sha256 = \"${SHA256}\","
echo " urls = [\"<your-upload-url>/${SYSROOT_NAME}.tar.xz\"],"
echo ")"