Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 93 additions & 18 deletions .github/workflows/build-llamacpp-rocm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,44 @@ jobs:

echo "Cleanup completed successfully"

build-deb:
name: Build .deb Package (${{ matrix.gfx_target }})
needs: [prepare-matrix, build-ubuntu]
runs-on: ubuntu-22.04
if: needs.build-ubuntu.result == 'success' && needs.prepare-matrix.outputs.should_build_ubuntu == 'true'
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.prepare-matrix.outputs.ubuntu_matrix) }}

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install packaging tools
run: sudo apt-get install -y patchelf dpkg-dev

- name: Download Ubuntu build artifact
uses: actions/download-artifact@v4
with:
name: llama-ubuntu-rocm-${{ matrix.gfx_target }}-x64
path: ./binaries

- name: Build .deb package
run: |
chmod +x scripts/create_deb.sh
bash scripts/create_deb.sh \
./binaries \
"0~snapshot~$(date -u +%Y%m%d)" \
"${{ matrix.gfx_target }}" \
./debs

- name: Upload .deb artifact
uses: actions/upload-artifact@v4
with:
name: llama-ubuntu-rocm-${{ matrix.gfx_target }}-x64-deb
path: debs/*.deb
retention-days: 30

test-stx-halo:
runs-on: ${{ matrix.runner }}
needs: [prepare-matrix, build-windows, build-ubuntu]
Expand Down Expand Up @@ -1020,50 +1058,64 @@ jobs:
echo "tag_exists=false" >> $GITHUB_OUTPUT
fi

- name: Create archives for all target artifacts
- name: Install packaging tools
if: steps.check-tag.outputs.tag_exists == 'false'
run: sudo apt-get install -y patchelf dpkg-dev

- name: Create archives and .deb packages for all target artifacts
if: steps.check-tag.outputs.tag_exists == 'false'
run: |
# Parse targets and operating systems from environment
targets="${{ env.GFX_TARGETS }}"
operating_systems="${{ env.OPERATING_SYSTEMS }}"
TAG="${{ steps.generate-tag.outputs.tag }}"

echo "Processing targets: $targets"
echo "Processing operating systems: $operating_systems"
echo "Using release tag: $TAG"


mkdir -p debs

# Create individual archives for each target and OS combination
IFS=',' read -ra TARGET_ARRAY <<< "$targets"
IFS=',' read -ra OS_ARRAY <<< "$operating_systems"

for os in "${OS_ARRAY[@]}"; do
os=$(echo "$os" | xargs) # trim whitespace
for target in "${TARGET_ARRAY[@]}"; do
target=$(echo "$target" | xargs) # trim whitespace

echo "Processing OS: $os, target: $target"

# Use artifact name to find the directory
artifact_name="llama-${os}-rocm-${target}-x64"
artifact_dir="./all-artifacts/${artifact_name}"

# Create final archive with release tag
final_archive_name="llama-${TAG}-${os}-rocm-${target}-x64"

if [ -d "$artifact_dir" ]; then
echo "Creating archive: ${final_archive_name}.zip"
cd "$artifact_dir"
zip -r "../../${final_archive_name}.zip" *
cd ../../

# Build .deb for Ubuntu targets
if [ "$os" = "ubuntu" ]; then
echo "Building .deb for ${target} at version ${TAG}"
bash scripts/create_deb.sh "$artifact_dir" "$TAG" "$target" debs
fi
else
echo "Warning: Artifact directory not found: $artifact_dir"
ls -la ./all-artifacts/
fi
done
done

echo "Created archives:"
ls -la *.zip
echo "Created .deb packages:"
ls -la debs/*.deb || echo "(none)"

- name: Create Release
if: steps.check-tag.outputs.tag_exists == 'false'
Expand All @@ -1086,26 +1138,33 @@ jobs:

# Verify archives exist
ls -la *.zip
# Prepare upload files list

# Prepare upload files list (zips)
upload_files=""
IFS=',' read -ra TARGET_ARRAY <<< "$targets"
IFS=',' read -ra OS_ARRAY <<< "$operating_systems"

for os in "${OS_ARRAY[@]}"; do
os=$(echo "$os" | xargs) # trim whitespace
for target in "${TARGET_ARRAY[@]}"; do
target=$(echo "$target" | xargs) # trim whitespace

final_archive_name="llama-${TAG}-${os}-rocm-${target}-x64"
if [ -f "${final_archive_name}.zip" ]; then
upload_files="${upload_files} ${final_archive_name}.zip"
fi
done
done

echo "Files to upload: $upload_files"


# Collect .deb packages
deb_files=""
for deb in debs/*.deb; do
[ -f "$deb" ] && deb_files="${deb_files} ${deb}"
done

echo "Zip files to upload: $upload_files"
echo "Deb files to upload: $deb_files"

# Create release with GitHub CLI
gh release create "$TAG" \
--title "$TAG" \
Expand All @@ -1116,5 +1175,21 @@ jobs:
**Llama.cpp Commit Hash**: $LLAMACPP_COMMIT_HASH
**Build Date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')

This release includes compiled llama.cpp binaries with ROCm support for multiple GPU targets and operating systems, with all essential ROCm runtime libraries included." \
$upload_files
This release includes compiled llama.cpp binaries with ROCm support for multiple GPU targets and operating systems, with all essential ROCm runtime libraries included.

### Ubuntu — Install via .deb (recommended)

\`\`\`bash
# Replace gfx1151 with your GPU target (see GPU Targets above)
wget https://github.com/lemonade-sdk/llamacpp-rocm/releases/download/${TAG}/llamacpp-rocm-gfx1151_${TAG}_amd64.deb
sudo dpkg -i llamacpp-rocm-gfx1151_${TAG}_amd64.deb
Comment on lines +1184 to +1185
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fix .deb filename in release install snippet

The release notes command uses llamacpp-rocm-gfx1151_${TAG}_amd64.deb, but scripts/create_deb.sh rewrites non-numeric versions like b1234 to 0~b1234 before building, so uploaded assets are named ..._0~${TAG}_amd64.deb. As written, the documented wget/dpkg -i lines point to a non-existent file for every normal release tag and will fail for users following the instructions.

Useful? React with 👍 / 👎.

llama-server -m /path/to/model.gguf -ngl 99
\`\`\`

### Ubuntu — Manual install from .zip

\`\`\`bash
unzip llama-${TAG}-ubuntu-rocm-<target>-x64.zip -d llamacpp-rocm
cd llamacpp-rocm && ./llama-server -m /path/to/model.gguf -ngl 99
\`\`\`" \
$upload_files $deb_files
174 changes: 174 additions & 0 deletions scripts/create_deb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/usr/bin/env bash
# create_deb.sh — Build a .deb package from an extracted llamacpp-rocm zip
#
# Usage:
# create_deb.sh <input_dir> <version> <gpu_target> [output_dir]
#
# Arguments:
# input_dir - Directory containing the extracted zip contents (flat layout)
# version - Package version, e.g. "b1241" or "1241"
# gpu_target - GPU target string, e.g. "gfx1151"
# output_dir - Where to write the .deb (default: current directory)
#
# Requires: patchelf, dpkg-deb, python3
#
# Installed layout:
# /usr/bin/ — llama-* executables and rpc-server
# /usr/lib/llamacpp-rocm/ — shared libraries (RPATH re-patched)
# /usr/lib/llamacpp-rocm/rocblas/ — rocBLAS GPU kernels
# /usr/lib/llamacpp-rocm/hipblaslt/ — hipBLASLt GPU kernels

set -euo pipefail

INPUT_DIR="${1:?Usage: $0 <input_dir> <version> <gpu_target> [output_dir]}"
VERSION="${2:?}"
GPU_TARGET="${3:?}"
OUTPUT_DIR="${4:-.}"

LIB_INSTALL_PATH="/usr/lib/llamacpp-rocm"
# Include GPU target in the package name so multiple GPU variants can coexist
# on disk; Provides/Conflicts ensure only one is active at a time.
PKG_NAME="llamacpp-rocm-${GPU_TARGET}"
# Debian version must start with a digit; prefix non-numeric versions with "0~"
if [[ "${VERSION}" =~ ^[0-9] ]]; then
DEB_VERSION="${VERSION}"
else
DEB_VERSION="0~${VERSION}"
fi
ARCH="amd64"

STAGE_DIR="$(mktemp -d)"
trap 'rm -rf "$STAGE_DIR"' EXIT

PKG_ROOT="${STAGE_DIR}/${PKG_NAME}_${DEB_VERSION}_${ARCH}"
BIN_DIR="${PKG_ROOT}/usr/bin"
LIB_DIR="${PKG_ROOT}${LIB_INSTALL_PATH}"
DEBIAN_DIR="${PKG_ROOT}/DEBIAN"

echo "==> Staging package: ${PKG_NAME} ${DEB_VERSION} (${GPU_TARGET}) → ${OUTPUT_DIR}"

mkdir -p "$BIN_DIR" "$LIB_DIR" "$DEBIAN_DIR"

# ---------------------------------------------------------------------------
# 1. Copy executables → /usr/bin/
# ---------------------------------------------------------------------------
echo "--> Copying executables"
for f in "$INPUT_DIR"/llama-* "$INPUT_DIR/rpc-server"; do
[ -f "$f" ] || continue
# Skip non-ELF files (e.g. zip archives that match llama-* glob)
file -b "$f" | grep -q "^ELF" || continue
install -m 755 "$f" "$BIN_DIR/"
done

# ---------------------------------------------------------------------------
# 2. Copy GPU kernel data directories (preserve tree structure)
# ---------------------------------------------------------------------------
echo "--> Copying rocblas / hipblaslt kernel data"
for kdir in rocblas hipblaslt; do
if [ -d "$INPUT_DIR/$kdir" ]; then
cp -r "$INPUT_DIR/$kdir" "$LIB_DIR/"
fi
done

# ---------------------------------------------------------------------------
# 3. Install shared libraries with proper symlinks (deduplicate)
# In the zip, libfoo.so / libfoo.so.X / libfoo.so.X.Y.Z all contain
# identical bytes. We install only the most-specific version as a real
# file, and create symlinks for the shorter names.
# ---------------------------------------------------------------------------
echo "--> Installing shared libraries (deduplicating soname copies)"
python3 - "$INPUT_DIR" "$LIB_DIR" <<'PYEOF'
import os, sys, re, shutil
from collections import defaultdict

src_dir, dst_dir = sys.argv[1], sys.argv[2]

entries = [f for f in os.listdir(src_dir)
if f.startswith('lib') and '.so' in f
and os.path.isfile(os.path.join(src_dir, f))]

# Group by the base name (everything up to and including ".so")
groups = defaultdict(list)
for name in entries:
base = re.sub(r'(\.so)(\..*)?$', r'\1', name) # e.g. "libfoo.so"
groups[base].append(name)

def version_weight(name, base):
"""Return a sort key: more version components = higher weight = canonical."""
suffix = name[len(base):] # e.g. "" or ".3" or ".3.0.0" or ".23.0git"
return (len(suffix.split('.')), suffix)

for base, members in sorted(groups.items()):
members.sort(key=lambda n: version_weight(n, base), reverse=True)
canonical = members[0] # most-specific version (real file)
shutil.copy2(os.path.join(src_dir, canonical), os.path.join(dst_dir, canonical))
# Create symlinks from shorter names → canonical
for alt in members[1:]:
link_path = os.path.join(dst_dir, alt)
if os.path.exists(link_path):
os.remove(link_path)
os.symlink(canonical, link_path)
print(f" symlink: {alt} -> {canonical}")

PYEOF

# ---------------------------------------------------------------------------
# 4. Re-patch RPATH on executables and shared libraries
# Original build uses $ORIGIN (all files in one flat dir).
# After installation, executables live in /usr/bin/ and libraries in
# /usr/lib/llamacpp-rocm/, so we must update RPATH accordingly.
# ---------------------------------------------------------------------------
echo "--> Re-patching RPATH"

# Executables: /usr/bin/ → find libraries at /usr/lib/llamacpp-rocm/
for f in "$BIN_DIR"/llama-* "$BIN_DIR/rpc-server"; do
[ -f "$f" ] || continue
patchelf --set-rpath "$LIB_INSTALL_PATH" "$f" 2>/dev/null || true
done

# Libraries: they also depend on each other → same RPATH
for f in "$LIB_DIR"/lib*.so*; do
[ -f "$f" ] && [ ! -L "$f" ] || continue
patchelf --set-rpath "$LIB_INSTALL_PATH" "$f" 2>/dev/null || true
done

# ---------------------------------------------------------------------------
# 5. Create DEBIAN/control
# ---------------------------------------------------------------------------
echo "--> Writing DEBIAN/control"

# Estimate installed size (kB)
INSTALLED_KB=$(du -sk "$PKG_ROOT/usr" | cut -f1)

cat > "$DEBIAN_DIR/control" <<CTRL
Package: ${PKG_NAME}
Version: ${DEB_VERSION}
Architecture: ${ARCH}
Maintainer: Lemonade SDK <https://github.com/lemonade-sdk>
Installed-Size: ${INSTALLED_KB}
Depends: libc6 (>= 2.34)
Provides: llamacpp-rocm
Conflicts: llamacpp-rocm
Section: science
Priority: optional
Description: llama.cpp with AMD ROCm GPU acceleration (${GPU_TARGET})
Self-contained llama.cpp build with bundled ROCm 7 runtime libraries.
Supports AMD GPU inference via HIP/ROCm with no separate ROCm installation
required. GPU target: ${GPU_TARGET}.
.
Includes llama-server, llama-cli, llama-quantize, and all other llama.cpp
tools, along with bundled ROCm libraries (hipBLAS, rocBLAS, HIP runtime,
LLVM/Clang JIT) and GPU kernel data for ${GPU_TARGET}.
CTRL

# ---------------------------------------------------------------------------
# 6. Build the .deb
# ---------------------------------------------------------------------------
mkdir -p "$OUTPUT_DIR"
DEB_FILE="${OUTPUT_DIR}/${PKG_NAME}_${DEB_VERSION}_${ARCH}.deb"

echo "--> Building ${DEB_FILE}"
dpkg-deb --build --root-owner-group "$PKG_ROOT" "$DEB_FILE"

echo "==> Done: ${DEB_FILE}"
dpkg-deb --info "$DEB_FILE"