133 lines
7.2 KiB
Python
133 lines
7.2 KiB
Python
"""Build a pinned isolated native worker; development VM builds use --backend cpu."""
|
||
import argparse
|
||
import hashlib
|
||
import json
|
||
import os
|
||
import re
|
||
from pathlib import Path
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
TAG = "b11461"
|
||
COMMIT = "4d756bc72bf00a4aacf410ae15a2d315f3db400d"
|
||
WINDOWS_CRT_FILES = (
|
||
"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 bundle_windows_crt(build):
|
||
"""Copy unchanged release CRT from the selected Visual Studio installation."""
|
||
cache = (build / "CMakeCache.txt").read_text(encoding="utf-8")
|
||
match = re.search(r"^CMAKE_GENERATOR_INSTANCE:[^=]+=([^\r\n]+)$", cache, re.MULTILINE)
|
||
if not match:
|
||
raise RuntimeError("Selected Visual Studio installation is missing from CMake cache")
|
||
installation = Path(match.group(1))
|
||
auxiliary = installation / "VC/Auxiliary/Build"
|
||
version = (auxiliary / "Microsoft.VCRedistVersion.default.txt").read_text().strip()
|
||
tools_version = (auxiliary / "Microsoft.VCToolsVersion.default.txt").read_text().strip()
|
||
if not re.fullmatch(r"\d+\.\d+\.\d+", version) or version.split(".")[:2] != tools_version.split(".")[:2]:
|
||
raise RuntimeError("Visual Studio CRT redistributable does not match selected build tools")
|
||
cmake_version = [re.search(rf"^CMAKE_CACHE_{part}_VERSION:[^=]+=(\d+)$", cache, re.MULTILINE)
|
||
for part in ("MAJOR", "MINOR", "PATCH")]
|
||
if all(cmake_version):
|
||
compiler_info = build / "CMakeFiles" / ".".join(match.group(1) for match in cmake_version) / "CMakeCXXCompiler.cmake"
|
||
if compiler_info.is_file():
|
||
compiler = compiler_info.read_text(encoding="utf-8")
|
||
actual = re.search(r'set\(CMAKE_CXX_COMPILER_VERSION "(\d+)\.(\d+)\.[^"]+"\)', compiler)
|
||
if not actual or (int(actual.group(1)), int(actual.group(2))) != (19, int(version.split(".")[1])):
|
||
raise RuntimeError("Visual Studio CRT redistributable does not match actual selected compiler")
|
||
source = installation / "VC/Redist/MSVC" / version / "x64/Microsoft.VC143.CRT"
|
||
files = [source / name for name in WINDOWS_CRT_FILES]
|
||
if any(not path.is_file() or path.is_symlink() for path in files):
|
||
raise RuntimeError("Required release Visual Studio CRT redistributable is missing")
|
||
destination = build / "Release"
|
||
for path in files:
|
||
shutil.copy2(path, destination / path.name)
|
||
(destination / "MSVC-REDISTRIBUTION.txt").write_text(
|
||
"Microsoft Visual C++ release runtime — copyright Microsoft Corporation.\n"
|
||
f"Unmodified x64 redistributable files from Visual Studio VC/Redist/MSVC/{version}/x64/Microsoft.VC143.CRT.\n"
|
||
"These proprietary files are distributed under Microsoft's applicable Visual Studio license terms, not searchi's license.\n"
|
||
"Terms: https://visualstudio.microsoft.com/license-terms/vs2022-ga-diagnosticbuildtools/\n"
|
||
"Redistributable list: https://learn.microsoft.com/en-us/visualstudio/releases/2022/redistribution\n"
|
||
"Deployment guidance: https://learn.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files\n",
|
||
encoding="utf-8")
|
||
|
||
|
||
def run(arguments, cwd=None):
|
||
subprocess.run([str(value) for value in arguments], cwd=cwd, check=True)
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="Build Searchi's pinned Google embedding worker")
|
||
parser.add_argument("--backend", choices=["cpu", "auto", "metal", "vulkan", "cuda"], default="auto")
|
||
parser.add_argument("--jobs", type=int, default=2)
|
||
args = parser.parse_args()
|
||
if not 1 <= args.jobs <= 16:
|
||
parser.error("jobs must be 1–16")
|
||
vulkan_loader = None
|
||
if os.name == "nt" and args.backend in {"auto", "vulkan"}:
|
||
from provision_vulkan import provision
|
||
sdk, vulkan_loader = provision()
|
||
os.environ["VULKAN_SDK"] = str(sdk)
|
||
source = ROOT / ".searchi/references/llama.cpp"
|
||
build = ROOT / ".searchi/runtime" / args.backend
|
||
if not source.exists():
|
||
source.parent.mkdir(parents=True, exist_ok=True)
|
||
run(["git", "clone", "--depth", "1", "--branch", TAG,
|
||
"https://github.com/ggml-org/llama.cpp.git", source])
|
||
actual = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=source, text=True).strip()
|
||
if actual != COMMIT:
|
||
raise RuntimeError("Runtime reference checkout differs from pinned commit; refusing to reset it")
|
||
patch = Path(__file__).with_name("no-logits.patch")
|
||
check = subprocess.run(["git", "apply", "--check", str(patch)], cwd=source, capture_output=True)
|
||
if check.returncode == 0:
|
||
run(["git", "apply", patch], cwd=source)
|
||
else:
|
||
reverse = subprocess.run(["git", "apply", "--reverse", "--check", str(patch)], cwd=source, capture_output=True)
|
||
if reverse.returncode:
|
||
raise RuntimeError("Runtime memory patch cannot be applied or verified")
|
||
if os.name == "nt" and args.backend != "cpu":
|
||
for patch_name in ["cpu-dispatch.patch", "cpu-only-loader.patch"]:
|
||
dispatch_patch = Path(__file__).with_name(patch_name)
|
||
check = subprocess.run(["git", "apply", "--check", str(dispatch_patch)], cwd=source, capture_output=True)
|
||
if check.returncode == 0:
|
||
run(["git", "apply", dispatch_patch], cwd=source)
|
||
elif subprocess.run(["git", "apply", "--reverse", "--check", str(dispatch_patch)], cwd=source, capture_output=True).returncode:
|
||
raise RuntimeError(f"Runtime patch cannot be applied or verified: {patch_name}")
|
||
cmake_exe = shutil.which("cmake")
|
||
if not cmake_exe:
|
||
try:
|
||
import cmake
|
||
cmake_exe = str(Path(cmake.CMAKE_BIN_DIR) / ("cmake.exe" if os.name == "nt" else "cmake"))
|
||
except ImportError as error:
|
||
raise RuntimeError("Install CMake first (python -m pip install cmake)") from error
|
||
worker_source = ROOT / "crates/searchi-semantic/native"
|
||
run([cmake_exe, "-S", worker_source, "-B", build,
|
||
"-DCMAKE_BUILD_TYPE=Release", f"-DLLAMA_CPP_DIR={source}",
|
||
f"-DSEARCHI_ACCELERATION={args.backend}"])
|
||
run([cmake_exe, "--build", build, "--config", "Release", "--parallel", args.jobs])
|
||
if vulkan_loader:
|
||
for name in ["vulkan-1.dll", "VULKAN-LICENSE.txt"]:
|
||
shutil.copy2(vulkan_loader / name, build / "Release" / name)
|
||
if os.name == "nt" and args.backend != "cpu":
|
||
directory = build / "Release"
|
||
bundle_windows_crt(build)
|
||
def checksum(path):
|
||
with path.open("rb") as stream:
|
||
return hashlib.file_digest(stream, "sha256").hexdigest()
|
||
inventory = {"worker_sha256": checksum(directory / "searchi-gemma-worker.exe"),
|
||
"libraries": [{"path": path.name, "bytes": path.stat().st_size, "sha256": checksum(path)}
|
||
for path in sorted(directory.glob("*.dll"))]}
|
||
temporary = directory / "runtime-libraries.json.partial"
|
||
temporary.write_text(json.dumps(inventory, indent=2), encoding="utf-8")
|
||
os.replace(temporary, directory / "runtime-libraries.json")
|
||
print(f"Runtime build complete: {build}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|