diff --git a/bin/build_and_publish_remote.sh b/bin/build_and_publish_remote.sh index 754571f..c76fa36 100755 --- a/bin/build_and_publish_remote.sh +++ b/bin/build_and_publish_remote.sh @@ -130,6 +130,8 @@ _rsync --delete \ --include 'systemd/***' \ --include 'nginx/***' \ --include 'tests/***' \ + --include 'requirements.txt' \ + --include 'requirements-dev.txt' \ --include 'pyproject.toml' \ --include 'playwright.config.js' \ --include 'package.json' \ diff --git a/bin/build_llama_runtime.sh b/bin/build_llama_runtime.sh index 8cf4a05..475796a 100755 --- a/bin/build_llama_runtime.sh +++ b/bin/build_llama_runtime.sh @@ -277,14 +277,20 @@ for so in "${build_dir}/lib/"*.so* "${build_dir}/bin/"*.so*; do done shopt -u nullglob -# Universal profile: cmake MODULE libraries (ggml-cpu-armv8*.so) may land in -# subdirectories of the build tree instead of bin/ or lib/. Copy any we missed. -if [ "${BUILD_PROFILE}" = "universal" ]; then - while IFS= read -r so; do - base="$(basename "${so}")" - [ ! -e "${slot_dir}/lib/${base}" ] && cp -P "${so}" "${slot_dir}/lib/" - done < <(find "${build_dir}" -name 'libggml*.so*' -type f 2>/dev/null) -fi +# Upstream layout shifts over time. Required shared libs can land outside the +# top-level build lib/bin dirs (for example examples/mtmd/libmtmd.so). Include +# both real files and SONAME symlinks, and dereference symlinks so every loader +# name (for example libmtmd.so.0) exists in the packaged lib/ directory. +while IFS= read -r so; do + base="$(basename "${so}")" + [ ! -e "${slot_dir}/lib/${base}" ] && cp -L "${so}" "${slot_dir}/lib/${base}" +done < <( + find "${build_dir}" \( -type f -o -type l \) \( \ + -name 'libmtmd*.so*' -o \ + -name 'libllama*.so*' -o \ + -name 'libggml*.so*' \ + \) 2>/dev/null +) copy_runtime_deps "${slot_dir}/lib" "${slot_dir}/bin/llama-server" "${slot_dir}/bin/llama-bench" "${slot_dir}/lib/"*.so* diff --git a/bin/install_dev.sh b/bin/install_dev.sh index 2509618..fda9efa 100755 --- a/bin/install_dev.sh +++ b/bin/install_dev.sh @@ -116,7 +116,8 @@ run_sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y \ wget \ curl \ jq \ - rsync + rsync \ + libvulkan1 if [ "${POTATO_ENFORCE_HOSTNAME}" = "1" ]; then current_hostname="$(hostnamectl --static 2>/dev/null || hostname)" diff --git a/tests/unit/test_shell_scripts.py b/tests/unit/test_shell_scripts.py index 43894eb..3b9f649 100644 --- a/tests/unit/test_shell_scripts.py +++ b/tests/unit/test_shell_scripts.py @@ -81,6 +81,32 @@ def test_ensure_model_script_keeps_shell_functions_outside_python_heredoc(): assert "free_space_bytes()" not in python_block +def test_build_llama_runtime_collects_nested_shared_libraries(): + """Upstream may place required .so files outside top-level build lib/bin dirs.""" + script = (REPO_ROOT / "bin" / "build_llama_runtime.sh").read_text(encoding="utf-8") + + assert "find \"${build_dir}\"" in script + assert "\\( -type f -o -type l \\)" in script + assert "cp -L \"${so}\" \"${slot_dir}/lib/${base}\"" in script + assert "libmtmd*.so*" in script + assert "libllama*.so*" in script + assert "libggml*.so*" in script + + +def test_remote_runtime_build_syncs_install_requirements(): + """The remote build checkout must be complete enough to run install_dev.sh.""" + script = (REPO_ROOT / "bin" / "build_and_publish_remote.sh").read_text(encoding="utf-8") + + assert "--include 'requirements.txt'" in script + + +def test_install_dev_installs_litert_native_runtime_dependencies(): + """LiteRT-LM 0.12 needs libvulkan.so.1 available even for CPU startup.""" + script = (REPO_ROOT / "bin" / "install_dev.sh").read_text(encoding="utf-8") + + assert "libvulkan1" in script + + def test_uninstall_script_executes_pi_cleanup_without_package_removal(tmp_path: Path): fakebin = tmp_path / "fakebin" fakebin.mkdir() @@ -393,4 +419,3 @@ def test_prepare_imager_aborts_on_missing_app(): script = (REPO_ROOT / "bin" / "prepare_imager_bundle.sh").read_text(encoding="utf-8") assert "ERROR: selected app missing from payload" in script assert "exit 1" in script -