diff --git a/.github/buildcheck/tests/test_versions.py b/.github/buildcheck/tests/test_versions.py
index b2f03ca0..3504e253 100644
--- a/.github/buildcheck/tests/test_versions.py
+++ b/.github/buildcheck/tests/test_versions.py
@@ -4,6 +4,7 @@
import io
import os
+import tempfile
import unittest
from unittest import mock
@@ -62,6 +63,33 @@ def test_only_coordinates_both_use_and_only_differences(self):
["s:p is 2 here and 9 in r1: bump it in every repository"])
+class GradleKotlinTest(unittest.TestCase):
+
+ def test_plugins_are_read_from_every_kts_file_outside_build_output(self):
+ with tempfile.TemporaryDirectory() as root:
+ os.makedirs(os.path.join(root, "app", "build"))
+ with open(os.path.join(root, "app", "build.gradle.kts"), "w", encoding="utf-8") as f:
+ f.write('plugins {\n id("com.android.application") version "9.4.0"\n'
+ ' id("org.jetbrains.kotlin.plugin.compose") version "2.4.20"\n'
+ ' kotlin("jvm") version "2.4.21"\n}\n')
+ with open(os.path.join(root, "app", "build", "stale.gradle.kts"), "w", encoding="utf-8") as f:
+ f.write('kotlin("jvm") version "1.0.0"\n')
+ self.assertEqual(versions.gradle_kotlin_plugins(root), [
+ ("app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20"),
+ ("app/build.gradle.kts", "org.jetbrains.kotlin.jvm", "2.4.21"),
+ ])
+
+ def test_a_plugin_behind_the_poms_kotlin_is_a_warning_and_nothing_else_is(self):
+ own = {"org.jetbrains.kotlin:kotlin-stdlib": {"2.4.21"}, "org.jetbrains.kotlinx:kotlinx-coroutines-core": {"1.11.0"}}
+ plugins = [("app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20"),
+ ("app/build.gradle.kts", "org.jetbrains.kotlin.jvm", "2.4.21")]
+ self.assertEqual(versions.compare_gradle_kotlin(own, plugins), [
+ "org.jetbrains.kotlin.plugin.compose is 2.4.20 in app/build.gradle.kts and the POMs use Kotlin 2.4.21: "
+ "Dependabot bumps only the POMs, bump the Gradle plugin with them"])
+ self.assertEqual(versions.compare_gradle_kotlin({"a:b": {"1"}}, plugins), [])
+ self.assertEqual(versions.compare_gradle_kotlin(own, []), [])
+
+
class MainTest(unittest.TestCase):
def run_main(self, siblings):
@@ -87,6 +115,19 @@ def test_differences_are_warnings_and_never_fail(self):
self.assertNotIn("BitcoinAddressFinder:", err)
self.assertIn("1 version differences", out)
+ def test_a_gradle_kotlin_plugin_behind_the_poms_is_reported_with_the_other_warnings(self):
+ kotlin = pom('org.jetbrains.kotlin'
+ 'kotlin-stdlib2.4.21')
+ out, err = io.StringIO(), io.StringIO()
+ with mock.patch.object(versions, "current_repo", return_value="java-llama.cpp"), \
+ mock.patch.object(versions, "read_local", return_value=[kotlin]), \
+ mock.patch.object(versions, "gradle_kotlin_plugins", return_value=[
+ ("android-llmservice/app/build.gradle.kts", "org.jetbrains.kotlin.plugin.compose", "2.4.20")]):
+ self.assertEqual(versions.main(["x"], "/nowhere", lambda repo, path: kotlin, out, err), 0)
+ self.assertIn("::warning::org.jetbrains.kotlin.plugin.compose is 2.4.20 in android-llmservice/app/build.gradle.kts "
+ "and the POMs use Kotlin 2.4.21", err.getvalue())
+ self.assertIn("1 version differences", out.getvalue())
+
def test_a_missing_pom_skips_that_file_not_the_repository(self):
out, err = io.StringIO(), io.StringIO()
bumped = pom('org.junit.jupiter'
diff --git a/.github/buildcheck/versions.py b/.github/buildcheck/versions.py
index 2935a761..7b1270d8 100644
--- a/.github/buildcheck/versions.py
+++ b/.github/buildcheck/versions.py
@@ -12,12 +12,17 @@
`${property}` references resolved -- not property
names, which differ between repositories and include values that must differ (the Java release).
+It also compares the Kotlin the POMs use with every Kotlin Gradle plugin a `*.gradle.kts` of the
+repository pins: Dependabot's maven ecosystem bumps `kotlin.version` and never sees the Gradle files,
+which is how a Compose compiler plugin stayed one patch release behind the POMs.
+
Warnings, never failures: a bump lands in four pull requests, and the first one merged would
otherwise turn the other three repositories red. The repository's own artifacts
(`net.ladenthin:*`) are left out: a sibling depends on the last RELEASE of java-llama.cpp while
java-llama.cpp itself is at the next snapshot.
"""
+import os
import re
import sys
import urllib.request
@@ -28,7 +33,7 @@
NS = "{http://maven.apache.org/POM/4.0.0}"
POMS = {
"java-llama.cpp": ("pom.xml", "llama/pom.xml", "llama-langchain4j/pom.xml", "llama-kotlin/pom.xml",
- "llama-platform/pom.xml", "llama-atmosphere-agent/pom.xml"),
+ "llama-platform/pom.xml", "llama-atmosphere-agent/pom.xml", "examples/server/pom.xml"),
"BitcoinAddressFinder": ("pom.xml",),
"srcmorph": ("pom.xml", "srcmorph/pom.xml", "srcmorph-cli/pom.xml", "srcmorph-maven-plugin/pom.xml"),
"streambuffer": ("pom.xml",),
@@ -42,6 +47,11 @@
CONFIGURED = {"palantirJavaFormat": "com.palantir.javaformat:palantir-java-format"}
RAW_URL = "https://raw.githubusercontent.com/{owner}/{repo}/HEAD/{path}"
PROPERTY = re.compile(r"\$\{([^}]+)\}")
+KOTLIN_GROUP = "org.jetbrains.kotlin"
+# `id("org.jetbrains.kotlin.plugin.compose") version "2.4.21"` and `kotlin("jvm") version "2.4.21"`
+GRADLE_KOTLIN = re.compile(
+ r'(?:id\("(org\.jetbrains\.kotlin(?:\.[A-Za-z.]+)?)"\)|kotlin\("([A-Za-z.]+)"\))\s+version\s+"([^"]+)"')
+SKIPPED_DIRS = {".git", ".gradle", ".idea", "build", "node_modules", "target"}
def coordinates(pom_texts):
@@ -94,6 +104,38 @@ def compare(own, others):
return warnings
+def gradle_kotlin_plugins(root):
+ """(path, plugin id, version) for every Kotlin Gradle plugin a `*.gradle.kts` below root pins, in
+ path order; build output and the tools' own directories are not searched."""
+ found = []
+ for dirpath, dirnames, filenames in os.walk(root):
+ dirnames[:] = sorted(d for d in dirnames if d not in SKIPPED_DIRS)
+ for name in sorted(filenames):
+ if not name.endswith(".gradle.kts"):
+ continue
+ path = os.path.join(dirpath, name)
+ with open(path, encoding="utf-8") as f:
+ text = f.read()
+ for match in GRADLE_KOTLIN.finditer(text):
+ plugin = match.group(1) or f"{KOTLIN_GROUP}.{match.group(2)}"
+ found.append((os.path.relpath(path, root).replace(os.sep, "/"), plugin, match.group(3)))
+ return found
+
+
+def compare_gradle_kotlin(own, plugins):
+ """Warnings for every Kotlin Gradle plugin whose version is not one the POMs use for
+ `org.jetbrains.kotlin:*`; nothing when the POMs use no Kotlin or no Gradle file pins a plugin."""
+ maven = set()
+ for coordinate, used in own.items():
+ if coordinate.startswith(KOTLIN_GROUP + ":"):
+ maven |= used
+ if not maven:
+ return []
+ return [f"{plugin} is {version} in {path} and the POMs use Kotlin {', '.join(sorted(maven))}: "
+ f"Dependabot bumps only the POMs, bump the Gradle plugin with them"
+ for path, plugin, version in plugins if version not in maven]
+
+
def fetch(repo, path):
with urllib.request.urlopen(RAW_URL.format(owner=OWNER, repo=repo, path=path), timeout=20) as response:
return response.read().decode("utf-8")
@@ -129,7 +171,7 @@ def main(argv, root, fetcher=fetch, out=sys.stdout, err=sys.stderr):
print(f"::warning::could not read {sibling}'s {path}: {e}", file=err)
if texts:
others[sibling] = coordinates(texts)
- warnings = compare(own, others)
+ warnings = compare(own, others) + compare_gradle_kotlin(own, gradle_kotlin_plugins(root))
for warning in warnings:
print(f"::warning::{warning}", file=err)
shared = set(own).intersection(*others.values()) if others else set()
diff --git a/.github/print-host-info.sh b/.github/print-host-info.sh
new file mode 100755
index 00000000..5e9928c5
--- /dev/null
+++ b/.github/print-host-info.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
+#
+# SPDX-License-Identifier: MIT OR Apache-2.0
+
+# Prints the runner's CPU, for reading a native build or test log: which instruction sets the
+# compiler saw (GGML_NATIVE, the x86 variants) and how many cores the job had. Informational only
+# -- it never fails the step. One script for every OS (the step runs `shell: bash`, which is Git
+# Bash on Windows).
+case "$(uname -s)" in
+ Linux)
+ echo "=== CPU information (lscpu) ==="
+ lscpu || true
+ echo
+ echo "=== CPU details (/proc/cpuinfo) ==="
+ cat /proc/cpuinfo || true
+ ;;
+ Darwin)
+ echo "=== CPU information (sysctl) ==="
+ sysctl hw.model hw.cachelinesize hw.cpufrequency hw.cachesize hw.physicalcpu hw.logicalcpu \
+ hw.packages hw.memsize hw.ncpu 2>/dev/null || true
+ echo
+ echo "=== Processor details (system_profiler) ==="
+ system_profiler SPHardwareDataType || true
+ ;;
+ MINGW* | MSYS* | CYGWIN*)
+ echo "=== CPU information (Get-CimInstance Win32_Processor) ==="
+ powershell.exe -NoProfile -Command 'Get-CimInstance Win32_Processor | Select-Object * | Format-List' || true
+ echo "=== Processor (systeminfo) ==="
+ systeminfo | grep -i processor || true
+ ;;
+ *)
+ echo "no CPU report for $(uname -s)"
+ ;;
+esac
+exit 0
diff --git a/.github/shared-files.sha256 b/.github/shared-files.sha256
index b2585e74..e047d4e3 100644
--- a/.github/shared-files.sha256
+++ b/.github/shared-files.sha256
@@ -22,13 +22,14 @@ cbe504ad081ba31ec16e11c11f879c64179ca7741bf0b1d1318a7fc969ed67dd .github/buildc
fe36042fbd5444f0b454ad1304cdafde901dd8cec57cfaba7c7086d996dfee27 .github/buildcheck/tests/test_sharedfiles.py
7fe208dd33184b4543328f97b339c95e23c408b120e6bfa6095de4182aeb9a19 .github/check-release-gate.py
1604ccdea83998814a3c1f98f746272222039867b0c7d692355e61495c1c648b .github/check-shared-files.py
-e5f1679dc7151b2676b2f587860bbaa17e1b89b44a5a4e9703eb3499248c4a06 .github/buildcheck/versions.py
-d454d33b61089b5f7bd6d439e3e5b58e01e7b01eeba6b4c2f7ed3360bbb158f0 .github/buildcheck/tests/test_versions.py
+74b4b18af6c5be2af54048de408ec6e138f37f85b9f9ed2fdef258e270a7c90c .github/buildcheck/versions.py
+de437064302f9314028afff00e17fc995e48869e07ee402206d23708c7ed355a .github/buildcheck/tests/test_versions.py
0c78f43dd181f079d35edde7cbf25bb22eac92c907845d8dc8eec78e812aa097 .github/check-versions.py
33c70847b6568604e8725d167a9b1d5910cc2a1a5b01fec7362be31b488ddb02 .github/buildcheck/runscripts.py
c4967e3011d474ec43dbc797d113685c0a8a01b7cc52e910a1356273e6a67dc9 .github/buildcheck/tests/test_runscripts.py
7ad8f7258c983c91e67db695e5ab6ea0f9279af007a6aa815f92f0d8a74c2ae4 .github/check-run-scripts.py
7da53fdb9537ccf7d3f3740be31f295bacb914fe186304b9ed8a65fd3d3d413e .github/print-crash-logs.sh
+fa1469664cb632e210ff3aa0669f15219d160ef13b486abeb978efade55f330b .github/print-host-info.sh
c8c02dc39aeacc154fad74071443c6513666aa94ca9f9c5fab49c5f39dea7ee6 .github/verify-signing-key.sh
e0b2a4f331eae326b61c71f78d6272d06d99140a85dedb7710f93402de862466 .github/verify-bytecode-version.sh
ab45f5c102b47dd16c325d4d9c283d158ba90c05f484eac45b2767885c4462f9 .github/signing-selftest/build.gradle.kts
diff --git a/.github/workflows/measurement-tools.yml b/.github/workflows/measurement-tools.yml
index d4f0ba24..8c3887b0 100644
--- a/.github/workflows/measurement-tools.yml
+++ b/.github/workflows/measurement-tools.yml
@@ -50,4 +50,4 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: ['3.10', '3.14']
+ python-version: ['3.10', '3.15']
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 1eccc5b8..569f53b4 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -265,6 +265,9 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
+ - name: Display CPU Info
+ shell: bash
+ run: bash .github/print-host-info.sh
- uses: actions/setup-java@v6
with:
java-version-file: .java-version
@@ -326,6 +329,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
+ - name: Display CPU Info
+ shell: bash
+ run: bash .github/print-host-info.sh
- uses: actions/setup-java@v6
with:
java-version-file: .java-version
@@ -388,6 +394,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
+ - name: Display CPU Info
+ shell: bash
+ run: bash .github/print-host-info.sh
- uses: actions/setup-java@v6
with:
java-version-file: .java-version
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0981b420..eb8fa924 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
+- Build and test tooling bumped in step with the sibling repositories: Spotless 3.10.4, Checker
+ Framework 4.3.1, `commons-codec` 1.23.0; the measurement-tools matrix runs Python 3.10 and 3.15
+ (3.15.0 final, from 3.14). The shared `check-versions.py` now also compares the Kotlin Gradle plugins
+ of a repository with its POMs' Kotlin (nothing to compare here) and reads java-llama.cpp's
+ `examples/server/pom.xml`.
+- **CI prints the runner's CPU** at the top of `test`, `test-opencl` and `vmlens`
+ (`.github/print-host-info.sh`, a shared file of the four repositories): pocl computes on the CPU, so a
+ throughput or a timing in those logs can be read against the hardware the job drew.
- **CI: snapshot deploy fails with HTTP 401 on Maven 3.10 (seen in java-llama.cpp).** The runners moved to Maven 3.10, which
sends a ``'s credentials only to the origins declared for it; for the id `central` that is
`https://repo.maven.apache.org`, so the upload to `central.sonatype.com/repository/maven-snapshots/`
diff --git a/CLAUDE.md b/CLAUDE.md
index b79fd287..c2c01e5b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -779,7 +779,9 @@ default branch lists it with a different hash. To change a shared file, change e
publish jobs unless `.github/release-gate-exemptions.txt` names it with a reason, and
**`check-versions.py`**, which **warns** where a Maven dependency or plugin (incl. the Spotless
formatter version) is used here in another version than in a sibling repository -- Dependabot bumps
-each repository on its own, so this is where the drift shows -- and **`check-run-scripts.py`**, which
+each repository on its own, so this is where the drift shows; it also compares the Kotlin Gradle plugins
+of a repository's `*.gradle.kts` files with its POMs' Kotlin, the files Dependabot never sees -- and
+**`check-run-scripts.py`**, which
runs `bash -n` over every `run:` script of the workflows and composite actions that runs in bash, so
a broken script (a lost line continuation, say) fails here instead of in the job running it. The JDK every
workflow uses is `.java-version` (setup-java's `java-version-file`), the same shared file in all four. Details and the
diff --git a/pom.xml b/pom.xml
index 8ad63f48..86c7f3a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@ SPDX-License-Identifier: Apache-2.0
1.14.0
2.50.0
0.14.2
- 4.3.0
+ 4.3.1
1.6.0
0.6.0
1.0.1
@@ -86,7 +86,7 @@ SPDX-License-Identifier: Apache-2.0
0.9.3
3.4.3
2.22.0
- 1.22.1
+ 1.23.0
3.10.0
6.1.3
3.0
@@ -106,7 +106,7 @@ SPDX-License-Identifier: Apache-2.0
artifact reaches a published artifact at all; both are test scope. -->
2.26.1
1.37
- 3.10.3
+ 3.10.4
2.102.0