Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/compute-changes/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ runs:
WINDOWS_GPU_BUILD_REQUIRED="false"
if [[ -n "$CHANGED_FILES" ]]; then
WINDOWS_CPU_INPUTS=$(echo "$CHANGED_FILES" | grep -E '(^crates/mesh-llm-nodejs/|^crates/skippy-ffi/|^scripts/build-windows\.ps1$|^third_party/llama\.cpp/|^Cargo\.toml$|^Cargo\.lock$|^\.github/cache-version\.txt$)' || true)
WINDOWS_GPU_INPUTS=$(echo "$CHANGED_FILES" | grep -E '(^crates/skippy-ffi/|^scripts/build-windows\.ps1$|^scripts/install-windows-sdk\.ps1$|^third_party/llama\.cpp/|^\.github/cache-version\.txt$|^\.github/actions/setup-windows-rocm-sdk/)' || true)
WINDOWS_GPU_INPUTS=$(echo "$CHANGED_FILES" | grep -E '(^crates/skippy-ffi/|^scripts/(build-windows|install-windows-sdk)\.ps1$|^scripts/(package-native-runtime|verify-native-runtime-package)\.sh$|^scripts/windows-native-runtime-deps\.py$|^scripts/tests/test_windows_native_runtime_deps\.py$|^third_party/llama\.cpp/|^\.github/cache-version\.txt$|^\.github/actions/(compute-changes/action\.yml$|setup-windows-rocm-sdk/))' || true)
if [[ -n "$WINDOWS_CPU_INPUTS" ]] || [[ "$BACKEND_RECIPE_CHANGED" == "true" ]]; then
WINDOWS_CPU_BUILD_REQUIRED="true"
fi
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
- '.github/cache-version.txt'
- '.github/workflows/ci.yml'
- '.github/workflows/smoke.yml'
- '.github/actions/compute-changes/action.yml'

ui:
- 'crates/mesh-llm-ui/**'
Expand Down Expand Up @@ -140,10 +141,15 @@ jobs:
- 'crates/skippy-ffi/**'
- 'scripts/build-windows.ps1'
- 'scripts/install-windows-sdk.ps1'
- 'scripts/package-native-runtime.sh'
- 'scripts/verify-native-runtime-package.sh'
- 'scripts/windows-native-runtime-deps.py'
- 'scripts/tests/test_windows_native_runtime_deps.py'
- 'third_party/llama.cpp/**'
- 'Justfile'
- '.github/cache-version.txt'
- '.github/actions/setup-windows-rocm-sdk/**'
- '.github/actions/compute-changes/action.yml'
- '.github/workflows/ci.yml'
- '.github/workflows/windows-warm-caches.yml'

Expand Down Expand Up @@ -1092,6 +1098,14 @@ jobs:
if: ${{ env.RUN_WINDOWS_GPU == 'true' }}
with:
persist-credentials: false
- uses: actions/setup-python@v6
if: ${{ env.RUN_WINDOWS_GPU == 'true' && matrix.backend == 'vulkan' }}
with:
python-version: '3.x'
- name: Test Windows native-runtime dependency resolver
if: ${{ env.RUN_WINDOWS_GPU == 'true' && matrix.backend == 'vulkan' }}
shell: pwsh
run: python -m unittest scripts.tests.test_windows_native_runtime_deps -v
- uses: dtolnay/rust-toolchain@stable
if: ${{ env.RUN_WINDOWS_GPU == 'true' }}
- uses: taiki-e/install-action@just
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/pr_builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,6 @@ jobs:
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/cache-version.txt'
windows_gpu:
- 'crates/skippy-ffi/**'
- 'scripts/build-windows.ps1'
- 'scripts/install-windows-sdk.ps1'
- 'third_party/llama.cpp/**'
- '.github/cache-version.txt'
- '.github/actions/setup-windows-rocm-sdk/**'

