diff --git a/cmake/hrx.cmake b/cmake/hrx.cmake index 3e559d68..fbe882d5 100644 --- a/cmake/hrx.cmake +++ b/cmake/hrx.cmake @@ -143,7 +143,7 @@ ExternalProject_Add(llama_hrx "-DCMAKE_C_FLAGS=${ONEBIT_HRX_PATH_MAP_FLAGS}" "-DCMAKE_CXX_FLAGS=${ONEBIT_HRX_PATH_MAP_FLAGS}" ${_hrx_gpu_private_args} - BUILD_COMMAND ${CMAKE_COMMAND} --build --target llama-server llama-bench + BUILD_COMMAND ${CMAKE_COMMAND} --build --target llama-server llama-bench llama-perplexity INSTALL_COMMAND "" BUILD_ALWAYS ON USES_TERMINAL_BUILD ON) diff --git a/scripts/pack-elf.sh b/scripts/pack-elf.sh index f067212e..7e3b3c6e 100644 --- a/scripts/pack-elf.sh +++ b/scripts/pack-elf.sh @@ -86,6 +86,98 @@ pack_localize() { done } +# A program or library ships only if every library it NEEDs resolves the way it will on a user's machine: +# plain ldd, with **no** LD_LIBRARY_PATH, so only the ELF's own RUNPATH and the system are consulted. A +# library path inherited from the build shell resolves against the build tree and hides exactly the defect +# this check exists for. Prints ": " for each NEEDED that cannot be found. +pack_unresolved_needed() { + local stage=$1 f + while IFS= read -r -d '' f; do + pack_is_dynelf "$f" || continue + env -u LD_LIBRARY_PATH ROCR_VISIBLE_DEVICES=-1 ldd "$f" 2>/dev/null | + awk -v f="$f" '$2 == "=>" && $3 == "not" {print f": "$1}' + done < <(find "$stage" -type f -print0) +} + +# Everything in the package has to come from the build being packaged. A binary copied in from another tree +# (a hardware-gate cache, a drop) carries that tree's RUNPATH and its build's missing parts: in the 2026-10-23 +# rc, `llama-perplexity` came from a hardware-gate build and pointed at that build's directory. Compares each +# staged file with the same relative path under the build and reports what has no counterpart there. +pack_require_provenance() { # + local stage=$1 build=$2 f rel bad=0 + while IFS= read -r -d '' f; do + pack_is_elf "$f" || continue + rel=${f#"$stage"/} + if [ ! -f "$build/$rel" ]; then + echo " $rel is not in the build being packaged" + bad=1 + elif ! cmp -s "$f" "$build/$rel"; then + echo " $rel differs from the build being packaged" + bad=1 + fi + done < <(find "$stage" -type f -print0) + return $bad +} + +# The programs `put` was asked for are the ones the release promises: if one of them cannot resolve, that is a +# build problem (the tool came from another tree, or its implementation library was not built) and the package +# has to fail rather than quietly lose the tool. Call once per directory group: pack_require_tools +# . +pack_require_tools() { + local stage=$1 rel=$2 f bad=0 + shift 2 + for f in "$@"; do + if [ ! -f "$stage/$rel/$f" ]; then + echo " missing from the package: $rel/$f" + bad=1 + continue + fi + while IFS= read -r line; do + echo " $rel/$f cannot load: ${line##*: }" + bad=1 + done < <(pack_unresolved_needed "$stage" | grep -F "$stage/$rel/$f:") + done + return $bad +} + +# Everything in the package has to come from the build being packaged. A binary copied in from another tree +# (a hardware-gate cache, a drop) carries that tree's RUNPATH and its build's missing parts: in the 2026-10-23 +# rc, `llama-perplexity` came from a hardware-gate build and pointed at that build's directory. Compares each +# staged file with the same relative path under the build and reports what has no counterpart there. +pack_require_provenance() { # + local stage=$1 build=$2 f rel bad=0 + while IFS= read -r -d '' f; do + pack_is_elf "$f" || continue + rel=${f#"$stage"/} + if [ ! -f "$build/$rel" ]; then + echo " $rel is not in the build being packaged" + bad=1 + elif ! cmp -s "$f" "$build/$rel"; then + echo " $rel differs from the build being packaged" + bad=1 + fi + done < <(find "$stage" -type f -print0) + return $bad +} + +# The programs `put` was asked for are the ones the release promises: if one of them cannot resolve, that is a +# build problem (the tool came from another tree, or its implementation library was not built) and the package +# has to fail rather than quietly lose the tool. Prints the offending programs. +# A thin binary whose implementation library was not built has to leave the package rather than ship broken +# (the 2026-10-23 rc shipped llama-perplexity without libllama-perplexity-impl.so). Removing the binary is +# the safe direction: the tool is then absent instead of failing at first use. +pack_prune_unloadable() { + local stage=$1 bad dropped=0 + while IFS= read -r line; do + bad=${line%%:*} + [ -f "$bad" ] || continue + echo " drop $(echo "$bad" | sed "s|^$stage/||"): needs a library the package does not carry" + rm -f "$bad" + dropped=$((dropped+1)) + done < <(pack_unresolved_needed "$stage") + [ "$dropped" = 0 ] || echo " $dropped program(s) dropped as unloadable" +} + pack_strip() { local f while IFS= read -r -d '' f; do @@ -116,6 +208,9 @@ $f: embedded build-machine path" grep -E "^/" | grep -vE "^(${ONEBIT_TOOLCHAIN_ROOT:-/opt/rocm-therock})/" | grep -q . && out="$out\n$f: absolute RUNPATH outside the toolchain root" done < <(find "$1" -type f -print0) + # every shipped program and library must resolve what it links against + while IFS= read -r line; do out="$out\n$line: a NEEDED library is not in the package or on the machine"; done \ + < <(pack_unresolved_needed "$1") [ -z "$out" ] || printf "%b\n" "$out" } diff --git a/scripts/package-linux.sh b/scripts/package-linux.sh index d66923cb..4c2d5963 100755 --- a/scripts/package-linux.sh +++ b/scripts/package-linux.sh @@ -97,6 +97,20 @@ pack_localize "$stage" "$build" # engine binary at the top of the tree is the one it does not visit (depth 1), so drop its rpath here. patchelf --remove-rpath "$stage/1bit" +# every program the release promises has to load from the package; one that does not is a build problem, not +# something to drop silently (the 2026-10-23 rc shipped llama-perplexity without its implementation library) +tools_bad="" +tools_bad+=$(pack_require_tools "$stage" hrx/llama/bin llama-server llama-bench llama-perplexity || true) +tools_bad+=$(pack_require_tools "$stage" ds4/hip ds4-server ds4 ds4-bench || true) +[ -z "$tools_bad" ] || { echo "shipped tools that cannot load:"; echo "$tools_bad"; exit 1; } + +# everything shipped has to come from the build being packaged +provenance_bad=$(pack_require_provenance "$stage" "$build" || true) +[ -z "$provenance_bad" ] || { echo "files that did not come from this build:"; echo "$provenance_bad"; exit 1; } + +# anything else that cannot load leaves the package rather than shipping broken +pack_prune_unloadable "$stage" + # symbol tables and debug info out of every program and library: the release ships compiled code # only (docs/releases.md, "Private components"); the dynamic symbols they link by stay pack_strip "$stage"