diff --git a/.github/buildcheck/__init__.py b/.github/buildcheck/__init__.py
deleted file mode 100644
index 9fd81d86..00000000
--- a/.github/buildcheck/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
-"""This repository's own build checks. The checks it shares with its sibling repositories live in
-bernardladenthin/actions (actionlib); these are the ones only srcmorph has.
-
-Run the tests from the repository root:
- python3 -m unittest discover -s .github/buildcheck/tests -t .github
-"""
diff --git a/.github/buildcheck/tests/__init__.py b/.github/buildcheck/tests/__init__.py
deleted file mode 100644
index 4833295f..00000000
--- a/.github/buildcheck/tests/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
diff --git a/.github/buildcheck/tests/helpers.py b/.github/buildcheck/tests/helpers.py
deleted file mode 100644
index d74bdea8..00000000
--- a/.github/buildcheck/tests/helpers.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
-"""Fixtures of this repository's build-check tests."""
-
-import os
-
-REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
diff --git a/.github/buildcheck/tests/test_verify_classifier_fatjars.py b/.github/buildcheck/tests/test_verify_classifier_fatjars.py
deleted file mode 100644
index 2aad20b7..00000000
--- a/.github/buildcheck/tests/test_verify_classifier_fatjars.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
-"""Drives .github/verify-classifier-fatjars.sh against fixture jars built here.
-
-srcmorph's own test, not a shared file: the script is srcmorph's alone. Every case of a check the
-script makes itself asserts the exit code AND the ::error:: line it promises, so a silent exit cannot
-pass for a diagnosed one; the corrupt-jar case asserts unzip's own exit code and that the script does
-not pass the file off as a jar without natives.
-"""
-
-import os
-import shutil
-import subprocess
-import tempfile
-import unittest
-import zipfile
-
-from buildcheck.tests.helpers import REPO
-
-SCRIPT = os.path.join(REPO, ".github", "verify-classifier-fatjars.sh")
-BASH = shutil.which("bash")
-UNZIP = shutil.which("unzip")
-
-PREFIX = "net/ladenthin/llama/"
-LINUX_CPU = PREFIX + "Linux/x86_64/cpu/libjllama.so"
-WINDOWS_CPU = PREFIX + "Windows/x86_64/cpu/jllama.dll"
-LINUX_CUDA = PREFIX + "Linux/x86_64/cuda13/libggml-cuda.so"
-NON_NATIVE = "net/ladenthin/srcmorph/cli/Main.class"
-DEFAULT = "srcmorph-cli-1.3.0-SNAPSHOT-jar-with-dependencies.jar"
-
-
-def classifier_jar(classifier):
- return f"srcmorph-cli-1.3.0-SNAPSHOT-jar-with-dependencies-{classifier}.jar"
-
-
-@unittest.skipUnless(BASH and UNZIP, "needs bash and unzip")
-class VerifyClassifierFatjarsTest(unittest.TestCase):
-
- def setUp(self):
- self.dir = tempfile.mkdtemp()
- self.addCleanup(shutil.rmtree, self.dir)
-
- def jar(self, name, entries):
- with zipfile.ZipFile(os.path.join(self.dir, name), "w") as z:
- z.writestr("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n")
- for entry in entries:
- z.writestr(entry, b"x")
-
- def run_script(self, *classifiers):
- return subprocess.run([BASH, SCRIPT, self.dir, *classifiers], capture_output=True, text=True)
-
- def assert_fails_with(self, result, message):
- self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
- self.assertIn("::error::", result.stderr)
- self.assertIn(message, result.stderr)
-
- def test_a_classifier_jar_with_its_backend_and_the_cpu_natives_passes(self):
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU, NON_NATIVE])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [LINUX_CPU, WINDOWS_CPU, LINUX_CUDA, NON_NATIVE])
- result = self.run_script("cuda13-linux-x86-64")
- self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
- self.assertIn("matches Linux/x86_64/cuda13/", result.stdout)
- self.assertEqual(result.stderr, "")
-
- def test_a_classifier_jar_holding_only_the_cpu_natives_fails(self):
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [LINUX_CPU, WINDOWS_CPU])
- self.assert_fails_with(self.run_script("cuda13-linux-x86-64"), "does not contain what its name promises")
-
- def test_a_classifier_jar_that_lost_a_default_native_fails(self):
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [LINUX_CPU, LINUX_CUDA])
- self.assert_fails_with(self.run_script("cuda13-linux-x86-64"), "lost natives of the default jar")
-
- def test_an_unmapped_classifier_shape_fails(self):
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU])
- self.jar(classifier_jar("cuda13-macos-aarch64"), [LINUX_CPU, WINDOWS_CPU, LINUX_CUDA])
- self.assert_fails_with(self.run_script("cuda13-macos-aarch64"), "has no expected native path")
-
- def test_a_classifier_jar_without_any_native_fails_naming_it(self):
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [NON_NATIVE])
- self.assert_fails_with(self.run_script("cuda13-linux-x86-64"),
- classifier_jar("cuda13-linux-x86-64") + " carries no native library at all")
-
- def test_a_default_jar_without_any_native_fails_naming_it(self):
- self.jar(DEFAULT, [NON_NATIVE])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [LINUX_CPU, WINDOWS_CPU, LINUX_CUDA])
- self.assert_fails_with(self.run_script("cuda13-linux-x86-64"), DEFAULT + " carries no native library at all")
-
- def test_a_default_jar_with_natives_of_one_os_fails(self):
- self.jar(DEFAULT, [LINUX_CPU])
- self.jar(classifier_jar("cuda13-linux-x86-64"), [LINUX_CPU, LINUX_CUDA])
- self.assert_fails_with(self.run_script("cuda13-linux-x86-64"), "carries natives for only 1 OS")
-
- def test_a_corrupt_jar_fails_with_unzip_s_own_error_not_as_a_jar_without_natives(self):
- """A jar unzip cannot read must end the script with unzip's own failure, not read as a jar
- without natives: were the helper's failure masked (a `|| true` on its call, or a lost
- pipefail), the script would report "carries no native library at all" with exit code 1 and
- an ::error:: line, a wrong diagnosis. So the case pins unzip's exit code (9,
- "End-of-central-directory signature not found") and the absence of the script's diagnosis,
- not merely a non-zero exit. (A `|| true` on the grep branch alone is not observable this way:
- pipefail carries unzip's status whatever that branch does; the branch only decides whether
- grep's own error, exit 2, is tolerated, and nothing a fixture can provoke makes grep fail.)"""
- self.jar(DEFAULT, [LINUX_CPU, WINDOWS_CPU])
- with open(os.path.join(self.dir, classifier_jar("cuda13-linux-x86-64")), "wb") as f:
- f.write(b"not a zip")
- result = self.run_script("cuda13-linux-x86-64")
- self.assertEqual(result.returncode, 9, result.stdout + result.stderr)
- self.assertIn("End-of-central-directory signature not found", result.stderr)
- self.assertNotIn("::error::", result.stderr)
- self.assertNotIn("carries no native library", result.stderr)
- self.assertNotIn("ok: ", result.stdout)
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/.github/release-assets.json b/.github/release-assets.json
index 6af75abf..8d8244cc 100644
--- a/.github/release-assets.json
+++ b/.github/release-assets.json
@@ -1,27 +1,3 @@
{
- "schema": 1,
- "files": [
- {
- "source": "fatjar-assets/srcmorph-cli-{version}-jar-with-dependencies.jar"
- },
- {
- "source": "fatjar-assets/srcmorph-cli-{version}-jar-with-dependencies-{classifier}.jar",
- "classifiers": [
- "cuda13-linux-x86-64",
- "cuda13-windows-x86-64",
- "vulkan-linux-x86-64",
- "vulkan-linux-aarch64",
- "vulkan-windows-x86-64",
- "vulkan-windows-aarch64",
- "opencl-windows-x86-64",
- "opencl-windows-aarch64",
- "rocm-linux-x86-64",
- "rocm-windows-x86-64",
- "sycl-linux-x86-64",
- "sycl-windows-x86-64",
- "openvino-linux-x86-64",
- "openvino-windows-x86-64"
- ]
- }
- ]
+ "schema": 1
}
diff --git a/.github/sign-fatjars.sh b/.github/sign-fatjars.sh
deleted file mode 100755
index a99844f0..00000000
--- a/.github/sign-fatjars.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env bash
-
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
-
-# srcmorph's own since java-llama.cpp dropped its fat jars (and its copy of this script).
-# GPG-signs the fat jars (jar-with-dependencies) in a directory with a
-# detached, armored .asc signature — the authenticity counterpart to any .sha256 integrity
-# file. The caller builds/collects the fat jars; this only signs every
-# *-jar-with-dependencies*.jar it finds in (thin jars are left untouched).
-#
-# Signed in the GitHub-Release attach path, not at Maven build time, because only that
-# dispatch-gated path receives the signing key: GPG_PRIVATE_KEY / GPG_PASSPHRASE are scoped
-# to the `maven-central` GitHub Environment. Cross-repo convention + per-repo shapes:
-# workspace/policies/fat-jar-release-assets.md.
-#
-# Usage: sign-fatjars.sh
-# directory holding the fat jars (and possibly thin jars); every
-# *-jar-with-dependencies*.jar in it is signed in place (-> .asc).
-#
-# Env: GPG_PRIVATE_KEY armored secret key (required)
-# GPG_PASSPHRASE passphrase for the key (may be empty)
-#
-# Fail-loud: aborts if the key is absent, if no fat jar is found, or if any signature
-# fails to verify.
-
-set -euo pipefail
-
-DIR="${1:?usage: sign-fatjars.sh }"
-
-if [ -z "${GPG_PRIVATE_KEY:-}" ]; then
- echo "::error::GPG_PRIVATE_KEY is empty — cannot sign the fat jars. The maven-central environment did not deliver the secret to this ref." >&2
- exit 1
-fi
-if [ -n "${GPG_PASSPHRASE:-}" ]; then echo "::add-mask::${GPG_PASSPHRASE}"; fi
-
-# Ephemeral, private keyring; removed on exit (do NOT touch the runner's default one).
-GNUPGHOME="$(mktemp -d)"
-export GNUPGHOME
-chmod 700 "$GNUPGHOME"
-cleanup() { gpgconf --kill gpg-agent >/dev/null 2>&1 || true; rm -rf "$GNUPGHOME"; }
-trap cleanup EXIT
-
-printf '%s\n' "$GPG_PRIVATE_KEY" | gpg --batch --import
-KEYID="$(gpg --list-secret-keys --with-colons --fixed-list-mode | awk -F: '$1=="sec"{print $5; exit}')"
-if [ -z "$KEYID" ]; then
- echo "::error::No secret key imported from GPG_PRIVATE_KEY." >&2
- exit 1
-fi
-
-shopt -s nullglob
-jars=("$DIR"/*-jar-with-dependencies*.jar)
-shopt -u nullglob
-if [ "${#jars[@]}" -eq 0 ]; then
- echo "::error::No *-jar-with-dependencies*.jar found in '$DIR' to sign." >&2
- exit 1
-fi
-
-for f in "${jars[@]}"; do
- # Skip a signature file itself if the glob ever catches one.
- case "$f" in *.asc) continue ;; esac
- printf '%s' "${GPG_PASSPHRASE:-}" | gpg --batch --yes --pinentry-mode loopback \
- --passphrase-fd 0 --local-user "$KEYID" --detach-sign --armor "$f"
- gpg --batch --verify "$f.asc" "$f"
- echo "signed + verified: $(basename "$f") -> $(basename "$f").asc"
-done
-
-echo "Signed ${#jars[@]} fat jar(s) in '$DIR'."
diff --git a/.github/verify-classifier-fatjars.sh b/.github/verify-classifier-fatjars.sh
deleted file mode 100755
index 08efa176..00000000
--- a/.github/verify-classifier-fatjars.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env bash
-
-# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-#
-# SPDX-License-Identifier: MIT OR Apache-2.0
-
-# Structural check of the srcmorph-cli fat jars before they are attached to a GitHub release.
-#
-# WHY THIS IS STRUCTURAL AND NOT A LAUNCH. The cross-repo rule is "no release asset is attached that
-# CI has not run" (workspace/policies/fat-jar-release-assets.md), and the `smoke-fatjar` job does
-# exactly that -- for the DEFAULT jar. The GPU-classifier jars cannot be covered the same
-# way: a GitHub-hosted runner has no CUDA/ROCm/SYCL/OpenVINO device, and the only command that would
-# load the native library is a real generation. `Plan` with the mock provider -- the one thing that
-# does run on a GPU-less runner -- never touches it, so "launching" a classifier jar would assert
-# nothing about the classifier. Pretending otherwise is worse than not checking: it reads as covered.
-#
-# What CAN be asserted without a device is that each jar is the artifact it claims to be:
-#
-# 1. exactly one jar per requested classifier, and no unexpected classifier jar;
-# 2. every jar carries at least one native library (a jar with none is a broken assembly that
-# would fail at runtime on every platform);
-# 3. every classifier jar carries the backend directory its NAME promises: net.ladenthin:llama
-# natives jars (6.0.0 on) are named -- and hold /// --
-# for a GPU jar one ggml backend module (libggml-.so / ggml-.dll), which the
-# loader puts next to the CPU library of llama-platform's jar for that platform.
-# This is the one that catches the failure the whole loop is exposed to. If
-# `-Dllama.classifier=` ever stops being wired through (a renamed property, a pom refactor),
-# Maven still resolves the default natives and the loop happily produces byte-similar jars
-# under GPU names. No other check in this pipeline would notice;
-# 4. every classifier jar still carries every native of the default jar: the GPU backend is added
-# next to the CPU natives, which are the loader's fallback when the GPU runtime is missing;
-# 5. the default jar carries natives for more than one OS (it is the all-platform CPU variant).
-#
-# A new classifier SHAPE fails this script until its expected native path is declared, rather than
-# being skipped silently.
-#
-# Usage: verify-classifier-fatjars.sh ...
-
-set -euo pipefail
-
-ASSET_DIR="${1:?usage: verify-classifier-fatjars.sh ...}"
-shift
-CLASSIFIERS=("$@")
-
-# Native libraries live under this prefix inside the jar, as ///.
-NATIVE_PREFIX="net/ladenthin/llama/"
-
-fail() {
- echo "::error::$*" >&2
- exit 1
-}
-
-[ -d "$ASSET_DIR" ] || fail "asset directory '$ASSET_DIR' does not exist"
-[ "${#CLASSIFIERS[@]}" -gt 0 ] || fail "no classifiers given -- pass the same list the build loop used"
-
-# Sorted list of native-library entries in a jar, one per line; empty for a jar without any.
-# grep's "no match" (exit 1) is tolerated so the explicit checks below name the jar -- under pipefail
-# it used to end the script on the assignment, silently. An unzip failure or a grep error (exit 2)
-# still fails the pipeline.
-natives_of() {
- unzip -Z1 "$1" | { grep -E "^${NATIVE_PREFIX}.*\.(so|dll|dylib)$" || [ "$?" -eq 1 ]; } | sort
-}
-
-# "///" directory a classifier's name promises (--). The
-# catch-all is deliberate: a classifier shape nobody has mapped must red the job, not pass unchecked.
-expected_path_of() {
- local c="$1" arch os_arch
- case "$c" in
- *-linux-x86-64) os_arch="Linux/x86_64" ;;
- *-linux-aarch64) os_arch="Linux/aarch64" ;;
- *-windows-x86-64) os_arch="Windows/x86_64" ;;
- *-windows-aarch64) os_arch="Windows/aarch64" ;;
- *) fail "classifier '$c' has no expected native path -- add its shape to expected_path_of()" ;;
- esac
- case "$c" in
- *-x86-64) arch="x86-64" ;;
- *) arch="${c##*-}" ;;
- esac
- local rest="${c%-"$arch"}"
- echo "$os_arch/${rest%-*}/"
-}
-
-default_jars=()
-while IFS= read -r j; do default_jars+=("$j"); done < <(
- find "$ASSET_DIR" -maxdepth 1 -type f -name '*-jar-with-dependencies.jar' | sort
-)
-[ "${#default_jars[@]}" -eq 1 ] \
- || fail "expected exactly 1 default fat jar in '$ASSET_DIR', got ${#default_jars[@]}: ${default_jars[*]:-none}"
-DEFAULT_JAR="${default_jars[0]}"
-
-default_natives="$(natives_of "$DEFAULT_JAR")"
-[ -n "$default_natives" ] || fail "$(basename "$DEFAULT_JAR") carries no native library at all"
-
-# The default jar is the all-platform CPU variant: more than one OS directory under the prefix.
-default_os_count=$(echo "$default_natives" | sed "s|^${NATIVE_PREFIX}||" | cut -d/ -f1 | sort -u | wc -l)
-[ "$default_os_count" -gt 1 ] \
- || fail "$(basename "$DEFAULT_JAR") carries natives for only $default_os_count OS -- it is supposed to be the all-platform CPU jar"
-echo "default: $(basename "$DEFAULT_JAR") -- $(echo "$default_natives" | wc -l) native(s) across $default_os_count OS"
-
-# Every classifier jar present must be one we asked for; an extra one means the loop and this check
-# disagree about what is being shipped.
-for jar in "$ASSET_DIR"/*-jar-with-dependencies-*.jar; do
- [ -e "$jar" ] || continue
- name="$(basename "$jar")"
- suffix="${name##*-jar-with-dependencies-}"
- found="${suffix%.jar}"
- matched=0
- for c in "${CLASSIFIERS[@]}"; do [ "$c" = "$found" ] && matched=1 && break; done
- [ "$matched" -eq 1 ] || fail "unexpected classifier jar '$name' -- '$found' is not in the requested list"
-done
-
-for c in "${CLASSIFIERS[@]}"; do
- jars=()
- while IFS= read -r j; do jars+=("$j"); done < <(
- find "$ASSET_DIR" -maxdepth 1 -type f -name "*-jar-with-dependencies-${c}.jar" | sort
- )
- [ "${#jars[@]}" -eq 1 ] \
- || fail "expected exactly 1 fat jar for classifier '$c', got ${#jars[@]}: ${jars[*]:-none}"
- jar="${jars[0]}"
-
- natives="$(natives_of "$jar")"
- [ -n "$natives" ] || fail "$(basename "$jar") carries no native library at all"
-
- want="$(expected_path_of "$c")"
- echo "$natives" | grep -qF -- "${NATIVE_PREFIX}${want}" \
- || fail "$(basename "$jar") carries no native under ${NATIVE_PREFIX}${want} -- it does not contain what its name promises: $(echo "$natives" | tr '\n' ' ')"
-
- # Every CPU native of the default jar must still be there: it is the loader's fallback.
- missing="$(comm -23 <(echo "$default_natives") <(echo "$natives"))"
- [ -z "$missing" ] \
- || fail "$(basename "$jar") lost natives of the default jar (the CPU fallback): $(echo "$missing" | tr '\n' ' ')"
-
- echo "ok: $(basename "$jar") -- $(echo "$natives" | wc -l) native(s), matches ${want}"
-done
-
-echo "classifier fat jars verified: ${#CLASSIFIERS[@]} classifier(s) + the default"
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 2034bdc1..faa5234d 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -78,10 +78,6 @@ jobs:
persist-credentials: false
- name: Files equal their templates in bernardladenthin/actions
uses: bernardladenthin/actions/common/check-templates@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- # This repository's own build checks build on actionlib (the setup step puts it on PYTHONPATH).
- - uses: bernardladenthin/actions/common/setup@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- - name: Build-check tests of this repository
- run: python3 -m unittest discover -s .github/buildcheck/tests -t .github
- name: Release gate (every job gates both publish jobs, or release-gate-exemptions.txt says why)
uses: bernardladenthin/actions/common/check-release-gate@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- name: Run scripts parse (bash -n over every bash run script of the workflows and actions)
@@ -178,23 +174,8 @@ jobs:
java-version-file: .java-version
distribution: temurin
cache: maven
- # srcmorph-cli runs the license collector of bernardladenthin/actions while packaging
- # (ACTIONS_DIR, exported here).
- - uses: bernardladenthin/actions/common/setup@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- name: Build
run: mvn --batch-mode --no-transfer-progress -DskipTests package
- - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
- with:
- name: plugin-jars
- # All three reactor modules produce their own jars: srcmorph (core library),
- # srcmorph-cli (includes the jar-with-dependencies fat jar — the CLI's deliverable),
- # and srcmorph-maven-plugin (the Maven plugin, renamed from llamacpp-ai-index-maven-plugin
- # in the migration). The former 4th module — a pom-only relocation-stub for the retired
- # coordinates — was published once and then removed from this reactor; see CLAUDE.md.
- path: |
- srcmorph/target/*.jar
- srcmorph-cli/target/*.jar
- srcmorph-maven-plugin/target/*.jar
test:
name: Test
@@ -209,9 +190,6 @@ jobs:
java-version-file: .java-version
distribution: temurin
cache: maven
- # `verify` packages srcmorph-cli, whose license collector comes from bernardladenthin/actions
- # (ACTIONS_DIR, exported here) -- as in `build`.
- - uses: bernardladenthin/actions/common/setup@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- name: Memory before tests
run: free -h
- name: Test
@@ -321,52 +299,61 @@ jobs:
if-no-files-found: ignore
# ---------------------------------------------------------------------------
- # Fat-jar smoke test — cross-repo standard job, kept in the same shape in
- # BitcoinAddressFinder and java-llama.cpp (see workspace/policies/fat-jar-release-assets.md,
- # "No release asset is attached that CI has not run"). The fat jar is a GitHub-Release asset,
- # and unit tests never touch it: they run off target/classes, so an unrunnable uber jar — no
- # Main-Class, a shade-mangled resource, a missing SLF4J binding — is built and GPG-signed
- # without anything noticing. Signing proves who built it, not that it works.
+ # Jar smoke test. srcmorph publishes no runnable jar: a user builds one from
+ # examples/build-your-own-jar (srcmorph-cli plus the natives of their OS/arch). This job takes
+ # exactly that path against the commit under test: it installs the reactor into the runner's
+ # local repository, builds the example with srcmorph.version and llama.version from the reactor,
+ # and launches the result the way a user would. Unit tests run off target/classes and never
+ # notice an unrunnable jar -- no Main-Class, a missing SLF4J binding, a Java 11 class on a Java 8
+ # floor.
#
- # srcmorph-specific: config_Plan.json uses the `mock` generation provider, so this needs no
- # GGUF model, no GPU and no network — it is the cheapest possible real launch of the CLI.
- # `Plan` is the command that cannot fail for configuration reasons (planOnly, nothing written);
- # `All` over a real source tree fails by design when files exceed the demo model's context
- # window, which would make the smoke non-deterministic rather than more thorough.
+ # config_Plan.json uses the `mock` generation provider, so this needs no GGUF model, no GPU and
+ # no network beyond Maven -- the cheapest possible real launch of the CLI. `Plan` is the command
+ # that cannot fail for configuration reasons (planOnly, nothing written); `All` over a real
+ # source tree fails by design when files exceed the demo model's context window, which would make
+ # the smoke non-deterministic rather than more thorough.
# ---------------------------------------------------------------------------
- smoke-fatjar:
- name: Smoke test fat jar
- needs: [build]
+ smoke-jar:
+ name: Smoke test the user-built jar
+ needs: startgate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
- with:
- name: plugin-jars
- path: fatjar/
- uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6
with:
java-version-file: .java-version
distribution: temurin
+ cache: maven
+ - name: Install the reactor, then build examples/build-your-own-jar against it
+ run: |
+ set -euo pipefail
+ mvn --batch-mode --no-transfer-progress -DskipTests install
+ version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version | tail -n1)
+ llama=$(mvn -q -DforceStdout -pl srcmorph help:evaluate -Dexpression=llama.version | tail -n1)
+ echo "srcmorph $version, llama $llama"
+ mvn --batch-mode --no-transfer-progress -f examples/build-your-own-jar/pom.xml -Dsrcmorph.version="$version" -Dllama.version="$llama" package
+ mkdir -p smoke-jars
+ cp examples/build-your-own-jar/target/srcmorph.jar smoke-jars/
+ cp "srcmorph-maven-plugin/target/srcmorph-maven-plugin-$version.jar" smoke-jars/
# Class-file floor. Production code here targets Java 8, so anything a consumer's JVM
# can load must be major 52 or lower -- a single Java 11 class (logback's
# LogbackServiceProvider was the real case) kills the process with
- # UnsupportedClassVersionError before any of our code runs. The script recurses into the
- # whole download directory, so every jar the build produced is checked, not just the fat
- # jar; module-info.class and META-INF/versions/** are skipped unconditionally because a
- # classpath JVM never loads them. The java/verify-bytecode action of bernardladenthin/actions.
+ # UnsupportedClassVersionError before any of our code runs. srcmorph.jar holds the CLI with
+ # its whole runtime classpath; the plugin jar is what Maven loads. module-info.class and
+ # META-INF/versions/** are skipped because a classpath JVM never loads them. The
+ # java/verify-bytecode action of bernardladenthin/actions.
- name: Verify Java 8 bytecode (no class newer than major 52)
uses: bernardladenthin/actions/java/verify-bytecode@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
with:
max-major: "52"
paths: |
- fatjar
- - name: Run fat-jar smoke test
+ smoke-jars
+ - name: Run the jar
uses: bernardladenthin/actions/java/smoke-fatjar-cli@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
with:
- jar-dir: fatjar
- jar-glob: 'srcmorph-cli-*-jar-with-dependencies.jar'
+ jar-dir: smoke-jars
+ jar-glob: 'srcmorph.jar'
work-dir: examples
marker: 'Main#run end\.'
args: config_Plan.json
@@ -374,7 +361,7 @@ jobs:
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
- name: smoke-fatjar-logs
+ name: smoke-jar-logs
path: |
smoke-out.log
smoke-err.log
@@ -513,7 +500,7 @@ jobs:
needs:
- check-snapshot
- code-style
- - smoke-fatjar
+ - smoke-jar
- plugin-it
- repository-checks
- vmlens
@@ -545,8 +532,7 @@ jobs:
*-SNAPSHOT) echo "OK: -SNAPSHOT version, continuing local snapshot staging." ;;
*) echo "::error::Refusing to publish non-SNAPSHOT version '$VERSION' from the snapshot job. Snapshot publishing requires a -SNAPSHOT version; releases go through the v* tag path."; exit 1 ;;
esac
- # srcmorph-cli runs the license collector of bernardladenthin/actions while packaging
- # (ACTIONS_DIR, exported here).
+ # The collect step runs actionlib of bernardladenthin/actions (on PYTHONPATH from here).
- uses: bernardladenthin/actions/common/setup@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- name: Show effective POM (debug)
run: mvn --batch-mode --no-transfer-progress -P release -DskipUpload=true help:effective-pom
@@ -554,44 +540,8 @@ jobs:
run: mvn --batch-mode --no-transfer-progress -P release -DskipUpload=true deploy -DskipTests
env:
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
- - name: Collect signed Central artifacts (plain/sources/javadoc; fat jars handled next)
+ - name: Collect signed Central artifacts (pom/jar/sources/javadoc)
run: python3 -m actionlib.java.releaseassets central-assets --maven-only
- - name: Build, sign & verify classifier fat jars (GitHub-only assets, never Central)
- env:
- GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
- GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
- run: |
- set -euo pipefail
- # Source of truth: java-llama.cpp's .github/natives.csv for the pinned ${llama.version}:
- # every row of kind `module` (a GPU backend module, additive to the library jar of its
- # platform) except the Android one, which no `java -jar` can use; the cpu-*/metal-*
- # library jars come with llama-platform. Each jar here is ADDED to the default CPU
- # natives (-Dllama.classifier activates the gpu-natives profile), so every classifier
- # fat jar keeps the CPU fallback: ggml loads the module when its runtime is installed.
- python3 -m actionlib.java.releaseassets --list-classifiers 1 > fatjar-classifiers.txt
- mapfile -t CLASSIFIERS < fatjar-classifiers.txt
- build() { mvn -B --no-transfer-progress -pl srcmorph-cli -am -DskipTests -Dmaven.javadoc.skip=true -Dllama.classifier="$1" package; }
- for c in "${CLASSIFIERS[@]}"; do
- build "$c"
- f=$(ls srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar)
- cp "$f" "${f%.jar}-${c}.jar"
- done
- # default CPU build LAST so the unsuffixed fat jar is the all-platform CPU variant
- build ""
- # Collect every fat jar variant, then GPG-sign each via the cross-repo shared script
- # (.github/sign-fatjars.sh; java-llama.cpp dropped its copy with its fat jars) — a .asc per fat jar.
- mkdir -p fatjar-assets
- cp srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies*.jar fatjar-assets/
- bash .github/sign-fatjars.sh fatjar-assets
- # Structural check of what the loop just produced. The smoke-fatjar job LAUNCHES the
- # default jar; the classifier jars cannot be covered that way (a GitHub runner has no
- # such device, and the only command that would load the native library is a real
- # generation), so this asserts instead that each jar is the artifact its name claims --
- # one jar per classifier, the backend directory the name promises, and every default
- # CPU native kept next to it. The backend directory is the point: if -Dllama.classifier=
- # ever stops being wired through, the loop silently ships copies of the CPU build under
- # GPU names and nothing else in this pipeline notices.
- bash .github/verify-classifier-fatjars.sh fatjar-assets "${CLASSIFIERS[@]}"
- name: Finalize and secure the signed publication
id: finalize
uses: bernardladenthin/actions/java/finalize-publication@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
@@ -622,7 +572,7 @@ jobs:
needs:
- check-tag
- code-style
- - smoke-fatjar
+ - smoke-jar
- plugin-it
- repository-checks
- vmlens
@@ -645,8 +595,7 @@ jobs:
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
- # srcmorph-cli runs the license collector of bernardladenthin/actions while packaging
- # (ACTIONS_DIR, exported here).
+ # The collect step runs actionlib of bernardladenthin/actions (on PYTHONPATH from here).
- uses: bernardladenthin/actions/common/setup@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
- name: Show effective POM (debug)
run: mvn --batch-mode --no-transfer-progress -P release -DskipUpload=true help:effective-pom
@@ -654,44 +603,8 @@ jobs:
run: mvn --batch-mode --no-transfer-progress -P release -DskipUpload=true deploy -DskipTests
env:
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
- - name: Collect signed Central artifacts (plain/sources/javadoc; fat jars handled next)
+ - name: Collect signed Central artifacts (pom/jar/sources/javadoc)
run: python3 -m actionlib.java.releaseassets central-assets --maven-only
- - name: Build, sign & verify classifier fat jars (GitHub-only assets, never Central)
- env:
- GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
- GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
- run: |
- set -euo pipefail
- # Source of truth: java-llama.cpp's .github/natives.csv for the pinned ${llama.version}:
- # every row of kind `module` (a GPU backend module, additive to the library jar of its
- # platform) except the Android one, which no `java -jar` can use; the cpu-*/metal-*
- # library jars come with llama-platform. Each jar here is ADDED to the default CPU
- # natives (-Dllama.classifier activates the gpu-natives profile), so every classifier
- # fat jar keeps the CPU fallback: ggml loads the module when its runtime is installed.
- python3 -m actionlib.java.releaseassets --list-classifiers 1 > fatjar-classifiers.txt
- mapfile -t CLASSIFIERS < fatjar-classifiers.txt
- build() { mvn -B --no-transfer-progress -pl srcmorph-cli -am -DskipTests -Dmaven.javadoc.skip=true -Dllama.classifier="$1" package; }
- for c in "${CLASSIFIERS[@]}"; do
- build "$c"
- f=$(ls srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar)
- cp "$f" "${f%.jar}-${c}.jar"
- done
- # default CPU build LAST so the unsuffixed fat jar is the all-platform CPU variant
- build ""
- # Collect every fat jar variant, then GPG-sign each via the cross-repo shared script
- # (.github/sign-fatjars.sh; java-llama.cpp dropped its copy with its fat jars) — a .asc per fat jar.
- mkdir -p fatjar-assets
- cp srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies*.jar fatjar-assets/
- bash .github/sign-fatjars.sh fatjar-assets
- # Structural check of what the loop just produced. The smoke-fatjar job LAUNCHES the
- # default jar; the classifier jars cannot be covered that way (a GitHub runner has no
- # such device, and the only command that would load the native library is a real
- # generation), so this asserts instead that each jar is the artifact its name claims --
- # one jar per classifier, the backend directory the name promises, and every default
- # CPU native kept next to it. The backend directory is the point: if -Dllama.classifier=
- # ever stops being wired through, the loop silently ships copies of the CPU build under
- # GPU names and nothing else in this pipeline notices.
- bash .github/verify-classifier-fatjars.sh fatjar-assets "${CLASSIFIERS[@]}"
- name: Finalize and secure the signed publication
id: finalize
uses: bernardladenthin/actions/java/finalize-publication@44390bd464395f560f4170d2c4bfb7a6348541b3 # v1.1.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29cba730..6c1d3968 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,32 @@ The release procedure (prompt template and step-by-step instructions) lives in [
## [Unreleased]
+### Removed
+- **No more fat jars on the GitHub release.** Up to 1.3.0 every release attached 15 signed
+ `srcmorph-cli--jar-with-dependencies[-].jar` files (a CPU jar for every
+ desktop platform plus one per llama GPU module); they were never on Maven Central. There are too
+ many OS, CPU and GPU combinations to ship one per machine, so from 1.4.0 you build the runnable jar
+ yourself (see Changed). Gone with them: the `package`-bound assembly in `srcmorph-cli`, its
+ `fat-jar.xml` descriptor, the runtime license collector that only those jars needed (and with it
+ the build's dependency on a bernardladenthin/actions checkout and `third-party-notices/`), and in
+ CI the classifier build loop, `sign-fatjars.sh`, `verify-classifier-fatjars.sh` and its tests.
+ `.github/release-assets.json` names no files beyond the Maven artifacts.
+
+### Changed
+- **`examples/windows-fatjar/` is now `examples/build-your-own-jar/`, for every platform.** Its
+ `pom.xml` takes `srcmorph-cli` from Maven Central, picks the natives jar of the build machine's
+ OS/arch with an OS-activated profile (`-Dnatives=` overrides it, and the enforcer
+ names the property when no profile matches), adds one GPU module with `-Dgpu=`, and
+ packs everything with the logging defaults into `target/srcmorph.jar`. `build.sh`/`build.bat`
+ pass their arguments to Maven; `run.sh`/`run.bat` start the jar. The eight `examples/run_*`
+ scripts now run that jar instead of `srcmorph-cli/target/*-jar-with-dependencies.jar`. The README
+ mentions JBang as a shortcut (`srcmorph-cli`'s jar names its main class); Maven is the supported
+ path.
+- **CI: `smoke-fatjar` is now `smoke-jar` and tests the user's path.** It installs the reactor,
+ builds `examples/build-your-own-jar` against it (`srcmorph.version` and `llama.version` read from
+ the reactor), checks the Java 8 bytecode floor over that jar and the plugin jar, and runs `Plan`.
+ The example pom is therefore under test on every pull request.
+
## [1.3.0] - 2026-10-26
### Added
diff --git a/CLAUDE.md b/CLAUDE.md
index 858c07ce..ade42267 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -59,13 +59,14 @@ llamacpp-ai-index-maven-plugin/ (repo root; reactor parent)
│ ├── indexer/ SourceFileIndexer, PackageIndexer, ProjectIndexer, AiFieldGenerationSupport, ...
│ ├── document/ prompt/ provider/ support/
│ └── src/test/resources/SmolLM2-135M-Instruct-Q3_K_M.gguf (real-model tests live here)
-├── srcmorph-cli/ CLI net.ladenthin:srcmorph-cli (fat jar = deliverable)
+├── srcmorph-cli/ CLI net.ladenthin:srcmorph-cli (thin jar; users build the runnable one)
│ └── src/main/java/net/ladenthin/srcmorph/cli/
│ ├── Main.java BitcoinAddressFinder cli/Main.java pattern
│ └── configuration/ CConfiguration + CCommand (BAF public-field style)
├── srcmorph-maven-plugin/ Maven plugin net.ladenthin:srcmorph-maven-plugin, goalPrefix srcmorph
│ └── src/main/java/net/ladenthin/maven/srcmorph/mojo/ (4 goal mojos + the abstract AbstractAiIndexMojo base; renamed package/properties)
├── examples/ config_*.json/.yaml + run_*.sh/.bat + simplelogger.properties
+│ └── build-your-own-jar/ pom.xml + build/run scripts: srcmorph-cli + this machine's natives -> srcmorph.jar
├── docs/ RELEASE.md + the ai-index model-benchmark writeups
└── .github/workflows/ CI adapted to the 3-module reactor
```
@@ -166,16 +167,21 @@ CLI driven by a single JSON or YAML configuration file:
`@Parameter` fields — a JSON/YAML key under `srcMorph` reads identically to the matching plugin XML
element. `CCommand` is `Plan | GenerateFileIndex | AggregatePackages | AggregateProject | All |
Calibrate`; the default is `Plan` (safe: no model load, nothing written).
-- The fat jar (`srcmorph-cli--jar-with-dependencies.jar`, main class
- `net.ladenthin.srcmorph.cli.Main`) is bound **unconditionally** to the `package` phase (a deliberate
- divergence from BAF's `-P assembly` opt-in — for this module the fat jar IS the deliverable).
-- Ships its own SLF4J binding (`org.slf4j:slf4j-simple`, runtime scope) — unlike the library
+- **No runnable jar is built or published here** (since 1.4.0). `mvn package` produces only the thin
+ `srcmorph-cli` jar (its manifest names `net.ladenthin.srcmorph.cli.Main`, so `jbang` can run it).
+ Users build the runnable jar with `examples/build-your-own-jar/pom.xml`: `srcmorph-cli` from
+ Central, `llama-platform` excluded, the one natives jar of the OS/arch picked by an OS-activated
+ profile (`-Dnatives=` overrides), an optional GPU module via `-Dgpu=`, packed by
+ `jar-with-dependencies` into `target/srcmorph.jar`. Up to 1.3.0 this module built a fat jar on
+ every `package` and CI released 15 of them on GitHub; there are too many OS/CPU/GPU combinations
+ to keep doing that.
+- Declares its own SLF4J binding (`org.slf4j:slf4j-simple`, runtime scope) — unlike the library
(consumer picks any SLF4J binding) and the plugin (gets one for free from Maven's own
`maven-slf4j-provider`), a standalone `java -jar` process needs to bring its own or every log line is
- silently dropped. It is **not** logback: see "Java 8 bytecode floor" below. Fat-jar defaults live in
- `srcmorph-cli/src/main/assembly-resources/simplelogger.properties`, added by the custom assembly
- descriptor `srcmorph-cli/src/assembly/fat-jar.xml` (the predefined `jar-with-dependencies` ref
- verbatim plus that one file) so they never reach the published `srcmorph-cli` jar.
+ silently dropped. It is **not** logback: see "Java 8 bytecode floor" below. The logging defaults
+ (INFO, stdout, timestamps) are `examples/build-your-own-jar/simplelogger.properties`, which that
+ pom packs into the user's jar; the published `srcmorph-cli` jar carries none, so an embedder's own
+ configuration is never overridden.
- **Architecture rules** (`CliArchitectureTest`): `cliIsLeaf` (nothing else in the reactor may depend on
this module — it is the leaf-most consumer), `noPublicMutableFields` (with the `configuration`
package carve-out), `noSystemExit`, `mavenFree` (must never depend on the Maven Plugin API — that
@@ -272,7 +278,7 @@ keep working.
```bash
mvn compile # Compiles every module (Java + generates nothing native; pure Java reactor)
mvn test # Runs every module's tests
-mvn package # Builds all four reactor projects: parent pom + 3 jars (incl. the CLI's fat jar)
+mvn package # Builds all four reactor projects: parent pom + 3 jars
mvn install # Installs all four into ~/.m2 (needed before iterating on a single module — see below)
```
@@ -307,8 +313,9 @@ mvn -pl srcmorph-maven-plugin srcmorph:generate -P srcmorph-selftest
### Run the CLI
```bash
-mvn -pl srcmorph-cli package
-java -jar srcmorph-cli/target/srcmorph-cli-1.4.0-SNAPSHOT-jar-with-dependencies.jar examples/config_All.json
+mvn install -DskipTests
+mvn -f examples/build-your-own-jar/pom.xml package -Dsrcmorph.version=1.4.0-SNAPSHOT
+java -jar examples/build-your-own-jar/target/srcmorph.jar examples/config_All.json
```
See `examples/` (repo root) for ready-to-run `config_*.json`/`.yaml` + paired `run_*.sh`/`.bat`
@@ -480,7 +487,8 @@ and both are easy to undo by accident:
fails every build with `Could not load type:
org.checkerframework.framework.qual.DoesNotUnrefineReceiver`. Processor and qualifiers must share a
major version. `provided` satisfies both: 4.3.0 where the checker needs it, and excluded from
- consumers' graph **and** from the fat jar (`jar-with-dependencies` takes scope `runtime`).
+ consumers' graph **and** from any `jar-with-dependencies` built from it (that descriptor takes
+ scope `runtime`).
`true` alone would not have been enough — that descriptor filters on scope
only. Safe because no source in this reactor imports `org.checkerframework`.
@@ -493,7 +501,7 @@ exceeds `max-major`:
with:
max-major: "52"
paths: | # jars or directories, one per line
- fatjar
+ smoke-jars
allow: | # optional: ':' globs to tolerate
```
@@ -502,7 +510,8 @@ whole artifact set. `module-info.class` and `META-INF/versions/**` are skipped u
a classpath JVM never loads either, which is why a `release 9` `module-info` is fine. `allow`
takes globs matched against `:` for anything else that must be
tolerated. Exit codes: 0 clean, 1 violations, **2 nothing to scan** (an empty input is a failure,
-never a pass). Wired into the `smoke-fatjar` job at `max-major` 52.
+never a pass). Wired into the `smoke-jar` job at `max-major` 52, over the user-built `srcmorph.jar`
+(the CLI with its whole runtime classpath) and the plugin jar.
## Javadoc Conventions
@@ -543,7 +552,7 @@ Run PIT with the lifecycle prefix. Reactor-wide (what CI does):
`-f srcmorph/pom.xml`. All three modules gate at `mutationThreshold` 100 — `srcmorph` (830 mutations),
`srcmorph-maven-plugin` (62, the five mojo classes) and `srcmorph-cli` (16). The CLI's
`Main.main(String[])` is the one documented exclusion: it is the process entry point, and the
-`smoke-fatjar` release-gating job already runs the real `java -jar` artifact and asserts
+`smoke-jar` release-gating job already runs the real `java -jar` artifact and asserts
`Main#run end.` in its output, which is an end-to-end check a unit mutant cannot reach.
**`LlamaCppJniAiGenerationProvider` is split so the gate can reach it, and the split is load-bearing.**
@@ -593,17 +602,14 @@ clear of the JPMS module-mode javadoc trap that bit BAF. **Before raising the Ja
level to ≥ 9 in any module, read**
[`../workspace/policies/jpms-module-descriptor.md`](../workspace/policies/jpms-module-descriptor.md).
-## Fat-jar release assets
-
-The `srcmorph-cli` fat jar (`jar-with-dependencies`) is a **GitHub-Release asset only — never
-Maven Central** (`srcmorph-cli/pom.xml` sets `false`), attached with a detached
-GPG `.asc`. CI builds **one fat jar per desktop GPU module jar of `net.ladenthin:llama`** (the default
-CPU jar + 14: `cuda13`, `vulkan`, `opencl`, `rocm`, `sycl`, `openvino` per OS/arch -- the `CLASSIFIERS`
-list in `publish.yml`, every `module` row of java-llama.cpp's `natives.csv` but the Android one; no
-`msvc`, `sycl-fp*` or 32-bit jar exists since llama 6.0.0) and signs them via `.github/sign-fatjars.sh`
-(srcmorph's own since java-llama.cpp dropped its fat jars with its modular natives). The convention +
-per-repo shapes + the classifier keep-in-sync rule are documented
-in [`../workspace/policies/fat-jar-release-assets.md`](../workspace/policies/fat-jar-release-assets.md).
+## Release assets
+
+srcmorph releases **only the Maven artifacts**: the four POMs and the three modules' jar, sources
+and javadoc, each with a detached GPG `.asc`, on Central and as GitHub release assets.
+`.github/release-assets.json` is `{"schema": 1}`: no extra file rules. Up to 1.3.0 the publish jobs
+also built, signed and attached one `srcmorph-cli` fat jar per desktop GPU module of
+`net.ladenthin:llama` (15 per release, never on Central); 1.4.0 dropped them together with
+`sign-fatjars.sh`, `verify-classifier-fatjars.sh` and the license collector that only they needed.
Every publication goes through the one gate of [bernardladenthin/actions](https://github.com/bernardladenthin/actions) (full text: [docs/publication.md](https://github.com/bernardladenthin/actions/blob/main/docs/publication.md)).
`check-snapshot` / `check-tag` run `java/check-release-ref`: a release publishes only from the tag
`v` of a non-SNAPSHOT version, a snapshot only a `-SNAPSHOT` version and never from a tag.
@@ -621,14 +627,13 @@ complete upload and starts as a draft; a versioned tag is refused), `central-sna
(environment, permissions, concurrency); `java/check-publication` checks their wiring in every pull
request.
-The publish jobs stage deploy-signed Maven outputs with `--maven-only` before the classifier
-loop rebuilds `target/`. The final collector uses `--maven-assets central-assets` and requires
-all configured fat jars from `fatjar-assets/`. The loop's classifier list comes from file rule 1
-in `.github/release-assets.json` via `--list-classifiers 1` (default CPU is built last).
+The publish jobs collect the deploy-signed Maven outputs with `--maven-only` into
+`central-assets/`, and `finalize-publication` reads them from there (`--maven-assets`).
-**srcmorph-specific smoke.** The cross-repo rule "no release asset is attached that CI has not run"
-is implemented here by the `smoke-fatjar` job (`needs: [build]`, gates both publish jobs): it
-downloads the `plugin-jars` artifact and runs the `java/smoke-fatjar-cli` action of [bernardladenthin/actions](https://github.com/bernardladenthin/actions)
+**srcmorph-specific smoke.** The `smoke-jar` job (`needs: startgate`, gates both publish jobs) takes
+the path a user takes: `mvn install` of the reactor, then `examples/build-your-own-jar` with
+`srcmorph.version` and `llama.version` read from the reactor, so the example pom itself is under test
+on every pull request. It then runs the `java/smoke-fatjar-cli` action of [bernardladenthin/actions](https://github.com/bernardladenthin/actions)
(BAF runs it too) from `examples/` against `config_Plan.json`, asserting exit 0 plus `Main#run end.` in the output.
`Plan` with the `mock` provider needs no GGUF, no GPU and no network, which makes this the cheapest
possible real launch of the CLI. **Do not "strengthen" it to `config_All.json` over a real source
@@ -676,15 +681,6 @@ in **`.github/actions-config.json`**:
- **publication**: the publication jobs are wired as the actions expect, and a dry run of
`.github/release-assets.json` against the real POMs collects exactly what it promises.
-**Packaging srcmorph-cli needs bernardladenthin/actions.** Its `package` runs the license collector
-(`python3 -m actionlib.java.thirdpartylicenses`): CI provides it through `common/setup`
-(`ACTIONS_DIR`) and the reusable SonarQube workflow, locally a checkout next to this repository
-(`../actions`) does; when neither is there, the collector call says so and where it looked, instead
-of a Python traceback.
-
-**srcmorph's own build checks** stay here (`.github/buildcheck/`, the classifier fat-jar check):
-`python3 -m unittest discover -s .github/buildcheck/tests -t .github`, run by `repository-checks`.
-
The JDK every workflow uses is `.java-version` (setup-java's `java-version-file`), a template of
the `java` set. Why a repository instead of copies:
[`../workspace/crossrepostatus.md`](../workspace/crossrepostatus.md).
diff --git a/README.md b/README.md
index 3ad1563a..aaafbf22 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ diagrams, or even source-to-source transformations.
| Module | Artifact | What it is |
|---|---|---|
| [`srcmorph/`](srcmorph/README.md) | `net.ladenthin:srcmorph` | The core library: a framework-free Java API (no Maven dependency) — the shared `SrcMorphConfiguration` plus the `generate`/`aggregate-packages`/`aggregate-project`/`calibrate` engines. Use this to embed srcmorph in your own tooling. |
-| [`srcmorph-cli/`](srcmorph-cli/README.md) | `net.ladenthin:srcmorph-cli` | A standalone CLI driven by one JSON or YAML configuration file (ships as a `java -jar`-ready fat jar). No Maven project required. |
+| [`srcmorph-cli/`](srcmorph-cli/README.md) | `net.ladenthin:srcmorph-cli` | A standalone CLI driven by one JSON or YAML configuration file. You build the runnable jar for your machine with one `mvn package` ([`examples/build-your-own-jar/`](examples/build-your-own-jar/)). |
| [`srcmorph-maven-plugin/`](srcmorph-maven-plugin/README.md) | `net.ladenthin:srcmorph-maven-plugin` | The Maven plugin, a thin wrapper around `srcmorph` — **renamed** from `net.ladenthin:llamacpp-ai-index-maven-plugin` (goal prefix `srcmorph`, was `ai-index`; properties `srcmorph.*`, were `aiIndex.*`). |
All three modules (plus the reactor parent pom) are released together from this one repository at
@@ -162,19 +162,29 @@ model recommendations: [`srcmorph-maven-plugin/README.md`](srcmorph-maven-plugin
### 2. Standalone CLI
+srcmorph publishes no runnable jar: there are too many OS, CPU and GPU combinations to ship one per
+machine. You build your own with Maven from
+[`examples/build-your-own-jar/`](examples/build-your-own-jar/): its `pom.xml` takes `srcmorph-cli`
+from Maven Central plus exactly the llama natives of your OS/arch (picked automatically), and
+optionally one GPU module.
+
```bash
-java -jar srcmorph-cli--jar-with-dependencies.jar examples/config_All.json
+cd examples/build-your-own-jar
+./build.sh # or build.bat; add e.g. -Dgpu=vulkan-linux-x86-64
+java -jar target/srcmorph.jar ../config_All.json
```
Every `examples/config_*.json`/`.yaml` file works out of the box with the `mock` provider (no GGUF
model required) — see [`examples/`](examples/) for the full set plus paired `run_*.sh`/`run_*.bat`
launcher scripts, and [`srcmorph-cli/README.md`](srcmorph-cli/README.md) for the config-file
-reference.
-
-**To build a fat jar with only your machine's natives** (for example the CPU and Vulkan classifiers
-on Windows), see [`docs/windows-intel-core-ultra.md`](docs/windows-intel-core-ultra.md). It covers a
-pom, `build.bat` and `run.bat` in [`examples/windows-fatjar/`](examples/windows-fatjar/), and a
-commented configuration.
+reference. A walkthrough for a Windows laptop with Vulkan is in
+[`docs/windows-intel-core-ultra.md`](docs/windows-intel-core-ultra.md).
+
+Without building anything, [JBang](https://www.jbang.dev/) can run the CLI straight from Maven
+Central. It resolves `srcmorph-cli` with the CPU natives of every desktop platform, and a GPU
+module is one more `--deps` jar:
+`jbang --deps net.ladenthin:llama:6.0.0:vulkan-linux-x86-64 net.ladenthin:srcmorph-cli: config.json`.
+Maven is the supported path; JBang is a shortcut.
### 3. Library dependency
diff --git a/REUSE.toml b/REUSE.toml
index af0c9fb2..014fe01b 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -30,26 +30,6 @@ precedence = "aggregate"
SPDX-FileCopyrightText = "HuggingFaceTB"
SPDX-License-Identifier = "Apache-2.0"
-# Upstream license texts of runtime dependencies whose JARs carry none, bundled into the CLI fat
-# jar by the license collector of bernardladenthin/actions (--supplemental, third-party-notices/notices.json).
-[[annotations]]
-path = "third-party-notices/notices.json"
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2026 Bernard Ladenthin "
-SPDX-License-Identifier = "Apache-2.0"
-
-[[annotations]]
-path = "third-party-notices/jspecify-1.0.1/**"
-precedence = "override"
-SPDX-FileCopyrightText = "2018-2020 The JSpecify Authors"
-SPDX-License-Identifier = "Apache-2.0"
-
-[[annotations]]
-path = "third-party-notices/snakeyaml-2.5/**"
-precedence = "override"
-SPDX-FileCopyrightText = "2008 SnakeYAML"
-SPDX-License-Identifier = "Apache-2.0"
-
[[annotations]]
path = "LICENSE"
precedence = "aggregate"
diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md
index 3c2928a6..13f5c84c 100644
--- a/THIRD_PARTY.md
+++ b/THIRD_PARTY.md
@@ -8,8 +8,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0
The project is Apache-2.0. Logback in the concrete core dependency declaration is
test-scoped; its parent dependencyManagement pin is not a runtime dependency.
-The runnable CLI uses SLF4J Simple and bundles the selected llama runtime/native artifacts.
-Those native artifacts have additional third-party contents requiring their own review.
+srcmorph publishes no runnable jar. The jar a user builds from `examples/build-your-own-jar`
+bundles SLF4J Simple and the llama natives they select; those native artifacts have additional
+third-party contents requiring their own review, and redistributing such a jar is the builder's
+responsibility.
## Review evidence
@@ -22,10 +24,7 @@ For unchanged Maven components, source is available from
as `--sources.jar`; POMs and upstream source repositories provide provenance.
Preserve copyright, license and NOTICE files when redistributing dependencies.
-Runnable BAF/srcmorph CLI packaging uses Python 3.8+ to collect original dependency
-legal files before the project JAR and fat JAR are assembled. Override `-Dlicense.python=python`
-if that is the Python command on the build host. Legal files are under
-`META-INF/third-party/dependencies/-/`.
-`inventory.json` maps exact JAR hashes/classifiers to preserved files and also lists JARs
-without embedded legal material, which require review or supplemental notices.
+Up to 1.3.0 srcmorph also released fat jars of the CLI, with the original dependency legal files
+collected under `META-INF/third-party/dependencies/`. From 1.4.0 there are none, and that collection
+step is gone with them.
This source-location statement does not clear unknown proprietary or historical Git findings.
diff --git a/TODO.md b/TODO.md
index bccdd19a..4b911b8f 100644
--- a/TODO.md
+++ b/TODO.md
@@ -10,48 +10,16 @@ everything below is genuinely still open.
## Open
-- **After the 1.3.0 release: build `examples/windows-fatjar` once against Central only**, with an
- empty local repository. The release PR set it to `1.3.0` and removed the `central-snapshots`
- block; the same check against the `1.3.0-SNAPSHOT` from Publish run
- [40367427445](https://github.com/bernardladenthin/srcmorph/actions/runs/40367427445) passed
- (2026-10-26, 32 MiB jar), so a failure now would point at the release upload, not the example.
-
-- **The GPU classifier fat jars are verified structurally, never launched.** Since 1.2.0
- `.github/verify-classifier-fatjars.sh` asserts each is the artifact its name claims (one jar per
- classifier, the promised backend directory next to the default CPU natives, so a broken
- `-Dllama.classifier=` cannot silently ship CPU-only builds under GPU names). What it cannot assert
- is that the jar *works*: a GitHub-hosted runner has no CUDA/ROCm/SYCL/OpenVINO device, and the only
- command that would load the native library is a real generation. Closing this needs hardware —
- a self-hosted runner, or a manual pre-release pass on one GPU box per backend. Worth knowing which
- half is covered before reading the green check as "the CUDA jar runs".
-
- **`cuda13-windows-x86-64` has had that pass (2026-10-23, against `llama 6.0.0-SNAPSHOT`).** On an
- RTX 3070 Laptop the `-P gpu-cuda` fat jar (258 MB, carrying `Windows/x86_64/cuda13/` next to the six
- CPU library directories) ran a real `GenerateFileIndex` over the production prompt set through the
- CLI *and* through `srcmorph:generate`, both at 3474 t/s prefill / 86.8 t/s decode, with the loader
- naming the module (`with GPU module(s) cuda13`) and ggml registering `[CUDA, RPC, CPU]`. The same
- config on the default CPU fat jar is 63 / 7.1 with `[RPC, CPU]`, so the jar demonstrably loads its
- backend rather than falling back. **Still unlaunched: `rocm`, `sycl`, `openvino` and `vulkan`** —
- no AMD, Intel or second GPU here. Vulkan is reachable on any NVIDIA box and is the cheapest one to
- add next.
-
-- **Drop the fat jars in 1.4.0 (owner's decision, 2026-10-26).** 1.3.0 is the last release with
- fat jars on GitHub; they were never on Central (`false`, `--maven-only`, and no
- `"central": true` in `.github/release-assets.json`), so removing them touches only the GitHub
- release. Users build their own jar locally, as `examples/windows-fatjar` shows. Since its
- modular natives java-llama.cpp ships no fat jar at all: the thin jar on Central is the deliverable,
- a GPU is `jbang --deps net.ladenthin:llama:: …`, and its smoke jobs launch the
- published jars on the classpath a consumer resolves. The same shape fits `srcmorph-cli`
- (`jbang net.ladenthin:srcmorph-cli: config.json` resolves slf4j-simple and llama-platform from its
- pom; a GPU is one more `--deps` jar). Removing them drops the default and 14 signed classifier fat
- jars per release, the build loop in `publish.yml`, `sign-fatjars.sh`, `verify-classifier-fatjars.sh`
- and `.github/release-assets.json`'s file rules. It also closes a gap the current shape has: the
- artifact `smoke-fatjar` launches is the fat jar built by the `build` job (a GitHub asset, rebuilt by
- the publish jobs' loop), while the `srcmorph-cli` jar that goes to Central is never launched by any
- job. The smoke has to move to the thin jar plus its resolved runtime classpath (what
- java-llama.cpp's smoke sets are), so it tests what is published. `README.md`,
- `srcmorph-cli/README.md`, `CLAUDE.md` and the eight `examples/run_*` scripts that name the fat jar
- go with it.
+- **No GPU module is launched in CI.** `smoke-jar` builds `examples/build-your-own-jar` with the CPU
+ natives only and runs `Plan`; a GitHub-hosted runner has no CUDA/ROCm/SYCL/OpenVINO device, and
+ only a real generation loads the native library. Closing this needs hardware: a self-hosted runner,
+ or a manual pre-release pass on one GPU box per backend (`build.sh -Dgpu=`, then a
+ `GenerateFileIndex` with `gpuLayers` set). **`cuda13-windows-x86-64` has had that pass** (2026-10-23,
+ `llama 6.0.0-SNAPSHOT`, still as a fat jar): on an RTX 3070 Laptop a real `GenerateFileIndex` through
+ the CLI *and* through `srcmorph:generate` ran at 3474 t/s prefill / 86.8 t/s decode with ggml
+ registering `[CUDA, RPC, CPU]`, against 63 / 7.1 with `[RPC, CPU]` on the CPU jar. **Still
+ unlaunched: `rocm`, `sycl`, `openvino` and `vulkan`** — no AMD, Intel or second GPU here. Vulkan is
+ reachable on any NVIDIA box and is the cheapest one to add next.
- **jqwik pin policy** — see [`../workspace/policies/jqwik-prompt-injection.md`](../workspace/policies/jqwik-prompt-injection.md). `jqwik.version ≤ 1.9.3` is mandatory (declared in `srcmorph/pom.xml`, the only reactor module with a jqwik test dependency).
diff --git a/docs/RELEASE.md b/docs/RELEASE.md
index b23c25bc..718b3f09 100644
--- a/docs/RELEASE.md
+++ b/docs/RELEASE.md
@@ -23,7 +23,10 @@ procedure:
- `srcmorph-maven-plugin/README.md` — its own `` snippet;
- `CLAUDE.md` — the reactor version and "Last released version" in the overview and layout blocks;
- `SECURITY.md` — the supported-release-line table;
- - `CHANGELOG.md` — the `## [X.Y.Z] - ` heading and the two link refs at the bottom.
+ - `CHANGELOG.md` — the `## [X.Y.Z] - ` heading and the two link refs at the bottom;
+ - `examples/build-your-own-jar/pom.xml` — `srcmorph.version` (and `llama.version` when the
+ reactor's moved). It names what a user resolves from Central, so it carries the release, never
+ the `-SNAPSHOT`; CI overrides both with the reactor's values in `smoke-jar`.
`grep -rn "" --include="*.md" .` finds what was missed; run it before tagging,
not after.
diff --git a/docs/windows-intel-core-ultra.md b/docs/windows-intel-core-ultra.md
index 3c23d4c7..0cda258b 100644
--- a/docs/windows-intel-core-ultra.md
+++ b/docs/windows-intel-core-ultra.md
@@ -4,51 +4,50 @@ SPDX-FileCopyrightText: 2026 Bernard Ladenthin
SPDX-License-Identifier: MIT
-->
-# srcmorph on Windows: build your own fat jar, then run it
+# srcmorph on Windows: build your own jar, then run it
-Two steps, four files, all in [`examples/windows-fatjar/`](../examples/windows-fatjar/):
+Two steps, all in [`examples/build-your-own-jar/`](../examples/build-your-own-jar/):
| File | What it does |
|---|---|
-| `pom.xml` | names srcmorph and **the llama natives this machine needs** (the classifiers) |
-| `build.bat` | one `mvn package` on a JDK 8 → `target\srcmorph.jar` |
+| `pom.xml` | srcmorph from Maven Central plus **the llama natives this machine needs** |
+| `build.bat` | one `mvn package` → `target\srcmorph.jar` |
| `srcmorph.yaml` | what to index, with which prompt and which model |
| `run.bat` | `java -jar target\srcmorph.jar srcmorph.yaml` |
-Needed on the machine: a JDK 8 or newer, Maven, and a GGUF model. Nothing else: no C++ toolchain and
-no GPU SDK. The Vulkan module uses the GPU driver that is already installed.
+Needed on the machine: a JDK 8 or newer and Maven on `PATH`, and a GGUF model. Nothing else: no C++
+toolchain and no GPU SDK. The Vulkan module uses the GPU driver that is already installed.
-Why build the jar yourself: the fat jar on the GitHub release carries the CPU natives of every
-desktop platform (~111 MiB with Vulkan). Your own carries only the ones you name (32 MiB for the two
-below), and you choose the GPU module.
+Why build the jar yourself: srcmorph publishes no runnable jar, because no set of pre-built jars
+covers every OS, CPU and GPU. Yours carries only the natives of this machine (32 MiB for the two
+below) and the GPU module you choose.
> [!NOTE]
> This is for **srcmorph 1.3.0** (on `net.ladenthin:llama` 6.0.0); both resolve from Maven Central.
-## Step 1 — pick the classifiers, build the jar
+## Step 1 — pick the GPU module, build the jar
-The pom declares one **library** jar for the OS/arch and, optionally, **GPU module** jars of the same
-OS/arch. For a Windows x86-64 machine with an Intel Core Ultra (Arc iGPU), as shipped:
+The pom takes one **library** jar for the OS/arch, picked by a Maven profile (`cpu-windows-x86-64`
+here), and optionally one **GPU module** jar of the same OS/arch. For a Windows x86-64 machine with an
+Intel Core Ultra (Arc iGPU):
| Classifier | Size | Why |
|---|--:|---|
| `cpu-windows-x86-64` | 16.6 MiB | `jllama.dll`, llama/ggml and all CPU variants; ggml picks the best one at start (`alderlake` on a Core Ultra: AVX2 + AVX-VNNI, no AVX-512) |
| `vulkan-windows-x86-64` | 12.0 MiB | the iGPU (or any GPU) through the driver's `vulkan-1.dll`; without a working driver ggml skips it and runs on the CPU |
-Other machines swap the classifiers: for example `cuda13-windows-x86-64` for an NVIDIA GPU (it needs
-the CUDA 13 runtime on `PATH`), or `cpu-linux-x86-64` on Linux. The full table is in the java-llama.cpp
-README, [Choosing the natives
+Other machines pick another GPU module: for example `cuda13-windows-x86-64` for an NVIDIA GPU (it
+needs the CUDA 13 runtime on `PATH`). On Linux and macOS the same pom picks the library jar of that
+OS. The full table is in the java-llama.cpp README, [Choosing the natives
jars](https://github.com/bernardladenthin/java-llama.cpp#choosing-the-natives-jars).
`openvino-*` is opt-in and can be slower than the CPU. Leave it out unless you want to experiment
with the NPU.
-Set `JAVA_HOME` in `build.bat` to your JDK, then:
-
```bat
-build.bat
+build.bat -Dgpu=vulkan-windows-x86-64
```
-The result is `target\srcmorph.jar`.
+The result is `target\srcmorph.jar`. Without `-Dgpu=...` it holds the CPU natives only (20 MiB).
## Step 2 — the configuration, then run
@@ -87,7 +86,7 @@ load_backends_next_to_this_library: 3 backend(s) registered [RPC, Vulkan, CPU],
[jllama] native backend 'cpu' loaded from with GPU module(s) vulkan; 18 file(s) in place for ggml to load
```
-- **`with no GPU module`**: the Vulkan jar is not in the build.
+- **`with no GPU module`**: the jar was built without `-Dgpu=vulkan-windows-x86-64`.
- **Two Vulkan devices** (an iGPU and a discrete GPU): choose one with `devices: Vulkan1` in the
`aiDefinition`.
- **`gpuLayers: 0`**, or `-Dnet.ladenthin.llama.backend=cpu` before `-jar` in `run.bat`: runs on the
@@ -103,7 +102,8 @@ load_backends_next_to_this_library: 3 backend(s) registered [RPC, Vulkan, CPU],
## Tested
-On Windows, `cmd.exe`, srcmorph 1.3.0-SNAPSHOT:
+On Windows, `cmd.exe`, srcmorph 1.3.0-SNAPSHOT, with the former `examples/windows-fatjar` (the same
+CPU + Vulkan natives, fixed in the pom instead of chosen by profile and `-Dgpu`):
- `build.bat` ran on a JDK 8 (1.8.0_504) and produced a 32 MiB jar holding only the Windows x86-64
natives.
- `Plan` ran on the same JDK 8.
@@ -111,3 +111,7 @@ On Windows, `cmd.exe`, srcmorph 1.3.0-SNAPSHOT:
machine has no JDK 8 installed.
- The test machine is a Ryzen with an AMD iGPU and an RTX 3070, which is why the log above was
written down for a Core Ultra rather than copied from it.
+
+With `examples/build-your-own-jar` and srcmorph 1.3.0 from Maven Central (2026-10-26, JDK 21, empty
+local repository): `mvn package` picked `cpu-windows-x86-64` and gave a 20 MiB jar,
+`-Dgpu=vulkan-windows-x86-64` a 32 MiB jar, and `Plan` ran.
diff --git a/examples/windows-fatjar/build.bat b/examples/build-your-own-jar/build.bat
similarity index 60%
rename from examples/windows-fatjar/build.bat
rename to examples/build-your-own-jar/build.bat
index 3645b7a8..da0e56ef 100644
--- a/examples/windows-fatjar/build.bat
+++ b/examples/build-your-own-jar/build.bat
@@ -3,9 +3,7 @@ REM
REM SPDX-License-Identifier: MIT OR Apache-2.0
@echo off
-rem Builds target\srcmorph.jar from pom.xml (the CLI plus the natives the pom names). Adapt JAVA_HOME.
+rem Builds target\srcmorph.jar from pom.xml (the CLI plus the natives of this OS/arch).
+rem Extra arguments go to Maven, e.g. build.bat -Dgpu=vulkan-windows-x86-64
cd /d "%~dp0"
-set "JAVA_HOME=C:\Program Files\Java\openjdk1.8.0_491"
-set "PATH=%JAVA_HOME%\bin;%PATH%"
-
-mvn -B package
+mvn -B package %*
diff --git a/examples/build-your-own-jar/build.sh b/examples/build-your-own-jar/build.sh
new file mode 100755
index 00000000..3e364a3d
--- /dev/null
+++ b/examples/build-your-own-jar/build.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
+#
+# SPDX-License-Identifier: MIT OR Apache-2.0
+
+# Builds target/srcmorph.jar from pom.xml (the CLI plus the natives of this OS/arch).
+# Extra arguments go to Maven, e.g. ./build.sh -Dgpu=vulkan-linux-x86-64
+set -euo pipefail
+cd "$(dirname "$0")"
+mvn -B package "$@"
diff --git a/examples/build-your-own-jar/pom.xml b/examples/build-your-own-jar/pom.xml
new file mode 100644
index 00000000..0817b62e
--- /dev/null
+++ b/examples/build-your-own-jar/pom.xml
@@ -0,0 +1,160 @@
+
+
+
+
+ 4.0.0
+ local
+ srcmorph-local
+ 1
+
+ jar
+
+
+ 1.3.0
+ 6.0.0
+ UTF-8
+
+
+
+
+ net.ladenthin
+ srcmorph-cli
+ ${srcmorph.version}
+
+
+
+ net.ladenthin
+ llama-platform
+
+
+
+
+
+ net.ladenthin
+ llama
+ ${llama.version}
+ ${natives}
+
+
+
+
+
+
+ ${project.basedir}
+
+ simplelogger.properties
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.6.3
+
+
+ require-natives
+
+ enforce
+
+
+
+
+ natives
+ No natives jar for this OS/arch. Pass one: -Dnatives=cpu-linux-x86-64 (see the java-llama.cpp README, "Choosing the natives jars").
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.8.0
+
+
+ jar-with-dependencies
+
+ srcmorph
+ false
+ false
+
+
+ net.ladenthin.srcmorph.cli.Main
+
+
+
+
+
+ package
+
+ single
+
+
+
+
+
+
+
+
+
+
+ linux-x86-64
+ Linuxamd64
+ cpu-linux-x86-64
+
+
+ linux-aarch64
+ Linuxaarch64
+ cpu-linux-aarch64
+
+
+ windows-x86-64
+ windowsamd64
+ cpu-windows-x86-64
+
+
+ windows-aarch64
+ windowsaarch64
+ cpu-windows-aarch64
+
+
+ macos-aarch64
+ macaarch64
+ metal-macos-aarch64
+
+
+
+ gpu
+ gpu
+
+
+ net.ladenthin
+ llama
+ ${llama.version}
+ ${gpu}
+
+
+
+
+
diff --git a/examples/build-your-own-jar/run.bat b/examples/build-your-own-jar/run.bat
new file mode 100644
index 00000000..9bb357c9
--- /dev/null
+++ b/examples/build-your-own-jar/run.bat
@@ -0,0 +1,8 @@
+REM SPDX-FileCopyrightText: 2026 Bernard Ladenthin
+REM
+REM SPDX-License-Identifier: MIT OR Apache-2.0
+
+@echo off
+rem Runs srcmorph with srcmorph.yaml from this directory (or the configuration named as argument).
+cd /d "%~dp0"
+if "%~1"=="" (java -jar target\srcmorph.jar srcmorph.yaml) else (java -jar target\srcmorph.jar "%~1")
diff --git a/examples/build-your-own-jar/run.sh b/examples/build-your-own-jar/run.sh
new file mode 100755
index 00000000..af1c2eb5
--- /dev/null
+++ b/examples/build-your-own-jar/run.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# SPDX-FileCopyrightText: 2026 Bernard Ladenthin
+#
+# SPDX-License-Identifier: MIT OR Apache-2.0
+
+# Runs srcmorph with srcmorph.yaml from this directory (or the configuration named as argument).
+set -euo pipefail
+cd "$(dirname "$0")"
+java -jar target/srcmorph.jar "${1:-srcmorph.yaml}"
diff --git a/srcmorph-cli/src/main/assembly-resources/simplelogger.properties b/examples/build-your-own-jar/simplelogger.properties
similarity index 55%
rename from srcmorph-cli/src/main/assembly-resources/simplelogger.properties
rename to examples/build-your-own-jar/simplelogger.properties
index f475e1ce..5430606a 100644
--- a/srcmorph-cli/src/main/assembly-resources/simplelogger.properties
+++ b/examples/build-your-own-jar/simplelogger.properties
@@ -2,13 +2,12 @@
#
# SPDX-License-Identifier: Apache-2.0
-# slf4j-simple defaults for the RUNNABLE FAT JAR only.
+# slf4j-simple defaults baked into target/srcmorph.jar (pom.xml packs this file into the jar).
#
-# This file is deliberately NOT under src/main/resources: that would put it into the
-# published srcmorph-cli jar, where anyone embedding the CLI would find our logging
-# configuration on their classpath. slf4j-simple reads whichever simplelogger.properties
-# the classloader hands it first, so a consumer with their own file would get a coin flip.
-# A library must not decide that; an application may, and the fat jar is the application.
+# The published srcmorph-cli jar deliberately carries no such file: anyone embedding the CLI
+# would find our logging configuration on their classpath, and slf4j-simple reads whichever
+# simplelogger.properties the classloader hands it first. A library must not decide that; the
+# jar you build here is the application, so it may.
#
# Every setting here is also overridable at launch with -Dorg.slf4j.simpleLogger..
@@ -16,8 +15,7 @@
org.slf4j.simpleLogger.defaultLogLevel=info
# stdout, not the slf4j-simple default of stderr: the CLI's own output belongs on the
-# same stream as everything else a user pipes or redirects. The fat-jar smoke test greps
-# for "Main#run end." on stdout, so this is load-bearing, not cosmetic.
+# same stream as everything else a user pipes or redirects.
org.slf4j.simpleLogger.logFile=System.out
# Wall-clock timestamps -- a run log without them cannot be correlated with anything.
diff --git a/examples/windows-fatjar/srcmorph.yaml b/examples/build-your-own-jar/srcmorph.yaml
similarity index 81%
rename from examples/windows-fatjar/srcmorph.yaml
rename to examples/build-your-own-jar/srcmorph.yaml
index fef557dc..adf4c6be 100644
--- a/examples/windows-fatjar/srcmorph.yaml
+++ b/examples/build-your-own-jar/srcmorph.yaml
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: MIT OR Apache-2.0
-# What run.bat does with it: every file under baseDirectory/subtrees with one of the fileExtensions
+# What run.sh / run.bat do with it: every file under baseDirectory/subtrees with one of the fileExtensions
# is matched to a rule in fieldGenerations. The rule names a prompt (promptDefinitions) and a model
# (aiDefinitions). srcmorph sends the prompt as the system message and the file's name and source as
# the user message, and writes the model's answer to outputDirectory as .ai.md.
@@ -15,16 +15,17 @@
command: Plan
srcMorph:
- # Use forward slashes in Windows paths here: in a double-quoted YAML string, "C:\x" is an escape.
- baseDirectory: "C:/path/to/project"
- outputDirectory: "C:/path/to/project/target/srcmorph-ai"
+ # On Windows use forward slashes ("C:/path/to/project"): in a double-quoted YAML string, "C:\x" is
+ # an escape.
+ baseDirectory: "/path/to/project"
+ outputDirectory: "/path/to/project/target/srcmorph-ai"
subtrees:
- "src/main/java"
fileExtensions:
- ".java"
generationProvider: llamacpp-jni # "mock" runs without a model, for a dry test of the setup
- modelDirectory: "C:/models" # a relative modelPath below is relative to this directory
+ modelDirectory: "/path/to/models" # a relative modelPath below is relative to this directory
promptDefinitions:
- key: java
@@ -37,7 +38,7 @@ srcMorph:
maxOutputTokens: 1024
temperature: 0.15
threads: 4 # the default; more only after measuring, never more than cores
- gpuLayers: 99 # 99 = everything on the GPU (Vulkan), 0 = CPU only
+ gpuLayers: 99 # 99 = everything on the GPU module in the jar, 0 = CPU only
fieldGenerations:
- promptKey: java
diff --git a/examples/run_all.bat b/examples/run_all.bat
index b56dd14a..4eabe9f9 100644
--- a/examples/run_all.bat
+++ b/examples/run_all.bat
@@ -7,16 +7,14 @@ rem Runs the srcmorph CLI against config_All.json: all three phases in order (Ge
rem AggregatePackages, AggregateProject). Uses the mock provider, so no GGUF model is required -
rem point generationProvider at "llamacpp-jni" and set a real modelPath to run a model.
rem
-rem The fat jar's file name is version-qualified (e.g.
-rem srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so
-rem the loop below picks whichever one was last built by "mvn package" in ..\srcmorph-cli.
+rem Runs the jar of build-your-own-jar\ (srcmorph-cli plus the natives of this machine); build it
+rem once with build-your-own-jar\build.bat.
setlocal
cd /d "%~dp0"
-set "JARFILE="
-for %%f in (..\srcmorph-cli\target\srcmorph-cli-*-jar-with-dependencies.jar) do set "JARFILE=%%f"
-if "%JARFILE%"=="" (
- echo srcmorph-cli fat jar not found under ..\srcmorph-cli\target\ - run "mvn package" first. 1>&2
+set "JARFILE=build-your-own-jar\target\srcmorph.jar"
+if not exist "%JARFILE%" (
+ echo %JARFILE% not found - run build-your-own-jar\build.bat first. 1>&2
exit /b 1
)
diff --git a/examples/run_all.sh b/examples/run_all.sh
index 67f6a36e..b3607ace 100755
--- a/examples/run_all.sh
+++ b/examples/run_all.sh
@@ -7,15 +7,14 @@
# AggregatePackages, AggregateProject). Uses the mock provider, so no GGUF model is required -
# point generationProvider at "llamacpp-jni" and set a real modelPath to run a model.
#
-# The fat jar's file name is version-qualified (e.g.
-# srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so the
-# glob below picks whichever one was last built by `mvn package` in ../srcmorph-cli.
+# Runs the jar of build-your-own-jar/ (srcmorph-cli plus the natives of this machine); build it
+# once with build-your-own-jar/build.sh.
set -euo pipefail
cd "$(dirname "$0")"
-JAR=$(ls ../srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar 2>/dev/null | head -n 1)
-if [ -z "$JAR" ]; then
- echo "srcmorph-cli fat jar not found under ../srcmorph-cli/target/ - run 'mvn package' first." >&2
+JAR=build-your-own-jar/target/srcmorph.jar
+if [ ! -f "$JAR" ]; then
+ echo "$JAR not found - run build-your-own-jar/build.sh first." >&2
exit 1
fi
diff --git a/examples/run_calibrate.bat b/examples/run_calibrate.bat
index 944172ad..6872ab24 100644
--- a/examples/run_calibrate.bat
+++ b/examples/run_calibrate.bat
@@ -8,16 +8,14 @@ rem and prints a paste-ready block per model. With the mock provid
rem this is a no-op smoke check; swap generationProvider to "llamacpp-jni" and set a real
rem modelPath to calibrate an actual GGUF model on this machine.
rem
-rem The fat jar's file name is version-qualified (e.g.
-rem srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so
-rem the loop below picks whichever one was last built by "mvn package" in ..\srcmorph-cli.
+rem Runs the jar of build-your-own-jar\ (srcmorph-cli plus the natives of this machine); build it
+rem once with build-your-own-jar\build.bat.
setlocal
cd /d "%~dp0"
-set "JARFILE="
-for %%f in (..\srcmorph-cli\target\srcmorph-cli-*-jar-with-dependencies.jar) do set "JARFILE=%%f"
-if "%JARFILE%"=="" (
- echo srcmorph-cli fat jar not found under ..\srcmorph-cli\target\ - run "mvn package" first. 1>&2
+set "JARFILE=build-your-own-jar\target\srcmorph.jar"
+if not exist "%JARFILE%" (
+ echo %JARFILE% not found - run build-your-own-jar\build.bat first. 1>&2
exit /b 1
)
diff --git a/examples/run_calibrate.sh b/examples/run_calibrate.sh
index 789fadd9..bb606658 100755
--- a/examples/run_calibrate.sh
+++ b/examples/run_calibrate.sh
@@ -8,15 +8,14 @@
# a no-op smoke check; swap generationProvider to "llamacpp-jni" and set a real modelPath to
# calibrate an actual GGUF model on this machine.
#
-# The fat jar's file name is version-qualified (e.g.
-# srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so the
-# glob below picks whichever one was last built by `mvn package` in ../srcmorph-cli.
+# Runs the jar of build-your-own-jar/ (srcmorph-cli plus the natives of this machine); build it
+# once with build-your-own-jar/build.sh.
set -euo pipefail
cd "$(dirname "$0")"
-JAR=$(ls ../srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar 2>/dev/null | head -n 1)
-if [ -z "$JAR" ]; then
- echo "srcmorph-cli fat jar not found under ../srcmorph-cli/target/ - run 'mvn package' first." >&2
+JAR=build-your-own-jar/target/srcmorph.jar
+if [ ! -f "$JAR" ]; then
+ echo "$JAR not found - run build-your-own-jar/build.sh first." >&2
exit 1
fi
diff --git a/examples/run_generate.bat b/examples/run_generate.bat
index d76153f7..4aed6970 100644
--- a/examples/run_generate.bat
+++ b/examples/run_generate.bat
@@ -7,16 +7,14 @@ rem Runs the srcmorph CLI against config_GenerateFileIndex.json (Phase 1 only: i
rem and fill in their AI-generated summary bodies). Uses the mock provider, so no GGUF model is
rem required - point generationProvider at "llamacpp-jni" and set a real modelPath to run a model.
rem
-rem The fat jar's file name is version-qualified (e.g.
-rem srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so
-rem the loop below picks whichever one was last built by "mvn package" in ..\srcmorph-cli.
+rem Runs the jar of build-your-own-jar\ (srcmorph-cli plus the natives of this machine); build it
+rem once with build-your-own-jar\build.bat.
setlocal
cd /d "%~dp0"
-set "JARFILE="
-for %%f in (..\srcmorph-cli\target\srcmorph-cli-*-jar-with-dependencies.jar) do set "JARFILE=%%f"
-if "%JARFILE%"=="" (
- echo srcmorph-cli fat jar not found under ..\srcmorph-cli\target\ - run "mvn package" first. 1>&2
+set "JARFILE=build-your-own-jar\target\srcmorph.jar"
+if not exist "%JARFILE%" (
+ echo %JARFILE% not found - run build-your-own-jar\build.bat first. 1>&2
exit /b 1
)
diff --git a/examples/run_generate.sh b/examples/run_generate.sh
index 97410bd6..6884f10b 100755
--- a/examples/run_generate.sh
+++ b/examples/run_generate.sh
@@ -7,15 +7,14 @@
# and fill in their AI-generated summary bodies). Uses the mock provider, so no GGUF model is
# required - point generationProvider at "llamacpp-jni" and set a real modelPath to run a model.
#
-# The fat jar's file name is version-qualified (e.g.
-# srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so the
-# glob below picks whichever one was last built by `mvn package` in ../srcmorph-cli.
+# Runs the jar of build-your-own-jar/ (srcmorph-cli plus the natives of this machine); build it
+# once with build-your-own-jar/build.sh.
set -euo pipefail
cd "$(dirname "$0")"
-JAR=$(ls ../srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar 2>/dev/null | head -n 1)
-if [ -z "$JAR" ]; then
- echo "srcmorph-cli fat jar not found under ../srcmorph-cli/target/ - run 'mvn package' first." >&2
+JAR=build-your-own-jar/target/srcmorph.jar
+if [ ! -f "$JAR" ]; then
+ echo "$JAR not found - run build-your-own-jar/build.sh first." >&2
exit 1
fi
diff --git a/examples/run_plan.bat b/examples/run_plan.bat
index 15883aea..338b04c1 100644
--- a/examples/run_plan.bat
+++ b/examples/run_plan.bat
@@ -7,16 +7,14 @@ rem Runs the srcmorph CLI against config_Plan.json: builds and logs the routing
rem GenerateFileIndex phase with planOnly forced true by the Plan command) - no model is loaded
rem and nothing is written. Safe to run with no GGUF model on disk (generationProvider is "mock").
rem
-rem The fat jar's file name is version-qualified (e.g.
-rem srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so
-rem the loop below picks whichever one was last built by "mvn package" in ..\srcmorph-cli.
+rem Runs the jar of build-your-own-jar\ (srcmorph-cli plus the natives of this machine); build it
+rem once with build-your-own-jar\build.bat.
setlocal
cd /d "%~dp0"
-set "JARFILE="
-for %%f in (..\srcmorph-cli\target\srcmorph-cli-*-jar-with-dependencies.jar) do set "JARFILE=%%f"
-if "%JARFILE%"=="" (
- echo srcmorph-cli fat jar not found under ..\srcmorph-cli\target\ - run "mvn package" first. 1>&2
+set "JARFILE=build-your-own-jar\target\srcmorph.jar"
+if not exist "%JARFILE%" (
+ echo %JARFILE% not found - run build-your-own-jar\build.bat first. 1>&2
exit /b 1
)
diff --git a/examples/run_plan.sh b/examples/run_plan.sh
index f51e2734..31e13107 100755
--- a/examples/run_plan.sh
+++ b/examples/run_plan.sh
@@ -7,15 +7,14 @@
# GenerateFileIndex phase with planOnly forced true by the Plan command) - no model is loaded and
# nothing is written. Safe to run with no GGUF model on disk (generationProvider is "mock").
#
-# The fat jar's file name is version-qualified (e.g.
-# srcmorph-cli-1.2.0-SNAPSHOT-jar-with-dependencies.jar) and changes on every version bump, so the
-# glob below picks whichever one was last built by `mvn package` in ../srcmorph-cli.
+# Runs the jar of build-your-own-jar/ (srcmorph-cli plus the natives of this machine); build it
+# once with build-your-own-jar/build.sh.
set -euo pipefail
cd "$(dirname "$0")"
-JAR=$(ls ../srcmorph-cli/target/srcmorph-cli-*-jar-with-dependencies.jar 2>/dev/null | head -n 1)
-if [ -z "$JAR" ]; then
- echo "srcmorph-cli fat jar not found under ../srcmorph-cli/target/ - run 'mvn package' first." >&2
+JAR=build-your-own-jar/target/srcmorph.jar
+if [ ! -f "$JAR" ]; then
+ echo "$JAR not found - run build-your-own-jar/build.sh first." >&2
exit 1
fi
diff --git a/examples/simplelogger.properties b/examples/simplelogger.properties
index 0f47105d..3dcf28b7 100644
--- a/examples/simplelogger.properties
+++ b/examples/simplelogger.properties
@@ -2,21 +2,20 @@
#
# SPDX-License-Identifier: Apache-2.0
-# Example slf4j-simple configuration for srcmorph-cli, replacing the former
-# logbackConfiguration.xml (the fat jar ships slf4j-simple now, because logback 1.4.0+ is
-# Java 11 bytecode while this project targets Java 8 -- see the pin in the reactor pom).
+# Example slf4j-simple configuration for srcmorph-cli (slf4j-simple, not logback, because
+# logback 1.4.0+ is Java 11 bytecode while this project targets Java 8 -- see the pin in the
+# reactor pom).
#
-# Two ways to use it:
+# Two ways to use it with the jar of build-your-own-jar/:
# * put a copy named simplelogger.properties FIRST on the classpath:
-# java -cp examples:srcmorph-cli--jar-with-dependencies.jar \
+# java -cp examples:examples/build-your-own-jar/target/srcmorph.jar \
# net.ladenthin.srcmorph.cli.Main examples/config_Plan.json
# * or set the individual keys as system properties, which always win:
# java -Dorg.slf4j.simpleLogger.defaultLogLevel=debug \
-# -jar srcmorph-cli--jar-with-dependencies.jar examples/config_Plan.json
+# -jar examples/build-your-own-jar/target/srcmorph.jar examples/config_Plan.json
#
-# The fat jar carries these same defaults internally
-# (srcmorph-cli/src/main/assembly-resources/simplelogger.properties), so this file is only
-# needed to change them.
+# That jar carries defaults of its own (build-your-own-jar/simplelogger.properties), so this
+# file is only needed to change them.
# debug shows the per-file indexer decisions; the shipped default is info.
org.slf4j.simpleLogger.defaultLogLevel=info
diff --git a/examples/windows-fatjar/pom.xml b/examples/windows-fatjar/pom.xml
deleted file mode 100644
index e7e09674..00000000
--- a/examples/windows-fatjar/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- 4.0.0
- local
- srcmorph-local
- 1
- pom
-
-
- 1.3.0
- 6.0.0
- UTF-8
-
-
-
-
- net.ladenthin
- srcmorph-cli
- ${srcmorph.version}
-
-
-
- net.ladenthin
- llama-platform
-
-
-
-
-
- net.ladenthin
- llama
- ${llama.version}
- cpu-windows-x86-64
-
-
-
- net.ladenthin
- llama
- ${llama.version}
- vulkan-windows-x86-64
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
- 3.7.1
-
-
- jar-with-dependencies
-
- srcmorph
- false
-
-
- net.ladenthin.srcmorph.cli.Main
-
-
-
-
-
- package
-
- single
-
-
-
-
-
-
-
diff --git a/examples/windows-fatjar/run.bat b/examples/windows-fatjar/run.bat
deleted file mode 100644
index c926df0d..00000000
--- a/examples/windows-fatjar/run.bat
+++ /dev/null
@@ -1,11 +0,0 @@
-REM SPDX-FileCopyrightText: 2026 Bernard Ladenthin
-REM
-REM SPDX-License-Identifier: MIT OR Apache-2.0
-
-@echo off
-rem Runs srcmorph with srcmorph.yaml from this directory. Adapt JAVA_HOME.
-cd /d "%~dp0"
-set "JAVA_HOME=C:\Program Files\Java\openjdk1.8.0_491"
-set "PATH=%JAVA_HOME%\bin;%PATH%"
-
-java -jar target\srcmorph.jar srcmorph.yaml
diff --git a/pom.xml b/pom.xml
index 9087e74a..b68724b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,10 +62,6 @@ SPDX-License-Identifier: Apache-2.0
-
- ${maven.multiModuleProjectDirectory}/../actions
@@ -193,7 +189,7 @@ SPDX-License-Identifier: Apache-2.0
COMPILE-ONLY, and that is the whole point. checker-qual is class-file major 55 from
4.0.0 on, its annotations are @Retention(RUNTIME), and anything that reflects over an
annotated element (Jackson does, and srcmorph-cli binds its whole configuration with
- Jackson) loads them — so a copy inside the fat jar is an UnsupportedClassVersionError
+ Jackson) loads them — so a copy on a consumer's runtime classpath is an UnsupportedClassVersionError
waiting for a Java 8 consumer.
Pinning the SHIPPED copy to the last Java 8 line (3.55.1) does not work and was
@@ -205,7 +201,7 @@ SPDX-License-Identifier: Apache-2.0
Scope `provided` satisfies both constraints at once: 4.3.0 on the compile classpath
where the checker needs it, and excluded from consumers' transitive graph AND from
- the fat jar (jar-with-dependencies takes scope runtime), so no checker-qual class of
+ any jar-with-dependencies built from it (that descriptor takes scope runtime), so no checker-qual class of
any version reaches a consumer's JVM. true alone would not have
been enough — that descriptor filters on scope only. Safe because no source in this
reactor imports org.checkerframework.
@@ -264,19 +260,6 @@ SPDX-License-Identifier: Apache-2.0
-
-
- actions-dir-from-environment
-
-
- env.ACTIONS_DIR
-
-
-
- ${env.ACTIONS_DIR}
-
-
org.checkerframework
@@ -122,8 +121,9 @@ SPDX-License-Identifier: Apache-2.0
slf4j-simple, not logback: logback 1.4.0+ is Java 11 bytecode and the production code
here targets Java 8 — see the rationale on the pin in the parent's dependencyManagement.
- Defaults ship in src/main/assembly-resources/simplelogger.properties (fat jar only);
- override at runtime with -Dorg.slf4j.simpleLogger.* or a classpath simplelogger.properties.
+ This jar ships no simplelogger.properties (it would override an embedder's own); the
+ runnable jar of examples/build-your-own-jar carries the defaults. Override at runtime with
+ -Dorg.slf4j.simpleLogger.* or a classpath simplelogger.properties.
-->
org.slf4j
@@ -198,11 +198,6 @@ SPDX-License-Identifier: Apache-2.0
git-commit-id-maven-plugin
10.0.1
-
- org.apache.maven.plugins
- maven-assembly-plugin
- 3.8.0
-
org.apache.maven.plugins
maven-compiler-plugin
@@ -289,64 +284,6 @@ SPDX-License-Identifier: Apache-2.0
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- runtime-license-classpath
- process-classes
- build-classpath
-
- runtime
- jar
- true
- ${project.build.directory}/runtime-license-classpath.txt
-
-
-
-
-
- org.codehaus.mojo
- exec-maven-plugin
- 3.6.4
-
-
- collect-runtime-licenses
- prepare-package
- exec
-
- ${license.python}
-
- ${ci.actions.dir}/lib
-
-
-
- -c
- import importlib.util, sys; importlib.util.find_spec('actionlib') or sys.exit('The license collector comes from bernardladenthin/actions: set ACTIONS_DIR to a checkout of it, or check it out next to this repository (../actions). Looked in ${ci.actions.dir}/lib'); from actionlib.java.thirdpartylicenses import main; sys.exit(main(sys.argv[1:]))
- --classpath
- ${project.build.directory}/runtime-license-classpath.txt
- --output
- ${project.build.directory}/third-party-dependencies
-
- --supplemental
- ${project.basedir}/../third-party-notices
- --require-complete
-
-
-
-
-
io.github.git-commit-id
git-commit-id-maven-plugin
@@ -572,56 +509,6 @@ SPDX-License-Identifier: Apache-2.0
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
- src/assembly/fat-jar.xml
-
-
-
- net.ladenthin.srcmorph.cli.Main
-
-
- false
-
-
-
- build-fat-jar
- package
-
- single
-
-
-
-
-
org.jacoco
jacoco-maven-plugin
@@ -721,10 +608,10 @@ SPDX-License-Identifier: Apache-2.0
diff --git a/srcmorph-cli/src/assembly/fat-jar.xml b/srcmorph-cli/src/assembly/fat-jar.xml
deleted file mode 100644
index 36e232da..00000000
--- a/srcmorph-cli/src/assembly/fat-jar.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
- jar-with-dependencies
-
- jar
-
- false
-
-
- /
- true
- true
- runtime
-
-
-
-
- ${project.basedir}/src/main/assembly-resources
- /
-
- simplelogger.properties
-
-
-
-
- ${project.build.directory}/third-party-dependencies
- META-INF/third-party/dependencies
-
-
-
diff --git a/srcmorph-cli/src/main/java/module-info.java b/srcmorph-cli/src/main/java/module-info.java
index 03f4222b..eab3b25c 100644
--- a/srcmorph-cli/src/main/java/module-info.java
+++ b/srcmorph-cli/src/main/java/module-info.java
@@ -29,9 +29,9 @@
* {@code requires static org.jspecify} is needed only at compile time of this
* descriptor; JSpecify annotations carry {@code RetentionPolicy.CLASS} so module-path
* consumers never need jspecify on their runtime path. Checker Framework qualifiers are
- * likewise compile-time only. The fat jar produced by {@code maven-assembly-plugin} is a
- * classpath-only artifact and flattens multiple {@code module-info.class} entries harmlessly,
- * same as any other jar-with-dependencies build.
+ * likewise compile-time only. The jar a user builds with {@code examples/build-your-own-jar}
+ * ({@code maven-assembly-plugin}) is a classpath-only artifact and flattens multiple
+ * {@code module-info.class} entries harmlessly, same as any other jar-with-dependencies build.
*
* This descriptor is NOT purely decorative — unlike the core/plugin modules'
* own {@code module-info.java} (which only need to satisfy the isolated, release-9
diff --git a/srcmorph-cli/src/test/java/net/ladenthin/srcmorph/cli/ExamplesConfigBindingTest.java b/srcmorph-cli/src/test/java/net/ladenthin/srcmorph/cli/ExamplesConfigBindingTest.java
index 352bfeb5..adabd170 100644
--- a/srcmorph-cli/src/test/java/net/ladenthin/srcmorph/cli/ExamplesConfigBindingTest.java
+++ b/srcmorph-cli/src/test/java/net/ladenthin/srcmorph/cli/ExamplesConfigBindingTest.java
@@ -83,7 +83,7 @@ private static List listExampleConfigFiles() throws IOException {
}
/**
- * {@code examples/windows-fatjar/} is the build-it-yourself example (docs/windows-intel-core-ultra.md):
+ * {@code examples/build-your-own-jar/} is how a user builds the runnable jar (docs/windows-intel-core-ultra.md):
* its {@code srcmorph.yaml} is meant for a real model, so it is not one of the mock fixtures above,
* but it must still bind under the strict mapper, start with the safe {@code Plan}, and route every
* file through a fallback rule that handles an oversized file. Its {@code pom.xml} names the CLI's
@@ -92,8 +92,8 @@ private static List listExampleConfigFiles() throws IOException {
* @throws IOException if an example file cannot be read
*/
@Test
- public void windowsFatJarExample_bindsAndNamesTheMainClass() throws IOException {
- final Path directory = EXAMPLES_DIRECTORY.resolve("windows-fatjar");
+ public void buildYourOwnJarExample_bindsAndNamesTheMainClass() throws IOException {
+ final Path directory = EXAMPLES_DIRECTORY.resolve("build-your-own-jar");
final CConfiguration configuration = Main.fromYaml(Main.readString(directory.resolve("srcmorph.yaml")));
assertThat(configuration.command, is(CCommand.Plan));
diff --git a/srcmorph-maven-plugin/README.md b/srcmorph-maven-plugin/README.md
index 652523f4..0d482df0 100644
--- a/srcmorph-maven-plugin/README.md
+++ b/srcmorph-maven-plugin/README.md
@@ -606,7 +606,7 @@ In both cases:
- **CUDA** needs a matching CUDA 13 toolkit + driver, and the toolkit's `bin\x64` (with `cudart64_13.dll`,
`cublas64_13.dll`, `cublasLt64_13.dll`) on `PATH` — the natives jar bundles only the backend module
`ggml-cuda.dll`, not the CUDA runtime.
-- **Vulkan** needs only the GPU driver (it brings `vulkan-1.dll`). A CLI fat jar built with exactly
+- **Vulkan** needs only the GPU driver (it brings `vulkan-1.dll`). A CLI jar built with exactly
the CPU and Vulkan natives of a Windows machine is in [docs/windows-intel-core-ultra.md](../docs/windows-intel-core-ultra.md).
- **GPU layer offload** — set `` inside your `` (see "Per-model
`` parameters" above): `-1` (default) does **not** pin a layer count, so llama.cpp
diff --git a/srcmorph-maven-plugin/pom.xml b/srcmorph-maven-plugin/pom.xml
index 970cbf1f..9eb2e657 100644
--- a/srcmorph-maven-plugin/pom.xml
+++ b/srcmorph-maven-plugin/pom.xml
@@ -147,7 +147,7 @@ SPDX-License-Identifier: Apache-2.0
org.checkerframework
diff --git a/srcmorph/pom.xml b/srcmorph/pom.xml
index 9e9c3b3c..57c821b2 100644
--- a/srcmorph/pom.xml
+++ b/srcmorph/pom.xml
@@ -111,7 +111,7 @@ SPDX-License-Identifier: Apache-2.0
org.checkerframework
@@ -816,8 +816,7 @@ SPDX-License-Identifier: Apache-2.0
run pays a one-time shader-compilation cost.
Example: mvn srcmorph:generate -P gpu-cuda -Dai.gpuLayers=20 (run from the plugin module,
which pulls this module's GPU natives jar transitively).
- Any other natives jar: -Dllama.classifier= (gpu-natives), which is also how CI
- builds one srcmorph-cli fat jar per GPU backend.
+ Any other natives jar: -Dllama.classifier= (gpu-natives).
(OpenCL is intentionally not offered: llama.cpp's OpenCL backend does not support NVIDIA.) -->
gpu-cuda
diff --git a/srcmorph/src/main/java/net/ladenthin/srcmorph/engine/EngineSupport.java b/srcmorph/src/main/java/net/ladenthin/srcmorph/engine/EngineSupport.java
index 8af1be95..76c03e0b 100644
--- a/srcmorph/src/main/java/net/ladenthin/srcmorph/engine/EngineSupport.java
+++ b/srcmorph/src/main/java/net/ladenthin/srcmorph/engine/EngineSupport.java
@@ -168,8 +168,8 @@ static LlamaCppJniConfig resolveLlamaCppJniConfig(
* Gated on the provider on purpose. Only {@code llamacpp-jni} loads a GGUF;
* the {@code mock} provider ignores {@code modelPath} entirely, and every shipped example config
* sets a deliberately non-existent {@code "unused-with-mock-provider.gguf"}. An ungated check
- * would red those examples, their binding tests, the CLI end-to-end test and the fat-jar release
- * smoke.
+ * would red those examples, their binding tests, the CLI end-to-end test and the jar smoke
+ * ({@code smoke-jar}) that gates every release.
*
* @param generationProvider the configured provider key
* @param modelDefinitionSupport model lookup built from the configured {@code }
diff --git a/third-party-notices/jspecify-1.0.1/LICENSE b/third-party-notices/jspecify-1.0.1/LICENSE
deleted file mode 100644
index d6456956..00000000
--- a/third-party-notices/jspecify-1.0.1/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/third-party-notices/notices.json b/third-party-notices/notices.json
deleted file mode 100644
index 86704dde..00000000
--- a/third-party-notices/notices.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "format": 1,
- "notices": [
- {
- "directory": "jspecify-1.0.1",
- "license": "Apache-2.0",
- "artifacts": [
- "jspecify"
- ]
- },
- {
- "directory": "snakeyaml-2.5",
- "license": "Apache-2.0",
- "artifacts": [
- "snakeyaml"
- ]
- }
- ]
-}
diff --git a/third-party-notices/snakeyaml-2.5/LICENSE.txt b/third-party-notices/snakeyaml-2.5/LICENSE.txt
deleted file mode 100644
index d9a10c0d..00000000
--- a/third-party-notices/snakeyaml-2.5/LICENSE.txt
+++ /dev/null
@@ -1,176 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS