Prepare inspected static Metal runtime inventory for macOS staging
This commit is contained in:
1 parent
07bbcb76e8
commit
4a7c4acdcb
11 files changed
+236
-13
No files matched your search
@@ -45,6 +45,7 @@ if(SEARCHI_ACCELERATION STREQUAL "cpu")
|
|||||||
set(GGML_METAL OFF CACHE BOOL "CPU test build" FORCE)
|
set(GGML_METAL OFF CACHE BOOL "CPU test build" FORCE)
|
||||||
elseif(SEARCHI_ACCELERATION STREQUAL "metal" OR (SEARCHI_ACCELERATION STREQUAL "auto" AND APPLE))
|
elseif(SEARCHI_ACCELERATION STREQUAL "metal" OR (SEARCHI_ACCELERATION STREQUAL "auto" AND APPLE))
|
||||||
set(GGML_METAL ON CACHE BOOL "Production Metal backend" FORCE)
|
set(GGML_METAL ON CACHE BOOL "Production Metal backend" FORCE)
|
||||||
|
set(GGML_METAL_EMBED_LIBRARY ON CACHE BOOL "Standalone worker contains Metal kernels" FORCE)
|
||||||
elseif(SEARCHI_ACCELERATION STREQUAL "cuda")
|
elseif(SEARCHI_ACCELERATION STREQUAL "cuda")
|
||||||
set(GGML_CUDA ON CACHE BOOL "Production CUDA backend" FORCE)
|
set(GGML_CUDA ON CACHE BOOL "Production CUDA backend" FORCE)
|
||||||
elseif(SEARCHI_ACCELERATION STREQUAL "vulkan")
|
elseif(SEARCHI_ACCELERATION STREQUAL "vulkan")
|
||||||
|
|||||||
+5
-1
@@ -8,7 +8,11 @@ Windows needs Visual Studio C++ build tools, a Windows SDK, and WebView2.
|
|||||||
Linux desktop builds need WebKitGTK 4.1 development libraries; macOS needs
|
Linux desktop builds need WebKitGTK 4.1 development libraries; macOS needs
|
||||||
Xcode command-line tools. Follow the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/).
|
Xcode command-line tools. Follow the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/).
|
||||||
Headless Linux CLI/worker builds do not need WebKitGTK or a display server.
|
Headless Linux CLI/worker builds do not need WebKitGTK or a display server.
|
||||||
They still need a C/C++ compiler, linker, CMake, and Rust: SQLite, USearch,
|
|
||||||
|
macOS runtime preparation embeds Metal kernel sources and inspects the static
|
||||||
|
worker's Apple dependencies before staging. See [macOS runtime](MACOS-RUNTIME.md)
|
||||||
|
for the prepared workflow and explicitly unexecuted native checks.
|
||||||
|
Headless builds still need a C/C++ compiler, linker, CMake, and Rust: SQLite, USearch,
|
||||||
NumKong, and the Google worker contain native code.
|
NumKong, and the Google worker contain native code.
|
||||||
|
|
||||||
## Windows CPU development
|
## Windows CPU development
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# macOS runtime preparation
|
||||||
|
|
||||||
|
Normal macOS `auto` builds select Metal with CPU fallback. The worker links
|
||||||
|
llama/ggml statically and uses Apple system libraries/frameworks. Searchi now
|
||||||
|
forces `GGML_METAL_EMBED_LIBRARY=ON` so upstream Metal kernel sources reside
|
||||||
|
inside the worker rather than requiring loose kernel files from the build tree.
|
||||||
|
|
||||||
|
After compilation, the builder inspects the artifact using Apple `lipo` and
|
||||||
|
`otool`; it does not execute the worker or initialize a GPU. Inspection requires:
|
||||||
|
|
||||||
|
- Supported arm64/x86_64 architectures, including both for a universal artifact.
|
||||||
|
- Only explicitly allowed Apple system libraries/frameworks, including Metal.
|
||||||
|
- A nonempty `__DATA,__ggml_metallib` section for each architecture.
|
||||||
|
- Current build-input provenance and a SHA-256 of the resulting worker.
|
||||||
|
|
||||||
|
The generated inventory declares the `static-metal-embedded` layout with an
|
||||||
|
empty packaged-library list. Normal macOS staging now requires this inventory;
|
||||||
|
the previous optional-inventory exception is removed for `auto`/`metal`.
|
||||||
|
Staging verifies source identity, worker bytes, Mach-O magic and the static
|
||||||
|
layout instead of requiring Windows DLLs or Linux shared libraries. Explicit
|
||||||
|
internal CPU tooling keeps its optional-inventory behavior.
|
||||||
|
|
||||||
|
On a native Mac with the required Apple build tools:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 tools/embeddinggemma2/build_runtime.py --backend auto --jobs 2
|
||||||
|
python3 tools/embeddinggemma2/stage_runtime.py --backend auto
|
||||||
|
```
|
||||||
|
|
||||||
|
These commands are prepared, **not executed on macOS**. Native compilation,
|
||||||
|
actual `lipo`/`otool` output, relocation outside the source tree, genuine Google
|
||||||
|
CPU/Metal inference, device fallback, app integration and signing/notarization
|
||||||
|
remain open. This change does not create a macOS release download.
|
||||||
|
|
||||||
|
## Evidence, 2026-10-13
|
||||||
|
|
||||||
|
Four controlled Mach-O metadata-fixture tests passed, covering static system
|
||||||
|
dependencies, foreign library rejection, missing Metal data and per-architecture
|
||||||
|
section coverage. Staging tests exercise the empty-library macOS layout and the
|
||||||
|
mandatory normal-profile policy. These fixtures are not executable Mac builds.
|
||||||
|
|
||||||
|
Across macOS metadata, staging, builder, provenance and Windows/Linux packaging
|
||||||
|
suites: 75 tests passed with one platform-specific skip, in normal and optimized
|
||||||
|
Python modes. Actual Windows and Ubuntu WSL normal runtime builds passed after
|
||||||
|
the recipe change. Windows staging and Linux inventory verification passed.
|
||||||
|
Worker hashes remain identical to their previously tested CPU deployments:
|
||||||
|
Windows `c01cb3ec13cfca722ee768e8350e1399a8b9a4d14a24a35adc3473a5cfc82a73`;
|
||||||
|
Linux `b52d50a1e676bd4bf64124daff1c4ce83d86a8bde01e4eaf09838cbb24a4c28f`.
|
||||||
|
No GPU inference or native macOS execution was performed.
|
||||||
@@ -122,6 +122,10 @@ release. Keep their evidence and unchecked production gates intact.
|
|||||||
- [x] Normal Windows/Linux source-build provenance gate before staging and
|
- [x] Normal Windows/Linux source-build provenance gate before staging and
|
||||||
archive preparation; stale inventories preserve existing resources/packages.
|
archive preparation; stale inventories preserve existing resources/packages.
|
||||||
Actual normal builds verified. See [runtime provenance](RUNTIME-PROVENANCE.md).
|
Actual normal builds verified. See [runtime provenance](RUNTIME-PROVENANCE.md).
|
||||||
|
- [x] Prepare static macOS Metal inventory and mandatory normal-profile staging,
|
||||||
|
with embedded-source/dependency/architecture metadata regressions.
|
||||||
|
- [ ] Execute native macOS runtime build, relocation and genuine CPU/Metal
|
||||||
|
inference; fixture metadata tests do not satisfy this gate.
|
||||||
- [ ] Verify power-loss durability and release-tool interaction with resource
|
- [ ] Verify power-loss durability and release-tool interaction with resource
|
||||||
directory swaps on each OS; fault-injected process interruption is narrower.
|
directory swaps on each OS; fault-injected process interruption is narrower.
|
||||||
- [x] Shared monotonic Basic/Debug search metrics, granular bounded engine traces,
|
- [x] Shared monotonic Basic/Debug search metrics, granular bounded engine traces,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ release preparation now records and checks build-input identity before staging
|
|||||||
or packaging a normal Windows/Linux runtime.
|
or packaging a normal Windows/Linux runtime.
|
||||||
|
|
||||||
`runtime-libraries.json` includes `build_provenance`, schema 1, the pinned
|
`runtime-libraries.json` includes `build_provenance`, schema 1, the pinned
|
||||||
llama.cpp commit and SHA-256 of six bounded inputs: worker.cpp, native
|
llama.cpp commit and SHA-256 of seven bounded inputs: worker.cpp, native
|
||||||
CMakeLists.txt, build_runtime.py and the three runtime patches. Only CRLF line
|
CMakeLists.txt, build_runtime.py, the macOS artifact inspector and the three runtime patches. Only CRLF line
|
||||||
endings in these build inputs are canonicalized to LF, allowing the same
|
endings in these build inputs are canonicalized to LF, allowing the same
|
||||||
source revision to compare across checkouts. Indexed user-file bytes and their
|
source revision to compare across checkouts. Indexed user-file bytes and their
|
||||||
extraction provenance are unaffected.
|
extraction provenance are unaffected.
|
||||||
@@ -29,8 +29,8 @@ check and were not altered retroactively.
|
|||||||
|
|
||||||
## Recorded evidence, 2026-10-13
|
## Recorded evidence, 2026-10-13
|
||||||
|
|
||||||
- 69 tests passed across provenance, builder, staging, Windows packaging and
|
- 75 tests passed across provenance, builder, macOS metadata, staging, Windows
|
||||||
Linux packaging suites; one platform-specific staging test remained skipped.
|
packaging and Linux packaging suites; one platform-specific staging test remained skipped.
|
||||||
The same suites passed under optimized Python. Regressions reject a valid old
|
The same suites passed under optimized Python. Regressions reject a valid old
|
||||||
inventory without provenance, changed source and oversized inputs, while
|
inventory without provenance, changed source and oversized inputs, while
|
||||||
preserving existing archives. Build-source CRLF/LF equivalence is checked.
|
preserving existing archives. Build-source CRLF/LF equivalence is checked.
|
||||||
@@ -39,17 +39,18 @@ check and were not altered retroactively.
|
|||||||
`c01cb3ec13cfca722ee768e8350e1399a8b9a4d14a24a35adc3473a5cfc82a73`;
|
`c01cb3ec13cfca722ee768e8350e1399a8b9a4d14a24a35adc3473a5cfc82a73`;
|
||||||
all 25 Windows DLL hashes also match the previously tested release exactly.
|
all 25 Windows DLL hashes also match the previously tested release exactly.
|
||||||
New staged inventory SHA-256:
|
New staged inventory SHA-256:
|
||||||
`7fde510984b4635ce127d25ad91aab9a391300d7968ac71e4f201cc8edd052e8`.
|
`b3859169d2c21ee5d40cdca038c3494f5620fe544adb2dd404d4535c89b5076b`.
|
||||||
- An actual historical extracted Windows inventory was rejected by normal
|
- An actual historical extracted Windows inventory was rejected by normal
|
||||||
staging preflight for missing provenance. Its files were only read.
|
staging preflight for missing provenance. Its files were only read.
|
||||||
- Actual normal Linux `auto` build passed on Ubuntu 24.04 WSL. The packager
|
- Actual normal Linux `auto` build passed on Ubuntu 24.04 WSL. The packager
|
||||||
verified 21 runtime inventory/files and all six source identities against the
|
verified 21 runtime inventory/files and all seven source identities against the
|
||||||
current repository, including checkout line-ending differences.
|
current repository, including checkout line-ending differences.
|
||||||
Worker SHA-256 remains
|
Worker SHA-256 remains
|
||||||
`b52d50a1e676bd4bf64124daff1c4ce83d86a8bde01e4eaf09838cbb24a4c28f`.
|
`b52d50a1e676bd4bf64124daff1c4ce83d86a8bde01e4eaf09838cbb24a4c28f`.
|
||||||
New inventory SHA-256:
|
New inventory SHA-256:
|
||||||
`de6e06c68815b35d83c6acbde468d100413f47c74536f0d4219a9edcbeb01a86`.
|
`6463ae8a126f8de3ca4349bbb02fd36852f30222fe30e11fb4070385ce1989e8`.
|
||||||
|
|
||||||
These normal builds retain GPU modules plus CPU fallback; no GPU inference was
|
These normal builds retain GPU modules plus CPU fallback; no GPU inference was
|
||||||
executed. macOS inventory integration, cross-compilation and actual GPU checks
|
executed. macOS inventory preparation is now implemented and fixture-tested;
|
||||||
|
[native macOS checks](MACOS-RUNTIME.md), cross-compilation and actual GPU checks
|
||||||
remain open. Published Windows and Linux assets retain their original hashes.
|
remain open. Published Windows and Linux assets retain their original hashes.
|
||||||
@@ -10,6 +10,7 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from runtime_provenance import fingerprint, UPSTREAM_COMMIT
|
from runtime_provenance import fingerprint, UPSTREAM_COMMIT
|
||||||
|
from macos_runtime import inspect as inspect_macos
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
TAG = "b11461"
|
TAG = "b11461"
|
||||||
@@ -215,6 +216,15 @@ def main():
|
|||||||
os.replace(temporary, directory / "runtime-libraries.json")
|
os.replace(temporary, directory / "runtime-libraries.json")
|
||||||
if linux_dispatch(args.backend):
|
if linux_dispatch(args.backend):
|
||||||
bundle_linux_libraries(build, provenance)
|
bundle_linux_libraries(build, provenance)
|
||||||
|
if sys.platform == "darwin" and args.backend in {"auto", "metal"}:
|
||||||
|
worker = build / "searchi-gemma-worker"
|
||||||
|
inventory = inspect_macos(worker)
|
||||||
|
with worker.open("rb") as stream:
|
||||||
|
inventory["worker_sha256"] = hashlib.file_digest(stream, "sha256").hexdigest()
|
||||||
|
inventory["build_provenance"] = provenance
|
||||||
|
temporary = build / "runtime-libraries.json.partial"
|
||||||
|
temporary.write_text(json.dumps(inventory, indent=2), encoding="utf-8")
|
||||||
|
os.replace(temporary, build / "runtime-libraries.json")
|
||||||
print(f"Runtime build complete: {build}")
|
print(f"Runtime build complete: {build}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"""Inspect a static Metal worker using Apple's tools, without executing it."""
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
MAGIC = {b"\xcf\xfa\xed\xfe", b"\xfe\xed\xfa\xcf", b"\xca\xfe\xba\xbe",
|
||||||
|
b"\xbe\xba\xfe\xca", b"\xca\xfe\xba\xbf", b"\xbf\xba\xfe\xca"}
|
||||||
|
SYSTEM_LIBRARIES = {"/usr/lib/libSystem.B.dylib", "/usr/lib/libc++.1.dylib",
|
||||||
|
"/usr/lib/libc++abi.dylib", "/usr/lib/libobjc.A.dylib"}
|
||||||
|
FRAMEWORKS = {"Foundation", "CoreFoundation", "Metal", "MetalKit", "Accelerate"}
|
||||||
|
|
||||||
|
|
||||||
|
def system_dependency(name):
|
||||||
|
if name in SYSTEM_LIBRARIES:
|
||||||
|
return True
|
||||||
|
return any(re.fullmatch(rf"/System/Library/Frameworks/{framework}\.framework/Versions/[A-Z]/{framework}", name)
|
||||||
|
for framework in FRAMEWORKS)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_inventory(manifest, worker):
|
||||||
|
with Path(worker).open("rb") as stream:
|
||||||
|
if stream.read(4) not in MAGIC:
|
||||||
|
raise RuntimeError("macOS worker is not a Mach-O artifact")
|
||||||
|
arches = manifest.get("architectures")
|
||||||
|
dependencies = manifest.get("system_libraries")
|
||||||
|
if (manifest.get("platform") != "macos" or manifest.get("layout") != "static-metal-embedded" or manifest.get("libraries") != []
|
||||||
|
or manifest.get("metal_embedded") is not True
|
||||||
|
or not isinstance(arches, list) or not 1 <= len(arches) <= 2
|
||||||
|
or not all(isinstance(arch, str) for arch in arches)
|
||||||
|
or len(set(arches)) != len(arches) or not set(arches) <= {"arm64", "x86_64"}
|
||||||
|
or not isinstance(dependencies, list) or not 1 <= len(dependencies) <= 32
|
||||||
|
or not all(isinstance(name, str) and system_dependency(name) for name in dependencies)
|
||||||
|
or not any("/Metal.framework/" in name for name in dependencies)):
|
||||||
|
raise RuntimeError("Invalid static Metal runtime inventory")
|
||||||
|
|
||||||
|
|
||||||
|
def inspect(worker):
|
||||||
|
worker = Path(worker)
|
||||||
|
|
||||||
|
def tool(arguments):
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(arguments, text=True, timeout=30)
|
||||||
|
except FileNotFoundError as error:
|
||||||
|
raise RuntimeError("macOS runtime inspection requires Apple lipo/otool") from error
|
||||||
|
if len(output.encode("utf-8")) > 65536:
|
||||||
|
raise RuntimeError("macOS metadata exceeds inspection limit")
|
||||||
|
return output
|
||||||
|
|
||||||
|
arches = tool(["lipo", "-archs", str(worker)]).split()
|
||||||
|
dependencies = []
|
||||||
|
for line in tool(["otool", "-L", str(worker)]).splitlines():
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
if line == f"{worker}:" or line in {f"{worker} (architecture {arch}):" for arch in arches}:
|
||||||
|
continue
|
||||||
|
match = re.fullmatch(r"\s+(.+?) \(compatibility version [0-9.]+, current version [0-9.]+\)", line)
|
||||||
|
if not match:
|
||||||
|
raise RuntimeError("Unrecognized Mach-O dependency metadata")
|
||||||
|
if match[1] not in dependencies:
|
||||||
|
dependencies.append(match[1])
|
||||||
|
sections = tool(["otool", "-l", str(worker)])
|
||||||
|
sizes = re.findall(r"sectname\s+__ggml_metallib\s+segname\s+__DATA\s+addr\s+0x[0-9a-fA-F]+\s+size\s+(0x[0-9a-fA-F]+)", sections)
|
||||||
|
if len(sizes) != len(arches) or not all(int(size, 16) > 0 for size in sizes):
|
||||||
|
raise RuntimeError("Each Mach-O architecture must contain nonempty embedded Metal kernels")
|
||||||
|
result = {"platform": "macos", "layout": "static-metal-embedded",
|
||||||
|
"architectures": arches, "system_libraries": sorted(dependencies),
|
||||||
|
"metal_embedded": True, "libraries": []}
|
||||||
|
validate_inventory(result, worker)
|
||||||
|
return result
|
||||||
@@ -8,6 +8,7 @@ INPUTS = (
|
|||||||
"crates/searchi-semantic/native/worker.cpp",
|
"crates/searchi-semantic/native/worker.cpp",
|
||||||
"crates/searchi-semantic/native/CMakeLists.txt",
|
"crates/searchi-semantic/native/CMakeLists.txt",
|
||||||
"tools/embeddinggemma2/build_runtime.py",
|
"tools/embeddinggemma2/build_runtime.py",
|
||||||
|
"tools/embeddinggemma2/macos_runtime.py",
|
||||||
"tools/embeddinggemma2/no-logits.patch",
|
"tools/embeddinggemma2/no-logits.patch",
|
||||||
"tools/embeddinggemma2/cpu-dispatch.patch",
|
"tools/embeddinggemma2/cpu-dispatch.patch",
|
||||||
"tools/embeddinggemma2/cpu-only-loader.patch",
|
"tools/embeddinggemma2/cpu-only-loader.patch",
|
||||||
|
|||||||
@@ -11,11 +11,17 @@ import stat
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from runtime_provenance import verify as verify_provenance
|
from runtime_provenance import verify as verify_provenance
|
||||||
|
from macos_runtime import validate_inventory as validate_macos
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
CRT_LIBRARIES = {"concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll", "msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll", "vcruntime140_threads.dll"}
|
CRT_LIBRARIES = {"concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll", "msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll", "vcruntime140_threads.dll"}
|
||||||
|
|
||||||
|
|
||||||
|
def inventory_required(backend, windows, system):
|
||||||
|
return ((windows or system.startswith("linux")) and backend in {"auto", "vulkan"}
|
||||||
|
or system == "darwin" and backend in {"auto", "metal"})
|
||||||
|
|
||||||
|
|
||||||
def library_name(name):
|
def library_name(name):
|
||||||
return isinstance(name, str) and bool(re.fullmatch(
|
return isinstance(name, str) and bool(re.fullmatch(
|
||||||
r"(?:[A-Za-z0-9_-]+\.dll|lib[A-Za-z0-9_-]+\.so(?:\.\d+)*)", name))
|
r"(?:[A-Za-z0-9_-]+\.dll|lib[A-Za-z0-9_-]+\.so(?:\.\d+)*)", name))
|
||||||
@@ -349,7 +355,8 @@ def verify_runtime(source, required=False):
|
|||||||
if hashlib.file_digest(stream, "sha256").hexdigest() != manifest.get("worker_sha256"):
|
if hashlib.file_digest(stream, "sha256").hexdigest() != manifest.get("worker_sha256"):
|
||||||
raise RuntimeError("Runtime worker checksum mismatch")
|
raise RuntimeError("Runtime worker checksum mismatch")
|
||||||
records = manifest.get("libraries")
|
records = manifest.get("libraries")
|
||||||
if not isinstance(records, list) or not 1 <= len(records) <= 64:
|
macos = manifest.get("platform") == "macos"
|
||||||
|
if not isinstance(records, list) or not (0 if macos else 1) <= len(records) <= 64:
|
||||||
raise RuntimeError("Invalid runtime dependency inventory")
|
raise RuntimeError("Invalid runtime dependency inventory")
|
||||||
paths, names = [], set()
|
paths, names = [], set()
|
||||||
for record in records:
|
for record in records:
|
||||||
@@ -373,9 +380,13 @@ def verify_runtime(source, required=False):
|
|||||||
raise RuntimeError(f"Runtime library checksum mismatch: {name}")
|
raise RuntimeError(f"Runtime library checksum mismatch: {name}")
|
||||||
paths.append(artifact)
|
paths.append(artifact)
|
||||||
if required:
|
if required:
|
||||||
if (source.suffix == ".exe") == (manifest.get("platform") == "linux"):
|
platform = manifest.get("platform")
|
||||||
|
if (source.suffix == ".exe" and platform not in {None, "windows"}) or (source.suffix != ".exe" and platform not in {"linux", "macos"}):
|
||||||
raise RuntimeError("Runtime inventory platform does not match worker")
|
raise RuntimeError("Runtime inventory platform does not match worker")
|
||||||
if manifest.get("platform") == "linux":
|
if macos:
|
||||||
|
validate_macos(manifest, source)
|
||||||
|
complete = not names and source.suffix != ".exe"
|
||||||
|
elif manifest.get("platform") == "linux":
|
||||||
core = {"ggml", "ggml-base", "llama", "mtmd"}
|
core = {"ggml", "ggml-base", "llama", "mtmd"}
|
||||||
present = {name for name in core if any(re.fullmatch(rf"lib{re.escape(name)}\.so(?:\.\d+)*", file) for file in names)}
|
present = {name for name in core if any(re.fullmatch(rf"lib{re.escape(name)}\.so(?:\.\d+)*", file) for file in names)}
|
||||||
complete = present == core and {"libggml-cpu-x64.so", "libggml-vulkan.so"}.issubset(names)
|
complete = present == core and {"libggml-cpu-x64.so", "libggml-vulkan.so"}.issubset(names)
|
||||||
@@ -421,7 +432,7 @@ def main():
|
|||||||
license_path = ROOT / ".searchi/references/llama.cpp/LICENSE"
|
license_path = ROOT / ".searchi/references/llama.cpp/LICENSE"
|
||||||
if not license_path.is_file():
|
if not license_path.is_file():
|
||||||
raise RuntimeError("Native worker license is missing")
|
raise RuntimeError("Native worker license is missing")
|
||||||
required_inventory = (os.name == "nt" or os.sys.platform.startswith("linux")) and args.backend in {"auto", "vulkan"}
|
required_inventory = inventory_required(args.backend, os.name == "nt", os.sys.platform)
|
||||||
dependencies = verify_runtime(source, required_inventory)
|
dependencies = verify_runtime(source, required_inventory)
|
||||||
crt_notice = source.parent / "MSVC-REDISTRIBUTION.txt"
|
crt_notice = source.parent / "MSVC-REDISTRIBUTION.txt"
|
||||||
if os.name == "nt" and args.backend != "cpu":
|
if os.name == "nt" and args.backend != "cpu":
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import macos_runtime
|
||||||
|
|
||||||
|
|
||||||
|
class MacRuntimeTests(unittest.TestCase):
|
||||||
|
def inspect_fixture(self, dependency=None, sections=1, arches="arm64"):
|
||||||
|
with tempfile.TemporaryDirectory(prefix="mac metadata ") as directory:
|
||||||
|
worker = Path(directory) / "searchi-gemma-worker"
|
||||||
|
worker.write_bytes(b"\xcf\xfa\xed\xfe" + b"metadata fixture, not executable")
|
||||||
|
libraries = f"{worker}:\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 100.0.0)\n"
|
||||||
|
libraries += "\t" + (dependency or "/System/Library/Frameworks/Metal.framework/Versions/A/Metal") + " (compatibility version 1.0.0, current version 100.0.0)\n"
|
||||||
|
loads = "sectname __ggml_metallib\nsegname __DATA\naddr 0x1000\nsize 0x100\n" * sections
|
||||||
|
with patch.object(macos_runtime.subprocess, "check_output", side_effect=[arches, libraries, loads]):
|
||||||
|
return macos_runtime.inspect(worker)
|
||||||
|
|
||||||
|
def test_static_system_metal_inventory_has_no_packaged_dylibs(self):
|
||||||
|
result = self.inspect_fixture()
|
||||||
|
self.assertEqual(result["architectures"], ["arm64"])
|
||||||
|
self.assertEqual(result["libraries"], [])
|
||||||
|
self.assertTrue(result["metal_embedded"])
|
||||||
|
|
||||||
|
def test_external_library_and_missing_metal_are_rejected(self):
|
||||||
|
for dependency in ("/private/build path/libggml.dylib", "/usr/lib/libc++.1.dylib"):
|
||||||
|
with self.subTest(dependency=dependency), self.assertRaises(RuntimeError):
|
||||||
|
self.inspect_fixture(dependency=dependency)
|
||||||
|
|
||||||
|
def test_all_architectures_require_embedded_sources(self):
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "Each Mach-O architecture"):
|
||||||
|
self.inspect_fixture(sections=0)
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "Each Mach-O architecture"):
|
||||||
|
self.inspect_fixture(sections=1, arches="arm64 x86_64")
|
||||||
|
self.assertEqual(self.inspect_fixture(sections=2, arches="arm64 x86_64")["architectures"], ["arm64", "x86_64"])
|
||||||
|
|
||||||
|
def test_malformed_inventory_and_foreign_artifact_fail(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
worker = Path(directory) / "worker"
|
||||||
|
worker.write_bytes(b"\xcf\xfa\xed\xfe")
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
macos_runtime.validate_inventory({"architectures": [["arm64"]]}, worker)
|
||||||
|
worker.write_bytes(b"MZxx")
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "Mach-O"):
|
||||||
|
macos_runtime.validate_inventory({}, worker)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -13,6 +13,29 @@ import runtime_provenance
|
|||||||
|
|
||||||
|
|
||||||
class StagingTests(unittest.TestCase):
|
class StagingTests(unittest.TestCase):
|
||||||
|
def test_normal_macos_requires_inventory_and_internal_cpu_remains_optional(self):
|
||||||
|
self.assertTrue(stage_runtime.inventory_required("auto", False, "darwin"))
|
||||||
|
self.assertTrue(stage_runtime.inventory_required("metal", False, "darwin"))
|
||||||
|
self.assertFalse(stage_runtime.inventory_required("cpu", False, "darwin"))
|
||||||
|
self.assertTrue(stage_runtime.inventory_required("auto", True, "win32"))
|
||||||
|
self.assertTrue(stage_runtime.inventory_required("auto", False, "linux"))
|
||||||
|
def test_static_macos_inventory_accepts_no_dynamic_libraries(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
worker = Path(directory) / "searchi-gemma-worker"
|
||||||
|
worker.write_bytes(b"\xcf\xfa\xed\xfe" + b"metadata fixture, not executable")
|
||||||
|
manifest = {"platform": "macos", "layout": "static-metal-embedded",
|
||||||
|
"architectures": ["arm64"], "metal_embedded": True, "libraries": [],
|
||||||
|
"system_libraries": ["/usr/lib/libSystem.B.dylib", "/System/Library/Frameworks/Metal.framework/Versions/A/Metal"],
|
||||||
|
"worker_sha256": hashlib.sha256(worker.read_bytes()).hexdigest(),
|
||||||
|
"build_provenance": runtime_provenance.fingerprint()}
|
||||||
|
inventory = worker.parent / "runtime-libraries.json"
|
||||||
|
inventory.write_text(json.dumps(manifest))
|
||||||
|
self.assertEqual(stage_runtime.verify_runtime(worker, True), [inventory])
|
||||||
|
manifest["metal_embedded"] = False
|
||||||
|
inventory.write_text(json.dumps(manifest))
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
stage_runtime.verify_runtime(worker, True)
|
||||||
|
|
||||||
def test_linux_inventory_stages_core_and_modules_and_removes_previous_owned_library(self):
|
def test_linux_inventory_stages_core_and_modules_and_removes_previous_owned_library(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
root = Path(directory)
|
root = Path(directory)
|
||||||
|
|||||||
Reference in new issue
Block a user