docs:
- 'docs/**'
- '**.md'
Expand Down Expand Up @@ -1026,6 +1018,14 @@ jobs:
run: Write-Host "Skipping Windows ${{ matrix.name }} because this change does not touch Windows GPU build inputs."
- uses: actions/checkout@v5
if: ${{ matrix.backend == 'cpu' || env.RUN_WINDOWS_GPU == 'true' }}
- uses: actions/setup-python@v6
if: ${{ env.RUN_WINDOWS_GPU == 'true' && matrix.backend == 'vulkan' }}
with:
python-version: '3.x'
- name: Test Windows native-runtime dependency resolver
if: ${{ env.RUN_WINDOWS_GPU == 'true' && matrix.backend == 'vulkan' }}
shell: pwsh
run: python -m unittest scripts.tests.test_windows_native_runtime_deps -v
- uses: dtolnay/rust-toolchain@stable
if: ${{ matrix.backend == 'cpu' || env.RUN_WINDOWS_GPU == 'true' }}
- uses: taiki-e/install-action@just
Expand Down
19 changes: 19 additions & 0 deletions crates/mesh-llm-system/src/hardware/skippy_devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,23 @@ mod tests {
assert_eq!(facts[0].stable_id.as_deref(), Some("pci:0000:65:00.0"));
assert_eq!(facts[1].backend_device.as_deref(), Some("HIP1"));
}

#[test]
fn vulkan_backend_device_is_runtime_selectable_gpu_fact() {
let facts = gpu_facts_from_backend_devices(vec![BackendDevice {
name: "Vulkan0".to_string(),
description: Some("AMD Radeon RX 9070 XT".to_string()),
device_id: Some("0000:03:00.0".to_string()),
memory_free: 15_000_000_000,
memory_total: 17_179_869_184,
device_type: BackendDeviceType::Gpu,
caps: 0,
}]);

assert_eq!(facts.len(), 1);
assert_eq!(facts[0].display_name, "AMD Radeon RX 9070 XT");
assert_eq!(facts[0].backend_device.as_deref(), Some("Vulkan0"));
assert_eq!(facts[0].vram_bytes, 17_179_869_184);
assert_eq!(facts[0].stable_id.as_deref(), Some("pci:0000:03:00.0"));
}
}
18 changes: 18 additions & 0 deletions crates/skippy-ffi/src/dynamic_library.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use libloading::Library;
use std::path::Path;

#[cfg(target_os = "windows")]
pub(crate) unsafe fn load(path: &Path) -> Result<Library, libloading::Error> {
use libloading::os::windows::{LOAD_WITH_ALTERED_SEARCH_PATH, Library as WindowsLibrary};

// LoadLibraryExW normally searches the application directory for a
// dependent DLL, even when the requested DLL has an absolute path. Native
// runtimes are installed elsewhere, so make the loaded DLL's directory the
// first dependency search location.
unsafe { WindowsLibrary::load_with_flags(path, LOAD_WITH_ALTERED_SEARCH_PATH) }.map(Into::into)
}

#[cfg(not(target_os = "windows"))]
pub(crate) unsafe fn load(path: &Path) -> Result<Library, libloading::Error> {
unsafe { Library::new(path) }
}
5 changes: 4 additions & 1 deletion crates/skippy-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub const FEATURE_NATIVE_MTP_N1: u64 = 1 << 25;
pub const FEATURE_NGRAM_SIMPLE_DRAFT: u64 = 1 << 26;
pub const FEATURE_NGRAM_CACHE_DRAFT: u64 = 1 << 27;

