Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8587093
Fix modular Android packaging and snapshot publication
bernardladenthin Oct 23, 2026
3f54dcf
Share configurable release asset checks and snapshot publication
bernardladenthin Oct 23, 2026
43e1a59
Fix mirror token selection and release checkout credentials
bernardladenthin Oct 23, 2026
672a923
docs: remove stale TODOs and record confirmed RPC release blocker
bernardladenthin Oct 23, 2026
dced2b7
fix: preserve RPC peer binding and make shutdown cancellable
bernardladenthin Oct 24, 2026
4c73665
docs: add CodeRabbit badge
bernardladenthin Oct 24, 2026
f86a810
docs: use repository-specific CodeRabbit reviews badge
bernardladenthin Oct 24, 2026
9522ab5
Preserve runtime license notices and document scoped FOSSA review
bernardladenthin Oct 24, 2026
8c1a941
Record SevenForums assessment as unresolved without asserting a relea…
bernardladenthin Oct 24, 2026
2869132
Fix Android AAR namespaces and pipefail-safe validation
bernardladenthin Oct 24, 2026
2ecbd98
Preserve COPYRIGHT documents in shared runtime license collector
bernardladenthin Oct 24, 2026
4b9a789
Address review feedback on docs, badge links and signature subtests
bernardladenthin Oct 24, 2026
dc1e23f
Gate signed publications locally and decouple Central and GitHub uploads
bernardladenthin Oct 24, 2026
45fafcb
Use JDK 25 for builds while preserving all artifact and test targets
bernardladenthin Oct 24, 2026
a725ca6
Run dependency submission with the repository build JDK
bernardladenthin Oct 24, 2026
6af2074
Unify publication finalization and independent provenance attestation
bernardladenthin Oct 24, 2026
222659b
Move to sccache 0.19.0 and cache nvcc again
bernardladenthin Oct 25, 2026
aaa8d07
Check publications against their tag, fix the snapshot staging, and a…
bernardladenthin Oct 25, 2026
3e30481
Upgrade llama.cpp from b11827 to b11881
claude Oct 25, 2026
212f4a7
Run the Windows C++ tests, and correct what the delta review found
bernardladenthin Oct 25, 2026
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
11 changes: 11 additions & 0 deletions .fossa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
# SPDX-License-Identifier: MIT OR Apache-2.0

version: 3
project:
locator: git+github.com/bernardladenthin/java-llama.cpp

# No fixed revision.branch: preserve the actual PR/main branch and commit.
# No license overrides or scope exclusions: test and runtime findings stay visible.
maven:
scope-exclude: []
49 changes: 49 additions & 0 deletions .github/actions/finalize-publication/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
# SPDX-License-Identifier: MIT OR Apache-2.0

name: Finalize signed publication
description: Collect, verify and secure the complete signed package before any publication consumer runs.
inputs:
channel:
description: Publication channel (snapshot or release).
required: true
maven-assets:
description: Optional directory containing Maven outputs preserved before a rebuild.
required: false
default: ''
outputs:
complete:
description: True only after the local gate AND the Actions handoff upload succeed.
value: ${{ steps.ready.outputs.complete }}
runs:
using: composite
steps:
- name: Collect signed artifacts
id: collect
shell: bash
env:
CHANNEL: ${{ inputs.channel }}
MAVEN_ASSETS: ${{ inputs.maven-assets }}
run: |
set -euo pipefail
case "$CHANNEL" in snapshot|release) ;; *) echo "::error::Invalid publication channel"; exit 1 ;; esac
args=("signed-${CHANNEL}-assets")
if [ -n "$MAVEN_ASSETS" ]; then args+=(--maven-assets "$MAVEN_ASSETS"); fi
python3 .github/collect-release-assets.py "${args[@]}"
- name: Verify and freeze the complete signed publication
id: preflight
shell: bash
env:
CHANNEL: ${{ inputs.channel }}
run: python3 .github/publication.py prepare "signed-${CHANNEL}-assets" publication-package --channel "$CHANNEL"
- name: Secure the checked publication package
id: handoff
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: signed-${{ inputs.channel }}-assets
path: publication-package/
if-no-files-found: error
- name: Release the secured package to independent consumers
id: ready
shell: bash
run: echo 'complete=true' >> "$GITHUB_OUTPUT"
4 changes: 3 additions & 1 deletion .github/actions/install-sccache-windows/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ inputs:
version:
description: sccache release to install.
required: false
default: '0.18.0'
default: '0.19.0'
runs:
using: composite
steps:
Expand All @@ -30,6 +30,8 @@ runs:
# version and arch; a version without a pinned hash is refused (the caller's
# continue-on-error then builds without the cache, which is the safe side).
$sha256 = @{
'0.19.0-x86_64' = '28be8c61f6b2c89b9eb2869f6af7f78e6f992e78a8abae0ce855fcc129cbeac1'
'0.19.0-aarch64' = '66d20f1ea5e29f4f309136323635b864e1d8f0a8b587abbc22271bc22d439a5a'
'0.18.0-x86_64' = '8965c74d5e8a225244f741e18ad2f3f504f48228dc1bac948fc22761a348363d'
'0.18.0-aarch64' = '205d613fa74a9a0525e41a5ace77b1c71907d5bd4a5e668bad79111776829290'
}["$env:SCCACHE_VERSION-$arch"]
Expand Down
9 changes: 6 additions & 3 deletions .github/android-consumer-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ Not a shipped project — it exists so CI proves Android Studio consumption end

