Skip to content

Aded mipsel support to YABB #36

Aded mipsel support to YABB

Aded mipsel support to YABB #36

Workflow file for this run

# SPDX-License-Identifier: 0BSD
# Copyright (c) 2023-2026 Aryan Ameri
#
# =============================================================================
# YABB Release Workflow
# Cross-compiles static binaries for all architectures
# - amd64, arm64, armv7l, ppc64le, loong64, mips64el: Zig cross-compilation
# - riscv64: RISCstar toolchain (Zig's musl has ABI incompatibility)
# - armv5l: Bootlin toolchain (Zig's musl emits NEON instructions)
# Note: armv7l requires NEON (Zig's musl is compiled with NEON optimisations)
#
# Workflow structure:
# 1. setup-nim: Build Nim once and upload as artifact (prevents download race)
# 2. build: 8 parallel jobs download Nim artifact + their toolchain
# 3. release: Collect all binaries and publish
#
# Archives use zstd compression (level 19) for best size
# =============================================================================
name: Release
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
# ==========================================================================
# Setup Nim - Build once, share with all build jobs
# This prevents 8 jobs from racing to download from nim-lang.org
# ==========================================================================
setup-nim:
name: Setup Nim
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Load versions from versions.env
run: |
if [ -f versions.env ]; then
source versions.env
echo "NIM_VERSION=$NIM_VERSION" >> $GITHUB_ENV
echo "Loaded: NIM=$NIM_VERSION"
else
echo "ERROR: versions.env not found"
exit 1
fi
- name: Cache Nim
uses: actions/cache@v4
id: nim-cache
with:
path: ~/nim-${{ env.NIM_VERSION }}
key: nim-${{ env.NIM_VERSION }}-${{ runner.os }}
- name: Install Nim from source
if: steps.nim-cache.outputs.cache-hit != 'true'
run: |
cd ~
echo "Installing Nim ${{ env.NIM_VERSION }} from source..."
curl -fsSL --proto '=https' --tlsv1.2 -LO \
"https://nim-lang.org/download/nim-${{ env.NIM_VERSION }}.tar.xz"
tar xf "nim-${{ env.NIM_VERSION }}.tar.xz"
cd "nim-${{ env.NIM_VERSION }}"
sh build.sh
./bin/nim c -d:release koch
./koch boot -d:release
./koch nimble
cd ..
rm -f "nim-${{ env.NIM_VERSION }}.tar.xz"
echo "Nim ${{ env.NIM_VERSION }} installed successfully"
- name: Create Nim tarball for artifact
run: |
cd ~
tar -cf nim-${{ env.NIM_VERSION }}.tar nim-${{ env.NIM_VERSION }}
ls -lh nim-${{ env.NIM_VERSION }}.tar
- name: Upload Nim as artifact
uses: actions/upload-artifact@v4
with:
name: nim-${{ env.NIM_VERSION }}
path: ~/nim-${{ env.NIM_VERSION }}.tar
retention-days: 1
compression-level: 0
# ==========================================================================
# Build - Cross-compile for all architectures in parallel
# ==========================================================================
build:
name: Build ${{ matrix.arch }}
needs: setup-nim
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
target: x86_64-linux-musl
nimcpu: ""
march: "-march=x86_64 -mno-avx -mno-avx2"
toolchain: zig
- arch: arm64
target: aarch64-linux-musl
nimcpu: "--cpu:arm64"
march: "-march=armv8-a -mtune=cortex_a72"
toolchain: zig
- arch: armv7l
target: arm-linux-musleabihf
nimcpu: "--cpu:arm"
march: "-mcpu=baseline"
toolchain: zig
- arch: riscv64
nimcpu: "--cpu:riscv64"
toolchain: riscstar
- arch: ppc64le
target: powerpc64le-linux-musl
nimcpu: "--cpu:powerpc64"
march: "-mcpu=pwr8 -mtune=pwr9"
toolchain: zig
- arch: loong64
target: loongarch64-linux-musl
nimcpu: "--cpu:loongarch64"
march: "-march=loongarch64"
toolchain: zig
- arch: mips64el
target: mips64el-linux-muslabi64
nimcpu: "--cpu:mips64"
march: "-mcpu=mips64r2"
toolchain: zig
- arch: mipsel
target: mipsel-linux-musleabihf
nimcpu: "--cpu:mips"
march: "-mcpu=mips32r2"
toolchain: zig
- arch: armv5l
nimcpu: "--cpu:arm"
toolchain: bootlin
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Load versions from versions.env
run: |
if [ -f versions.env ]; then
source versions.env
echo "NIM_VERSION=$NIM_VERSION" >> $GITHUB_ENV
echo "ZIG_VERSION=$ZIG_VERSION" >> $GITHUB_ENV
echo "RISCSTAR_VERSION=$RISCSTAR_VERSION" >> $GITHUB_ENV
echo "BOOTLIN_ARMV5_VERSION=$BOOTLIN_ARMV5_VERSION" >> $GITHUB_ENV
echo "Loaded: NIM=$NIM_VERSION, ZIG=$ZIG_VERSION, RISCSTAR=$RISCSTAR_VERSION, BOOTLIN=$BOOTLIN_ARMV5_VERSION"
else
echo "ERROR: versions.env not found"
exit 1
fi
- name: Download Nim artifact
uses: actions/download-artifact@v4
with:
name: nim-${{ env.NIM_VERSION }}
path: /home/runner
- name: Extract Nim
run: |
cd ~
tar -xf nim-${{ env.NIM_VERSION }}.tar
rm nim-${{ env.NIM_VERSION }}.tar
echo "Nim ${{ env.NIM_VERSION }} ready"
- name: Cache toolchain
uses: actions/cache@v4
id: toolchain-cache
with:
path: |
${{ matrix.toolchain == 'zig' && '~/zig' || '' }}
${{ matrix.toolchain == 'riscstar' && '~/riscv64-toolchain' || '' }}
${{ matrix.toolchain == 'bootlin' && '~/armv5-toolchain' || '' }}
key: toolchain-${{ matrix.toolchain }}-${{ matrix.toolchain == 'zig' && env.ZIG_VERSION || matrix.toolchain == 'riscstar' && env.RISCSTAR_VERSION || env.BOOTLIN_ARMV5_VERSION }}
- name: Install Zig
if: matrix.toolchain == 'zig' && steps.toolchain-cache.outputs.cache-hit != 'true'
run: |
cd ~
ZIG_FILE="zig-x86_64-linux-${{ env.ZIG_VERSION }}.tar.xz"
echo "Installing Zig ${{ env.ZIG_VERSION }}..."
# Get expected SHA256 from Zig's official JSON API
echo "Fetching checksum from Zig JSON API..."
EXPECTED_SHA=$(curl -fsSL --proto '=https' --tlsv1.2 \
"https://ziglang.org/download/index.json" | \
jq -r '."${{ env.ZIG_VERSION }}"."x86_64-linux".shasum')
if [ -z "$EXPECTED_SHA" ] || [ "$EXPECTED_SHA" = "null" ]; then
echo "ERROR: Could not fetch checksum for Zig ${{ env.ZIG_VERSION }}"
exit 1
fi
echo "Expected SHA256: $EXPECTED_SHA"
# Download tarball
curl -fsSL --proto '=https' --tlsv1.2 \
-o "${ZIG_FILE}" \
"https://ziglang.org/download/${{ env.ZIG_VERSION }}/${ZIG_FILE}"
# Verify checksum
echo "Verifying SHA256 checksum..."
echo "$EXPECTED_SHA $ZIG_FILE" | sha256sum -c -
# Extract
tar xf "${ZIG_FILE}"
rm -rf zig
mv "zig-x86_64-linux-${{ env.ZIG_VERSION }}" zig
rm -f "${ZIG_FILE}"
echo "Zig ${{ env.ZIG_VERSION }} installed and verified successfully"
- name: Install RISCstar RISC-V64 toolchain
if: matrix.toolchain == 'riscstar' && steps.toolchain-cache.outputs.cache-hit != 'true'
run: |
cd ~
echo "Installing RISCstar RISC-V64 toolchain ${{ env.RISCSTAR_VERSION }}..."
# NOTE: RISCstar does not publish checksums - using TLS for transport security only
curl -fsSL --proto '=https' --tlsv1.2 \
-o "riscstar-toolchain.tar.xz" \
"https://releases.riscstar.com/toolchain/${{ env.RISCSTAR_VERSION }}/riscstar-toolchain-${{ env.RISCSTAR_VERSION }}+qemu-x86_64-riscv64-none-linux-musl.tar.xz"
mkdir -p riscv64-toolchain
tar xf "riscstar-toolchain.tar.xz" -C riscv64-toolchain --strip-components=1
rm -f "riscstar-toolchain.tar.xz"
echo "RISCstar ${{ env.RISCSTAR_VERSION }} installed successfully"
- name: Install Bootlin ARMv5 toolchain
if: matrix.toolchain == 'bootlin' && steps.toolchain-cache.outputs.cache-hit != 'true'
run: |
cd ~
TOOLCHAIN_URL="https://toolchains.bootlin.com/downloads/releases/toolchains/armv5-eabi/tarballs"
TOOLCHAIN_FILE="${{ env.BOOTLIN_ARMV5_VERSION }}.tar.xz"
CHECKSUM_FILE="${{ env.BOOTLIN_ARMV5_VERSION }}.sha256"
echo "Installing Bootlin ARMv5 toolchain ${{ env.BOOTLIN_ARMV5_VERSION }}..."
# Secure download: HTTPS-only, TLS 1.2 minimum, fail on errors
echo "Downloading toolchain and checksum..."
curl -fsSL --proto '=https' --tlsv1.2 \
-o "${TOOLCHAIN_FILE}" \
"${TOOLCHAIN_URL}/${TOOLCHAIN_FILE}"
curl -fsSL --proto '=https' --tlsv1.2 \
-o "${CHECKSUM_FILE}" \
"${TOOLCHAIN_URL}/${CHECKSUM_FILE}"
# Verify SHA256 checksum
echo "Verifying SHA256 checksum..."
sha256sum -c "${CHECKSUM_FILE}"
# Extract toolchain
mkdir -p armv5-toolchain
tar xf "${TOOLCHAIN_FILE}" -C armv5-toolchain --strip-components=1
rm -f "${TOOLCHAIN_FILE}" "${CHECKSUM_FILE}"
echo "Bootlin ${{ env.BOOTLIN_ARMV5_VERSION }} installed and verified successfully"
- name: Add tools to PATH
run: |
echo "$HOME/nim-${{ env.NIM_VERSION }}/bin" >> $GITHUB_PATH
echo "$HOME/.nimble/bin" >> $GITHUB_PATH
case "${{ matrix.toolchain }}" in
zig) echo "$HOME/zig" >> $GITHUB_PATH ;;
riscstar) echo "$HOME/riscv64-toolchain/bin" >> $GITHUB_PATH ;;
bootlin) echo "$HOME/armv5-toolchain/bin" >> $GITHUB_PATH ;;
esac
- name: Install zigcc
if: matrix.toolchain == 'zig'
run: |
if ! command -v zigcc &> /dev/null; then
nimble install -y zigcc
fi
zigcc --version
- name: Verify toolchain (Zig)
if: matrix.toolchain == 'zig'
run: |
nim --version
nimble --version
zig version
zigcc --version
- name: Verify toolchain (RISCstar)
if: matrix.toolchain == 'riscstar'
run: |
nim --version
nimble --version
riscv64-none-linux-musl-gcc --version
- name: Verify toolchain (Bootlin)
if: matrix.toolchain == 'bootlin'
run: |
nim --version
nimble --version
arm-buildroot-linux-musleabi-gcc --version
- name: Install project dependencies
run: |
# Pre-emptively remove any stale nim package from cache
rm -rf ~/.nimble/pkgs2/nim-*
nimble install -d --accept
nimble setup
# Remove again in case nimble reinstalled nim from registry
rm -rf ~/.nimble/pkgs2/nim-*
- name: Build ${{ matrix.arch }} binary (Zig)
if: matrix.toolchain == 'zig'
run: |
echo "Cross-compiling for ${{ matrix.arch }} using Zig..."
nim c -d:release --opt:speed --mm:orc -d:lto \
--cc:clang --clang.exe:zigcc --clang.linkerexe:zigcc \
${{ matrix.nimcpu }} \
--passC:'-target ${{ matrix.target }}' \
--passL:'-target ${{ matrix.target }}' \
--passC:'${{ matrix.march }}' \
--passC:-ffunction-sections --passC:-fdata-sections \
--passC:-flto=thin --passL:-flto=thin \
--passL:-Wl,--gc-sections --passL:-s \
-o:bin/yabb src/yabb.nim
echo "Built: bin/yabb (${{ matrix.arch }})"
- name: Build ${{ matrix.arch }} binary (RISCstar)
if: matrix.toolchain == 'riscstar'
run: |
echo "Cross-compiling for ${{ matrix.arch }} using RISCstar..."
# Create symlinks for Nim cross-compilation (expects riscv64-linux-gnu-*)
cd ~/riscv64-toolchain/bin
for tool in gcc g++ ar ld strip objcopy objdump ranlib nm readelf; do
ln -sf "riscv64-none-linux-musl-$tool" "riscv64-linux-gnu-$tool"
done
cd /home/runner/work/yabb/yabb
# Note: Using -flto=auto for parallel LTO (GCC's default is single-threaded)
nim c -d:release --opt:speed --mm:orc \
${{ matrix.nimcpu }} \
--gcc.exe:riscv64-none-linux-musl-gcc \
--gcc.linkerexe:riscv64-none-linux-musl-gcc \
--passL:-static \
--passC:-O3 --passC:-march=rv64gc \
--passC:-ffunction-sections --passC:-fdata-sections \
--passC:-flto=auto --passL:-flto=auto \
--passL:-Wl,--gc-sections \
-o:bin/yabb src/yabb.nim
riscv64-none-linux-musl-strip -s bin/yabb
echo "Built: bin/yabb (${{ matrix.arch }})"
- name: Build ${{ matrix.arch }} binary (Bootlin)
if: matrix.toolchain == 'bootlin'
run: |
echo "Cross-compiling for ${{ matrix.arch }} using Bootlin..."
# Create symlinks for Nim cross-compilation (--cpu:arm expects arm-linux-gnueabihf-*)
# Bootlin uses wrapper scripts that look for .br_real, so symlink both
cd ~/armv5-toolchain/bin
for tool in gcc g++ ar ld strip objcopy objdump ranlib nm readelf; do
ln -sf "arm-buildroot-linux-musleabi-$tool" "arm-linux-gnueabihf-$tool"
# Also symlink the .br_real files that Bootlin wrappers need
if [ -f "arm-buildroot-linux-musleabi-$tool.br_real" ]; then
ln -sf "arm-buildroot-linux-musleabi-$tool.br_real" "arm-linux-gnueabihf-$tool.br_real"
fi
done
cd /home/runner/work/yabb/yabb
# Note: Using -flto=auto for parallel LTO (GCC's default is single-threaded)
nim c -d:release --opt:speed --mm:orc \
${{ matrix.nimcpu }} \
--gcc.exe:arm-buildroot-linux-musleabi-gcc \
--gcc.linkerexe:arm-buildroot-linux-musleabi-gcc \
--passL:-static \
--passC:-O3 \
--passC:-march=armv5t \
--passC:-mfloat-abi=soft \
--passC:-mabi=aapcs-linux \
--passC:-ffunction-sections \
--passC:-fdata-sections \
--passC:-flto=auto --passL:-flto=auto \
--passL:-Wl,--gc-sections \
-o:bin/yabb src/yabb.nim
arm-buildroot-linux-musleabi-strip --strip-all bin/yabb
echo "Built: bin/yabb (${{ matrix.arch }})"
- name: Verify binary
run: |
echo "=== bin/yabb (${{ matrix.arch }}) ==="
file bin/yabb
ls -lh bin/yabb
if ldd bin/yabb 2>&1 | grep -qE "not a dynamic executable|statically linked"; then
echo "OK: Statically linked"
else
echo "WARNING: May be dynamically linked"
ldd bin/yabb || true
fi
- name: Prepare release archive
run: |
VERSION="${GITHUB_REF_NAME#v}"
ARCHIVE_NAME="yabb-${VERSION}-linux-${{ matrix.arch }}"
mkdir -p "${ARCHIVE_NAME}"
# Copy binary
cp bin/yabb "${ARCHIVE_NAME}/"
# Create shell_completions directory and copy completions
mkdir -p "${ARCHIVE_NAME}/shell_completions"
cp completions/yabb.bash "${ARCHIVE_NAME}/shell_completions/"
cp completions/yabb.fish "${ARCHIVE_NAME}/shell_completions/"
cp completions/yabb.zsh "${ARCHIVE_NAME}/shell_completions/"
# Create scripts directory and copy scripts
mkdir -p "${ARCHIVE_NAME}/scripts"
cp scripts/setup-yabb.sh "${ARCHIVE_NAME}/scripts/"
chmod +x "${ARCHIVE_NAME}/scripts/setup-yabb.sh"
cp scripts/yabb.service "${ARCHIVE_NAME}/scripts/"
cp scripts/yabb.timer "${ARCHIVE_NAME}/scripts/"
# Create symlink for setup-yabb.sh at root
ln -s scripts/setup-yabb.sh "${ARCHIVE_NAME}/setup-yabb.sh"
# Copy and rename sample config
cp config/yabb.toml.example "${ARCHIVE_NAME}/yabb.conf"
# Copy installation instructions
cp Install.txt "${ARCHIVE_NAME}/"
# Create tarball with zstd compression (level 19 = highest standard)
tar -cvf - "${ARCHIVE_NAME}" | zstd -19 -o "${ARCHIVE_NAME}.tar.zst"
# List contents for verification
echo ""
echo "Archive contents:"
zstd -d -c "${ARCHIVE_NAME}.tar.zst" | tar -tvf -
# Output for subsequent steps
echo "ARCHIVE_NAME=${ARCHIVE_NAME}" >> $GITHUB_ENV
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARCHIVE_NAME }}
path: ${{ env.ARCHIVE_NAME }}.tar.zst
retention-days: 1
# ==========================================================================
# Release - Collect all binaries and publish
# ==========================================================================
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: yabb-*
merge-multiple: true
- name: Generate checksums
run: |
cd artifacts
sha256sum *.tar.zst > SHA256SUMS
echo "Generated checksums:"
cat SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/*.tar.zst
artifacts/SHA256SUMS
generate_release_notes: true
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}