#[cfg(feature = "dynamic-runtime")]
mod dynamic_library;

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AbiVersion {
Expand Down Expand Up @@ -752,7 +755,7 @@ mod dynamic {
let mut libraries = Vec::with_capacity(paths.len());
for path in paths {
libraries.push(
unsafe { Library::new(path.as_ref()) }
unsafe { crate::dynamic_library::load(path.as_ref()) }
.map_err(|err| NativeRuntimeLoadError::Load(err.to_string()))?,
);
}
Expand Down
19 changes: 19 additions & 0 deletions scripts/package-native-runtime.sh
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,25 @@ for library in "${runtime_libraries[@]}"; do
library_paths+=("lib/$name")
done

if [[ "$runtime_os" == "windows" && "$BACKEND" == "vulkan" ]]; then
dependency_args=()
for library in "${runtime_libraries[@]}"; do
dependency_args+=(--search-dir "$(dirname "$library")")
done
"$(python_bin)" "$SCRIPT_DIR/windows-native-runtime-deps.py" collect \
--lib-dir "$stage_dir/lib" \
"${dependency_args[@]}"

library_paths=()
while IFS= read -r library; do
name="$(basename "$library")"
if [[ "$name" != "$primary_name" ]]; then
library_paths+=("lib/$name")
fi
done < <(find "$stage_dir/lib" -maxdepth 1 -type f -name '*.dll' | sort)
library_paths+=("lib/$primary_name")
fi

rewrite_macos_runtime_paths
rewrite_linux_runtime_paths

Expand Down
134 changes: 134 additions & 0 deletions scripts/tests/test_windows_native_runtime_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import importlib.util
import json
import os
import pathlib
import shutil
import struct
import subprocess
import tempfile
import unittest


SCRIPT = pathlib.Path(__file__).parents[1] / "windows-native-runtime-deps.py"
VERIFY_SCRIPT = pathlib.Path(__file__).parents[1] / "verify-native-runtime-package.sh"
SPEC = importlib.util.spec_from_file_location("windows_native_runtime_deps", SCRIPT)
DEPS = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
SPEC.loader.exec_module(DEPS)


def bash_executable() -> str:
if os.name != "nt":
return shutil.which("bash") or "bash"
git = shutil.which("git")
if git:
candidate = pathlib.Path(git).parent.parent / "bin" / "bash.exe"
if candidate.is_file():
return str(candidate)
raise RuntimeError("Git Bash is required for native-runtime verifier tests")


def write_pe(path: pathlib.Path, imports: list[str]) -> None:
data = bytearray(0x800)
data[:2] = b"MZ"
struct.pack_into("<I", data, 0x3C, 0x80)
data[0x80:0x84] = b"PE\0\0"
struct.pack_into("<HHIIIHH", data, 0x84, 0x8664, 1, 0, 0, 0, 0xF0, 0)
optional = 0x98
struct.pack_into("<H", data, optional, 0x20B)
struct.pack_into("<II", data, optional + 120, 0x1000, (len(imports) + 1) * 20)
section = optional + 0xF0
data[section : section + 8] = b".rdata\0\0"
struct.pack_into("<IIII", data, section + 8, 0x500, 0x1000, 0x500, 0x200)
name_offset = 0x300
for index, name in enumerate(imports):
descriptor = 0x200 + index * 20
name_rva = 0x1000 + name_offset - 0x200
struct.pack_into("<IIIII", data, descriptor, 0, 0, 0, name_rva, 0)
encoded = name.encode("ascii") + b"\0"
data[name_offset : name_offset + len(encoded)] = encoded
name_offset += len(encoded)
path.write_bytes(data)


class WindowsNativeRuntimeDepsTests(unittest.TestCase):
def test_reads_pe_import_table(self):
with tempfile.TemporaryDirectory() as directory:
library = pathlib.Path(directory) / "ggml-vulkan.dll"
write_pe(library, ["KERNEL32.dll", "libstdc++-6.dll"])

self.assertEqual(
DEPS.imported_dlls(library), ["KERNEL32.dll", "libstdc++-6.dll"]
)

def test_collects_mingw_dependency_closure(self):
with tempfile.TemporaryDirectory() as directory:
root = pathlib.Path(directory)
lib_dir = root / "artifact" / "lib"
search_dir = root / "sdk" / "Bin"
lib_dir.mkdir(parents=True)
search_dir.mkdir(parents=True)
write_pe(
lib_dir / "ggml-vulkan.dll",
["vulkan-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll"],
)
write_pe(
search_dir / "libstdc++-6.dll",
["KERNEL32.dll", "libgcc_s_seh-1.dll"],
)
write_pe(search_dir / "libwinpthread-1.dll", ["KERNEL32.dll"])
write_pe(search_dir / "libgcc_s_seh-1.dll", ["KERNEL32.dll"])

copied = DEPS.collect_dependencies(lib_dir, [search_dir])

self.assertEqual(
{path.name for path in copied},
{"libgcc_s_seh-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll"},
)
DEPS.verify_dependencies(lib_dir)

def test_verification_rejects_missing_non_system_dependency(self):
with tempfile.TemporaryDirectory() as directory:
lib_dir = pathlib.Path(directory)
write_pe(lib_dir / "ggml-vulkan.dll", ["libstdc++-6.dll"])

with self.assertRaisesRegex(RuntimeError, "libstdc\\+\\+-6.dll"):
DEPS.verify_dependencies(lib_dir)

def test_package_verifier_accepts_closed_windows_dependency_graph(self):
with tempfile.TemporaryDirectory() as directory:
artifact = pathlib.Path(directory) / "meshllm-native-runtime-windows-x86_64-vulkan"
lib_dir = artifact / "lib"
lib_dir.mkdir(parents=True)
write_pe(lib_dir / "ggml-vulkan.dll", ["vulkan-1.dll", "libstdc++-6.dll"])
write_pe(lib_dir / "libstdc++-6.dll", ["KERNEL32.dll"])
write_pe(lib_dir / "llama.dll", ["ggml-vulkan.dll"])
manifest = {
"runtime": {
"id": artifact.name,
"mesh_version": "0.72.1",
"skippy_abi": "0.1.32",
"platform": {"os": "windows", "arch": "x86_64"},
"backend": {"kind": "vulkan"},
"libraries": [
"lib/ggml-vulkan.dll",
"lib/libstdc++-6.dll",
"lib/llama.dll",
],
}
}
(artifact / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")

result = subprocess.run(
[bash_executable(), VERIFY_SCRIPT.as_posix(), artifact.as_posix()],
check=False,
capture_output=True,
text=True,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("verified native runtime artifact", result.stdout)


if __name__ == "__main__":
unittest.main()
25 changes: 25 additions & 0 deletions scripts/verify-native-runtime-package.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TMP_ROOT=""
trap 'rm -rf "$TMP_ROOT"' EXIT

Expand All @@ -14,6 +15,7 @@ Verifies MeshLLM native runtime artifacts:
- all runtime.libraries exist
- library_sha256 matches the primary library
- Linux shared-library RUNPATH/RPATH is relocatable and resolves packaged deps
- Windows non-system DLL imports are present in the artifact
- archive checksum sidecar when present
EOF
}
Expand Down Expand Up @@ -152,9 +154,32 @@ if library_sha256:
PY
verify_macos_runtime_paths "$artifact_dir" "$manifest"
verify_linux_runtime_paths "$artifact_dir" "$manifest"
verify_windows_runtime_dependencies "$artifact_dir" "$manifest"
echo "verified native runtime artifact: $artifact_dir"
}

verify_windows_runtime_dependencies() {
local artifact_dir="$1"
local manifest="$2"
if ! "$(python_bin)" - "$manifest" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as fh:
runtime = json.load(fh)["runtime"]
is_windows_vulkan = (
runtime["platform"].get("os") == "windows"
and runtime["backend"].get("kind") == "vulkan"
)
raise SystemExit(0 if is_windows_vulkan else 1)
PY
then
return 0
fi
"$(python_bin)" "$SCRIPT_DIR/windows-native-runtime-deps.py" verify \
--lib-dir "$artifact_dir/lib"
}

verify_macos_runtime_paths() {
local artifact_dir="$1"
local manifest="$2"
Expand Down
Loading
Loading