Two CI jobs drive it (`.github/workflows/publish.yml`):

1. **`package-android-aar`** — `assembleRelease` with R8: validates AAR format, manifest
1. **`package-android-aar`** — `assembleRelease` with R8 for both `-PjllamaBackend=cpu` (the
default) and `-PjllamaBackend=opencl`. The latter declares only the OpenCL dependency and
resolves the CPU AAR transitively, catching duplicate classes between the two AARs.
Validates AAR format, manifest
minSdk merge, `jni/{arm64-v8a,x86_64}` packaging, and the shipped consumer ProGuard
rules (asserts the APK still carries the binding and, per ABI, `libjllama.so`, ggml's
shared libraries and at least one `libggml-cpu-*.so` module).
Expand All @@ -23,5 +26,5 @@ Two CI jobs drive it (`.github/workflows/publish.yml`):
so a local `connectedAndroidTest` against a bare emulator stays green.

What the emulator job deliberately does NOT cover: arm64 kernels (real-device ABI — the
example app on hardware covers that) and the Adreno/OpenCL flavor (no OpenCL ICD in the
emulator).
example app on hardware covers that) and Adreno/OpenCL execution (no OpenCL ICD in the
emulator). OpenCL dependency resolution, R8 and APK packaging are covered by the first job.
6 changes: 5 additions & 1 deletion .github/android-consumer-test/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ plugins {
// Injected by CI (-PjllamaVersion=<reactor version>) so the fixture always tests
// the AAR that was just built and published to mavenLocal.
val jllamaVersion: String = providers.gradleProperty("jllamaVersion").get()
val jllamaBackend = providers.gradleProperty("jllamaBackend").orElse("cpu").get()
require(jllamaBackend in listOf("cpu", "opencl")) { "Unknown jllamaBackend: $jllamaBackend" }

android {
namespace = "net.ladenthin.llama.consumertest"
Expand Down Expand Up @@ -48,7 +50,9 @@ android {
}

dependencies {
implementation("net.ladenthin:llama-android:$jllamaVersion")
// The OpenCL case resolves the CPU AAR transitively, exactly as a consumer does.
val artifact = if (jllamaBackend == "opencl") "llama-android-opencl" else "llama-android"
implementation("net.ladenthin:$artifact:$jllamaVersion")

// On-emulator instrumentation (test-android-emulator CI job).
androidTestImplementation("androidx.test.ext:junit:1.3.0")
Expand Down
52 changes: 30 additions & 22 deletions .github/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ fi
#
# SCCACHE_DL_VERSION is overridable per-job, so a container that crashes one sccache build can
# try another without editing this script (the in-container panic that stalled phase 2 was on
# v0.8.2; v0.18.0 is the latest release and the default). A wrong/unavailable version just fails
# v0.8.2; v0.19.0 is the default). A wrong/unavailable version just fails
# the `curl -f` and falls back to an uncached build, so bumping it can never red a build.
SCCACHE_DL_VERSION="${SCCACHE_DL_VERSION:-0.18.0}"
SCCACHE_DL_VERSION="${SCCACHE_DL_VERSION:-0.19.0}"
case "$(uname -m)" in
x86_64) SCCACHE_DL_ARCH="x86_64" ;;
aarch64|arm64) SCCACHE_DL_ARCH="aarch64" ;;
Expand All @@ -49,6 +49,8 @@ if [ "${USE_CACHE:-true}" = "true" ] && [ -n "${SCCACHE_WEBDAV_TOKEN:-}${SCCACHE
# publishes next to the release assets); another SCCACHE_DL_VERSION brings its own through
# SCCACHE_DL_SHA256, and without a known hash the fetch is refused rather than trusted.
case "${SCCACHE_DL_VERSION}-${SCCACHE_DL_ARCH}" in
0.19.0-x86_64) SCCACHE_DL_SHA256="${SCCACHE_DL_SHA256:-aa73c7270c997894038c0d8f0d297865f57569e1687883322bc431713d4bb9dd}" ;;
0.19.0-aarch64) SCCACHE_DL_SHA256="${SCCACHE_DL_SHA256:-3e47fc4a71b1ddd1b4294a0de9d34498922fdf41e3cd0bda5df3954e3d6deb9d}" ;;
0.18.0-x86_64) SCCACHE_DL_SHA256="${SCCACHE_DL_SHA256:-45f1447fbe231e3037bde351ef70677dd212216c8d62ae7ca409fecc4d6acc89}" ;;
0.18.0-aarch64) SCCACHE_DL_SHA256="${SCCACHE_DL_SHA256:-2b3284d5da3b46a47dc4229e75bb7b88ac4aa99c8d754fb7d2f84997e5a4354a}" ;;
*) SCCACHE_DL_SHA256="${SCCACHE_DL_SHA256:-}" ;;
Expand Down Expand Up @@ -127,28 +129,28 @@ if [ "${USE_CACHE:-true}" = "true" ] && command -v sccache >/dev/null 2>&1 \
&& [ -n "${SCCACHE_WEBDAV_TOKEN:-}${SCCACHE_GHA_ENABLED:-}" ] \
&& sccache_can_wrap_compiler; then
LAUNCH="-DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache"
# CUDA builds: nvcc through sccache is OPT-IN (SCCACHE_WRAP_NVCC=true), off by default.
# Wrapping nvcc cached the per-arch .cu device passes too, which are the dominant cost of the
# full-arch CUDA job (measured: ~51 min cold -> ~15 min warm with CUDA 13.2). Since the CUDA
# 13.4 toolkit (2026-09-27), sccache 0.18.0 fails on the very first .cu TUs, ten seconds into
# the build, in every job log examined (four of four: runs 37643964068, 37680715063,
# 37831180985, 37925157175): `Missing "cubin" file output` / `Compiler killed by signal 126`
# -- sccache's nvcc command-line parsing, not a compile error. The retry below then rebuilt
# EVERYTHING without a launcher, gcc TUs included (47-80 min depending on the runner). Not
# wrapping nvcc keeps the ~280 gcc objects cached and runs nvcc directly from the start, with
# no wasted first attempt. Re-try the opt-in on a PR run after bumping SCCACHE_DL_VERSION (or
# a CUDA bump); flip the default back once a warm run shows CUDA hits in the stats table.
# CUDA builds: nvcc goes through sccache too (SCCACHE_WRAP_NVCC=false opts out). That caches the
# per-arch .cu device passes, the dominant cost of the full-arch CUDA job (measured: ~51 min cold
# -> ~15 min warm with CUDA 13.2). It was off from 2026-10-09 to 2026-10-25: with CUDA 13.4 and
# sccache 0.18.0 every job failed on the first .cu TUs with `Missing "cubin" file output` /
# `Compiler killed by signal 126`, and the uncached retry rebuilt everything. The cause was ggml's
# default arch list, whose PTX-only `90-virtual` target makes nvcc run a ptxas check pass without
# `-o` (mozilla/sccache#2862, fix PR #2809). The list this project passes since 2026-10-11
# (`build_cuda_linux.sh`: every arch `-real`, only `121-virtual` PTX-only) does not trigger it:
# measured in nvidia/cuda:13.4.3 with llama.cpp b11815, sccache 0.18.0 builds six ggml-cuda TUs
# with 108/108 hits warm (19 s vs. 243 s), and ggml's list fails exactly as CI did. The "signal
# 126" was sccache reporting an exit code as a signal, fixed in 0.19.0 (mozilla/sccache#2932).
# Scoped to CUDA builds (GGML_CUDA in the cmake args): CMAKE_CUDA_COMPILER_LAUNCHER is inert
# when CUDA is not an enabled language, but keeping it scoped leaves the CPU/Android jobs'
# configure output clean. ggml does not wrap nvcc on its own here: its GGML_CCACHE block only
# fires when neither CMAKE_C_COMPILER_LAUNCHER nor CMAKE_CXX_COMPILER_LAUNCHER is set.
case " $* " in
*" -DGGML_CUDA=1 "* | *" -DGGML_CUDA=ON "* | *" -DGGML_CUDA=on "*)
if [ "${SCCACHE_WRAP_NVCC:-false}" = "true" ]; then
if [ "${SCCACHE_WRAP_NVCC:-true}" = "true" ]; then
LAUNCH="$LAUNCH -DCMAKE_CUDA_COMPILER_LAUNCHER=sccache"
echo "build.sh: sccache will also wrap nvcc (CUDA build detected, SCCACHE_WRAP_NVCC=true)"
echo "build.sh: sccache will also wrap nvcc (CUDA build detected; SCCACHE_WRAP_NVCC=false opts out)"
else
echo "build.sh: nvcc runs uncached (CUDA build detected, SCCACHE_WRAP_NVCC is not 'true'); sccache wraps the C/C++ TUs only"
echo "build.sh: nvcc runs uncached (CUDA build detected, SCCACHE_WRAP_NVCC=${SCCACHE_WRAP_NVCC}); sccache wraps the C/C++ TUs only"
fi ;;
esac
# ROCm builds: on Linux ggml compiles its ~150 HIP TUs as the CMake language HIP, which the
Expand Down Expand Up @@ -188,16 +190,22 @@ cmake -Bbuild $LAUNCH $REQUIRE_COMMIT $@ || exit 1
# still fails fast (and is reported) instead of triggering a wasteful uncached rebuild. The
# "Compiler not supported" signature additionally covers the CUDA case: if wrapping nvcc breaks
# (sccache declining/erroring on the nvcc driver), the retry rebuilds the full-arch CUDA job
# without any launcher rather than redding it. "Compiler killed by signal" covers a second CUDA
# failure mode seen with CUDA 13.3's nvcc: sccache's nvcc wrapping loses an intermediate .ptx file
# for a "-virtual" architecture target (e.g. 75-virtual), so fatbinary aborts with "Could not open
# input file '*.ptx'" and sccache reports the underlying nvcc invocation as killed — an sccache/nvcc
# incompatibility, not a real compile error, so it gets the same uncached-retry treatment.
# without any launcher rather than redding it. "fatbinary fatal" covers a second CUDA failure mode
# seen with CUDA 13.3's nvcc: sccache's nvcc wrapping loses an intermediate .ptx file for a
# "-virtual" architecture target (e.g. 75-virtual), so fatbinary aborts with "Could not open input
# file '*.ptx'" -- an sccache/nvcc incompatibility, not a real compile error, so it gets the same
# uncached-retry treatment. Up to sccache 0.18 that case (and every ordinary nvcc compile error)
# also printed "Compiler killed by signal <n>", an exit code misreported as a signal; 0.19.0 reports
# the exit code (mozilla/sccache#2932), so a genuine CUDA compile error no longer matches here and
# fails fast instead of costing an uncached rebuild. The pattern stays for an older SCCACHE_DL_VERSION.
# 'Missing "cubin"' is sccache's own error when nvcc's ptxas check pass writes no output
# (mozilla/sccache#2862, a PTX-only target such as ggml's default 90-virtual): with 0.18 it came
# with the "killed by signal" line, with 0.19.0 that line is gone, so it is matched by itself.
build_log="$(mktemp 2>/dev/null || echo "/tmp/jllama-build.$$.log")"
cmake --build build --config Release -j"${JOBS}" 2>&1 | tee "$build_log"
build_rc=${PIPESTATUS[0]}
if [ "$build_rc" -ne 0 ]; then
if [ -n "$LAUNCH" ] && grep -qiE 'sccache: error|Server startup failed|cache storage failed|Compiler not supported|Compiler killed by signal' "$build_log"; then
if [ -n "$LAUNCH" ] && grep -qiE 'sccache: error|Server startup failed|cache storage failed|Compiler not supported|Compiler killed by signal|fatbinary fatal|Missing "cubin"' "$build_log"; then
echo "build.sh: build failed via an sccache cache error — retrying WITHOUT cache (clean reconfigure)."
rm -f "$build_log"
rm -rf build && mkdir -p build
Expand Down
9 changes: 5 additions & 4 deletions .github/buildcheck/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
"""Build checks as a library, so buildcheck/tests can test them. Standard library only.

Two kinds of module live here. workflow.py, releasegate.py, sharedfiles.py, versions.py and
runscripts.py (with their tests and the check-*.py entry points next to this package) are kept
BYTE-IDENTICAL in java-llama.cpp, BitcoinAddressFinder, srcmorph and streambuffer: each repository
lists them in .github/shared-files.sha256, which its `shared-files` job checks (see sharedfiles.py).
Two kinds of module live here. workflow.py, releasegate.py, sharedfiles.py, versions.py,
runscripts.py, releaseassets.py, publication.py and snapshot.py (with their tests and the *.py
entry points next to this package) are kept BYTE-IDENTICAL in java-llama.cpp,
BitcoinAddressFinder, srcmorph and streambuffer: each repository lists them in
.github/shared-files.sha256, which its `shared-files` job checks (see sharedfiles.py).
Every other module is the repository's own.

Run the tests from the repository root:
Expand Down
Loading
Loading