diff --git a/external-builds/llama.cpp/README.md b/external-builds/llama.cpp/README.md new file mode 100644 index 00000000000..4af7ce5d562 --- /dev/null +++ b/external-builds/llama.cpp/README.md @@ -0,0 +1,121 @@ +# Build llama.cpp with ROCm Support + +This integration adds **`llama.cpp`** as an external subproject to **TheRock**, enabling AMD GPU acceleration via the **ROCm** platform. + +[`llama.cpp`](https://github.com/ggerganov/llama.cpp) is an open-source project providing a lightweight, efficient C/C++ implementation of **Large Language Model** architectures. It enables running LLMs locally on CPU and GPU with minimal dependencies. Designed for **portability**, **performance**, and **ease of integration**, it serves as an ideal backend for research, development, and embedded AI applications. + +This document describes how to configure, build, and test `llama.cpp` as part of TheRock build system with ROCm acceleration enabled. + +______________________________________________________________________ + +## Table of Contents + +- [Support Status](#support-status) +- [Build Instructions](#build-instructions) +- [Running / Testing llama.cpp](#running--testing-llamacpp) + +## Support Status + +| Project / Feature | Linux Support | Windows Support | +| ----------------- | --------------- | --------------- | +| **llama.cpp** | ✅ Passes Tests | ✅ Passes Tests | + +> [!NOTE] +> Building **rocWMMA** is currently **not implemented** in TheRock. + +> [!WARNING] +> Certain tests are known to fail on **gfx1030** and **gfx1031** GPU targets. + +## Build Instructions + +By default, scripts use the **upstream** `llama.cpp` repository. +You can modify the source path or reference your own fork if desired. + +### Prerequisites and Setup + +The provided build scripts assume: + +- You have already completed a **full ROCm build** using TheRock. +- You have the **`rocm-sdk`** tool installed, which is used internally to: + - Locate the ROCm build directory. + - Identify available AMD GPU targets. +- You have environemnt variables set up correctly for ROCm, including PATH updates to find ROCm libraries at runtime. + +Before building, ensure the following: + +- **ROCm is installed** and accessible. +- You have **Python 3.8+** installed and accessible. + +> [!NOTE] +> The default path is determined automatically using `rocm-sdk path --root` + +### Quickstart + +Building `llama.cpp` is a two-step process: + +1. **Clone the Repository** + + Use the `llamacpp_repo.py` script to download and prepare the `llama.cpp` sources: + + ```bash + python llamacpp_repo.py + ``` + +1. **Build with ROCm Support** + + Then build the project using the `llamacpp_build.py` script: + + ```bash + python llamacpp_build.py + ``` + +> [!NOTE] +> Check scripts for detailed explanation + +The build process automatically detects supported AMD GPU targets and configures CMake. + +## Running / Testing llama.cpp + +After the build completes, the compiled binaries and shared libraries can be found under: + +``` +llama.cpp/build/bin +``` + +You can manually run the generated executables, but the recommended approach is to use the automated test runner. + +> [!TIP] +> If you built `llama.cpp` in a custom directory, you can define an environment variable to make it easier to locate: +> +> ```bash +> export LLAMACPP_BUILD_DIR=/path/to/custom/build +> ``` +> +> The scripts will automatically reference this variable. + +### Test Execution + +To verify functionality, use the **`llamacpp_test.py`** script. +It automatically discovers all test binaries in `build/bin`, runs them, and provides a clean summary. + +Example: + +```bash +# From external-builds/llama.cpp directory +python ./llamacpp_test.py +``` + +You can optionally save logs for later analysis: + +```bash +python ./llamacpp_test.py --save-logging test_results.log +``` + +The test runner: + +- Finds all valid test executables under the `build/bin` directory. +- Skips known failing or unsupported tests. +- Prints a concise summary with success/failure counts. + +> [!NOTE] +> Refer to the source code of [llamacpp_test.py](./llamacpp_test.py) for detailed information on skip rules, supported arguments, and logging options. diff --git a/external-builds/llama.cpp/llamacpp_build.py b/external-builds/llama.cpp/llamacpp_build.py new file mode 100755 index 00000000000..c096582047b --- /dev/null +++ b/external-builds/llama.cpp/llamacpp_build.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python3 + +""" +Automates building llama.cpp with ROCm. + +This script configures and builds the llama.cpp repository for AMD GPUs using +an installed ROCm SDK. It is intended to be used by developers who already +have a prepared source checkout (see llamacpp_repo.py) and want a reproducible +CMake-based build that integrates TheRock/ROCm toolchain conventions + +Assumptions: + +- A working CMake installation is available. Ninja is used as the generator by + default if present (the script passes -G Ninja). +- ROCm SDK is installed and discoverable via rocm_sdk (python -m rocm_sdk). +- The llama.cpp source tree has been prepared by llamacpp_repo.py + or otherwise exists at the path passed to --llama-dir. + +Usage examples: + +# Use defaults (discover ROCm root and targets via rocm_sdk) +python3 ./llamacpp_build.py +# Specify ROCm root and number of jobs +python3 ./llamacpp_build.py --rocm-dir /opt/rocm --jobs 16 + +Arguments: + +--rocm-dir Path to ROCm installation directory (default: rocm_sdk --root) +--llama-dir Path to llama.cpp source directory (default: ./llama.cpp next to script) +--build-dir Path to build directory (default: ./llama.cpp/build next to script) +--cmake-build-type CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel) (default: Release) +--rocm-targets Comma-separated ROCm targets (e.g., gfx1030,gfx1100). If omitted, discovered via rocm_sdk. +--jobs Number of jobs for building (passed to cmake --build -j). "0" lets CMake decide (default: 0). +--llama-curl Enable libcurl support (Boolean flag) +--llama-openssl Enable OpenSSL support (Boolean flag) +--llama-llguidance Enable LLGuidance support (Boolean flag) +--use-ccache Use ccache as the compiler launcher (Boolean flag) +--clean Clean build directory before building (Boolean flag) + +Windows notes +------------- +- The script attempts to locate rc.exe and mt.exe under the Windows Kits + installation (C:\Program Files (x86)\Windows Kits\10x\bin). When found, + it automatically sets -DCMAKE_RC_COMPILER and -DCMAKE_MT for CMake. +- Please note that you need to have environemnt variables set up correctly for + ROCm on Windows, including PATH updates to find ROCm DLLs at runtime. +""" + +import os +import sys +import shlex +import shutil +import argparse +import subprocess +from pathlib import Path +from typing import Tuple, Optional + +script_dir = Path(__file__).resolve().parent + + +def directory_if_exists(dir: Path) -> Path | None: + if dir.exists(): + return dir + else: + return None + + +def capture(args: list[str | Path], cwd: Path) -> str: + args = [str(arg) for arg in args] + print(f"++ Capture [{cwd}]$ {shlex.join(args)}") + try: + return subprocess.check_output( + args, cwd=str(cwd), stderr=subprocess.STDOUT, text=True + ).strip() + except subprocess.CalledProcessError as e: + print(f"Error capturing output: {e}") + print(f"Output from the failed command:\n{e.output}") + return "" + + +def get_rocm_path(path_name: str) -> Path: + return Path( + capture( + [sys.executable, "-m", "rocm_sdk", "path", f"--{path_name}"], cwd=Path.cwd() + ).strip() + ) + + +def get_rocm_sdk_version() -> str: + return capture( + [sys.executable, "-m", "rocm_sdk", "version"], cwd=Path.cwd() + ).strip() + + +def get_rocm_sdk_targets() -> str: + # Run `rocm-sdk targets` to get the default architecture + targets = capture([sys.executable, "-m", "rocm_sdk", "targets"], cwd=Path.cwd()) + if not targets: + print("Warning: rocm-sdk targets returned empty or failed") + return "" + # Convert space-separated targets to comma-separated + return targets.replace(" ", ",") + + +def remove_dir_if_exists(dir: Path): + if dir.exists(): + print(f"++ Removing {dir} ...") + shutil.rmtree(dir) + + +def find_windows_kits_tools() -> Tuple[Optional[Path], Optional[Path]]: + """ + Find rc.exe and mt.exe under Windows Kits 10 bin directory. + Returns (rc_path, mt_path) or (None, None) if not found. + """ + base = Path(r"C:\Program Files (x86)\Windows Kits\10\bin") + # Try direct existence + if not base.exists(): + # fallback to PATH lookup + rc = Path(shutil.which("rc.exe")) if shutil.which("rc.exe") else None + mt = Path(shutil.which("mt.exe")) if shutil.which("mt.exe") else None + return (rc, mt) + + # Collect version dirs that look like "10.0.xxxxx.x" + versions = [ + p for p in base.iterdir() if p.is_dir() and p.name and p.name[0].isdigit() + ] + if versions: + # sort versions lexicographically by dotted parts (highest first) + def ver_key(p: Path): + parts = [] + for part in p.name.split("."): + try: + parts.append(int(part)) + except ValueError: + parts.append(part) + return parts + + versions.sort(key=ver_key, reverse=True) + for v in versions: + x64 = v / "x64" + rc = x64 / "rc.exe" + mt = x64 / "mt.exe" + if rc.exists() and mt.exists(): + return (rc, mt) + + # Last-resort: search recursively and PATH lookup + rc = next(base.rglob("rc.exe"), None) if base.exists() else None + mt = next(base.rglob("mt.exe"), None) if base.exists() else None + if not rc and shutil.which("rc.exe"): + rc = Path(shutil.which("rc.exe")) + if not mt and shutil.which("mt.exe"): + mt = Path(shutil.which("mt.exe")) + return (rc, mt) + + +def setup_environment(rocm_path: Path) -> Path: + """Set required ROCm-related environment variables and print summary. + + Returns: + Path: the computed CMake prefix path (rocm_path / "lib" / "cmake") + """ + if not rocm_path.exists(): + sys.exit(f"Error: ROCM_PATH '{rocm_path}' does not exist.") + + cmake_prefix = directory_if_exists(rocm_path) / "lib" / "cmake" + + rocm_sdk_version = get_rocm_sdk_version() or "unknown" + + # TODO(#1658): when this gets merged into main consider refactoring environment setup + env: dict[str, str] = { + "ROCM_PATH": str(rocm_path), + "ROCM_HOME": str(rocm_path), + "HIP_PATH": str(rocm_path), + "HIP_PATH_70": str(rocm_path), + "LLVM_PATH": str(rocm_path), + # "CMAKE_PREFIX_PATH": str(cmake_prefix), + } + + # Workaround missing device lib bitcode. Same comment as in + # pytorch/build_prod_wheels.py applies here. This is bad/annoying. + # Unfortunately, it has been hardcoded for a long time. So we use a clang + # env var to force a specific device lib path to workaround the hack to + # get pytorch to build. This may or may not only affect the Python wheels + # with their own quirks on directory layout. Obviously, this should be + # completely burned with fire once the root causes are eliminted. + hip_device_lib_path = rocm_path / "lib" / "llvm" / "amdgcn" / "bitcode" + if not hip_device_lib_path.exists(): + print( + "WARNING: Default location of device libs not found. Relying on " + "clang heuristics which are known to be buggy in this configuration" + ) + else: + env["HIP_DEVICE_LIB_PATH"] = str(hip_device_lib_path) + + # find HIP clang + hip_clang = directory_if_exists(rocm_path / "llvm" / "bin" / "clang") + if not hip_clang: + hip_clang = directory_if_exists( + rocm_path / "lib" / "llvm" / "bin" / "clang.exe" + ) + + if not hip_clang: + sys.exit(f"Error: HIP compiler not found at {hip_clang}") + print(f"Using HIPCXX found at: {hip_clang}") + env["HIPCXX"] = str(hip_clang) + + if os.name != "posix": + llvm_dir = rocm_path / "lib" / "llvm" / "bin" + env.update( + { + "HIP_CLANG_PATH": str(llvm_dir.resolve().as_posix()), + "CC": str((llvm_dir / "clang-cl.exe").resolve()), + "CXX": str((llvm_dir / "clang-cl.exe").resolve()), + } + ) + else: + env.update( + { + # Workaround GCC12 compiler flags. + "CXXFLAGS": " -Wno-error=maybe-uninitialized -Wno-error=uninitialized -Wno-error=restrict ", + "CPPFLAGS": " -Wno-error=maybe-uninitialized -Wno-error=uninitialized -Wno-error=restrict ", + } + ) + + # update PATH and environment + # use correct separator for platform + sep = ";" if os.name != "posix" else ":" + os.environ[ + "PATH" + ] = f"{str(rocm_path / 'bin')}{sep}{str(rocm_path / 'lib')}{sep}" + os.environ.get( + "PATH", "" + ) + os.environ.update(env) + + # Print summary information here (moved out of build) + print(f"\nrocm version {rocm_sdk_version}:") + print(f" PYTHON VERSION: {sys.version}") + print(f" CMAKE_PREFIX_PATH = {cmake_prefix}") + print(f" ROCM_HOME = {rocm_path}") + print("Environment variables set for ROCm:") + for k, v in env.items(): + print(f" {k}={v}") + + return cmake_prefix + + +def build(args: argparse.Namespace): + rocm_dir: Path | None = args.rocm_dir + repo_path: Path | None = args.llama_dir + build_dir: Path | None = args.build_dir + + if not repo_path.exists(): + sys.exit( + f"Error: Repository path '{repo_path}' does not exist. Run the llamacpp_repo.py script first." + ) + + # Setup environment and print summary from one place + cmake_prefix = setup_environment(rocm_dir) + + # clean if requested + if args.clean and build_dir.exists(): + print(f"Cleaning build directory ...") + remove_dir_if_exists(build_dir) + + build_dir.mkdir(parents=True, exist_ok=True) + + rocm_targets = args.rocm_targets + if rocm_targets is None: + rocm_targets = get_rocm_sdk_targets() + print(f" Using default target from rocm-sdk targets: {rocm_targets}") + else: + print(f" Using provided rocm targets: {rocm_targets}") + + if not rocm_targets: + raise ValueError( + "No --rocm-targets provided and rocm-sdk targets returned empty. " + "Please specify --rocm-targets (e.g., gfx1100)." + ) + + rocm_targets_list = [t.strip() for t in rocm_targets.split(",")] + rocm_targets_str = ";".join(rocm_targets_list) # CMake expects ;-separated + + cmake_args = [ + "cmake", + "-S", + str(repo_path), + "-B", + str(build_dir), + "-G", + "Ninja", + f"-DGPU_TARGETS={rocm_targets_str}", + # Used to specify AMD GPU targets for older llama.cpp + # repos that use GGML_AMDGPU_TARGETS instead of GPU_TARGETS + f"-DAMDGPU_TARGETS={rocm_targets_str}", + f"-DCMAKE_BUILD_TYPE={args.cmake_build_type}", + f"-DLLAMA_CURL={args.llama_curl}", + f"-DLLAMA_OPENSSL={args.llama_openssl}", + f"-DLLAMA_LLGUIDANCE={args.llama_llguidance}", + "-DHIP_PLATFORM=amd", + "-DGGML_HIP=ON", + # Windows-only RC/MT flags will be appended below when applicable + # Disable rocWMMA fused attention to avoid build issues + # TODO: re-evaluate this when rocWMMA is introduced in TheRock + "-DGGML_HIP_ROCWMMA_FATTN=OFF", + # Fixes the RPATH to find ROCm shared libraries at runtime + # (libhipblas.so.3) TODO: check if needed for Windows + f"-DCMAKE_BUILD_RPATH={str(rocm_dir / 'lib')}", + f"-DCMAKE_INSTALL_RPATH={str(rocm_dir / 'lib')}", + ] + + # Append Windows-specific compiler tools if running on Windows and found + if os.name != "posix": + rc_path, mt_path = find_windows_kits_tools() + if rc_path and mt_path: + print(f"Found Windows Kits tools: rc={rc_path}, mt={mt_path}") + cmake_args.append(f"-DCMAKE_RC_COMPILER={rc_path.as_posix()}") + cmake_args.append(f"-DCMAKE_MT={mt_path.as_posix()}") + else: + print( + "Warning: rc.exe / mt.exe not found in Windows Kits. Skipping -DCMAKE_RC_COMPILER / -DCMAKE_MT flags." + ) + + if args.use_ccache: + print("Using ccache, compilation results will be cached.") + cmake_args.append("-DGGML_CCACHE=ON") + else: + cmake_args.append("-DGGML_CCACHE=OFF") + + print("\nCMake flags set:") + for arg in cmake_args: + if arg.startswith("-D"): + print(f" {arg}") + + print(f"\n++ Running CMake configuration for llama.cpp in {build_dir} ...") + subprocess.run(cmake_args, check=True) + + print(f"\n++ Building llama.cpp in {build_dir} ...") + subprocess.run( + [ + "cmake", + "--build", + str(build_dir), + "--config", + args.cmake_build_type, + "--", + f"-j{args.jobs}", + ], + check=True, + ) + + print(f"\nllama.cpp built successfully in {build_dir}") + + +def main(argv: list[str]): + p = argparse.ArgumentParser(description="Build llama.cpp with ROCm support") + + p.add_argument( + "--rocm-dir", + type=Path, + default=get_rocm_path("root"), + help="Path to ROCm installation directory", + ) + p.add_argument( + "--llama-dir", + type=Path, + default=directory_if_exists(script_dir / "llama.cpp"), + help="Path to llama.cpp source directory", + ) + p.add_argument( + "--build-dir", + type=Path, + default=directory_if_exists(script_dir / "llama.cpp") / "build", + help="Path to build directory", + ) + p.add_argument( + "--cmake-build-type", + type=str, + default="Release", + help="CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)", + ) + p.add_argument( + "--rocm-targets", + type=str, + help="Comma-separated ROCm targets", + ) + p.add_argument( + "--jobs", + type=str, + default="0", + help="Number of jobs for building (passed to cmake --build -j)", + ) + p.add_argument( + "--llama-curl", + action=argparse.BooleanOptionalAction, + default=False, + help="Enable libcurl support", + ) + p.add_argument( + "--llama-openssl", + action=argparse.BooleanOptionalAction, + default=False, + help="Enable OpenSSL support", + ) + p.add_argument( + "--llama-llguidance", + action=argparse.BooleanOptionalAction, + default=False, + help="Enable LLGuidance support", + ) + p.add_argument( + "--use-ccache", + action=argparse.BooleanOptionalAction, + help="Use ccache as the compiler launcher", + ) + p.add_argument( + "--clean", + action=argparse.BooleanOptionalAction, + default=None, + help="Clean build directory before building", + ) + + args = p.parse_args(argv) + + print("\nArguments received:") + for k, v in vars(args).items(): + print(f" {k}: {v}") + print("\nStarting build process...\n") + + build(args) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/external-builds/llama.cpp/llamacpp_repo.py b/external-builds/llama.cpp/llamacpp_repo.py new file mode 100755 index 00000000000..25be42a03bd --- /dev/null +++ b/external-builds/llama.cpp/llamacpp_repo.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +""" +Checks out llama.cpp. + +Automates preparing the llama.cpp repository. While all steps could be performed manually with git, +this script simplifies the process and ensures consistency. + +Primary activities include: + +* Cloning the llama.cpp repository into `THIS_MAIN_REPO_NAME` if it does not + exist locally. +* Fetching the specified `--repo-hashtag` (default: `amd-integration`) from + the remote repository. +* Checking out the exact commit fetched using `FETCH_HEAD`. +* Tagging the checked-out commit as `THEROCK_UPSTREAM_DIFFBASE` for reference. + +Unlike PyTorch, llama.cpp does not currently require: + +* Submodule initialization or updates. +* HIPIFY preprocessing. +* Patch application (the patch-related logic is currently not used). + +This script can be used for: + +* One-shot builds: simply run the script to have the correct repository state. +* Development: you can make changes to the repository locally, and the + `THEROCK_UPSTREAM_DIFFBASE` tag serves as a known reference point for + integration with TheRock workflows. + +Primary usage: + + ./llamacpp_repo.py + +Optional arguments: + +* `--repo` : Local path to clone or use the repository (default: ./llama.cpp) +* `--repo-name` : Subdirectory name for the repo (default: llama.cpp) +* `--repo-hashtag`: Branch, tag, or commit to fetch (default: amd-integration) +* `--gitrepo-origin`: URL of the remote repository (default: https://github.com/ROCm/llama.cpp.git) +* `--depth` : Optional shallow fetch depth +* `--jobs` : Number of parallel fetch jobs (default: 10) + +After running, the repository is ready for local development or building +with TheRock. +""" + +import argparse +from pathlib import Path +import sys + +import repo_management + +THIS_MAIN_REPO_NAME = "llama.cpp" +THIS_DIR = Path(__file__).resolve().parent +DEFAULT_REPO_HASHTAG = "master" +GIT_REPO_ORIGIN = "https://github.com/ggml-org/llama.cpp.git" +FETCH_JOBS = 10 + + +def main(): + # Create a simple argparse parser for optional arguments + p = argparse.ArgumentParser("llamacpp_repo.py") + p.add_argument( + "--repo", + type=Path, + default=THIS_DIR / THIS_MAIN_REPO_NAME, + help="Git repository path", + ) + p.add_argument( + "--repo-name", + default=THIS_MAIN_REPO_NAME, + help="Subdirectory name in which to checkout repo", + ) + p.add_argument( + "--repo-hashtag", + default=DEFAULT_REPO_HASHTAG, + help="Git repository ref/tag to checkout", + ) + p.add_argument( + "--gitrepo-origin", + default=GIT_REPO_ORIGIN, + help="Git repository URL", + ) + p.add_argument("--depth", type=int, default=None, help="Fetch depth") + p.add_argument("--jobs", type=int, default=FETCH_JOBS, help="Number of fetch jobs") + + args = p.parse_args() + repo_management.do_checkout(args) + + +if __name__ == "__main__": + main() diff --git a/external-builds/llama.cpp/llamacpp_test.py b/external-builds/llama.cpp/llamacpp_test.py new file mode 100755 index 00000000000..4b64a0dc44a --- /dev/null +++ b/external-builds/llama.cpp/llamacpp_test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +""" +Automates the execution of llama.cpp integrated tests. + +It supports both full and smoke test modes, filters out tests that require +unavailable resources or are known to fail, and logs results to the console +or an optional log file. + +Test binaries are discovered dynamically from the build directory. +""" + +import os +import sys +import logging +import argparse +import subprocess +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +LLAMA_BIN_DIR = Path( + os.getenv("LLAMACPP_BUILD_DIR", SCRIPT_DIR / "llama.cpp" / "build" / "bin") +) + +TESTS_TO_IGNORE = [ + # TODO: these tests require additional files/models. Disable for now. + "test-tokenizer-1-spm", + "test-gbnf-validator", + "test-json-schema-to-grammar", + "test-quantize-stats", + "test-tokenizer-0", + "test-chat", + "test-tokenizer-1-bpe", + "test-thread-safety", + # TODO: fails due to FPE for Flash Attention ops + # on GFX1030 GPU (AMD RX 6700 XT). + # "test-backend-ops", +] + +SMOKE_TESTS = [ + "test-llama-grammar", + "test-arg-parser", + "test-log", + "test-c", + "test-alloc", + "test-gguf", +] + + +def setup_logging(log_file=None): + """Configure logging to console and optionally to a file.""" + handlers = [logging.StreamHandler()] + if log_file: + file_handler = logging.FileHandler(log_file, mode="w") + handlers.append(file_handler) + + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=handlers, + ) + + +def main(argv: list[str]): + p = argparse.ArgumentParser(description="Run llama.cpp tests.") + p.add_argument( + "--save-logging", metavar="FILE", help="Save logs to a specified file" + ) + args = p.parse_args(argv) + + setup_logging(args.save_logging) + + test_type = os.getenv("TEST_TYPE", "full") + pattern = "test-*.exe" if os.name != "posix" else "test-*" + all_tests = [ + p for p in LLAMA_BIN_DIR.glob(pattern) if p.stem not in TESTS_TO_IGNORE + ] + + if test_type == "smoke": + all_tests = [t for t in all_tests if t.name in SMOKE_TESTS] + if not all_tests: + logging.warning("No matching tests found!") + return + + logging.info(f"Running {len(all_tests)} tests ({test_type} mode)...") + + for test in all_tests: + logging.info(f"++ Running {test.name}") + try: + subprocess.run([str(test)], cwd=LLAMA_BIN_DIR, check=True) + logging.info(f"[OK] {test.name} ran successfully") + except subprocess.CalledProcessError as e: + logging.error(f"[FAIL] {test.name} exited with code {e.returncode}") + raise + + logging.info("All selected tests finished successfully.") + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/external-builds/llama.cpp/repo_management.py b/external-builds/llama.cpp/repo_management.py new file mode 100644 index 00000000000..b2ce0a0d667 --- /dev/null +++ b/external-builds/llama.cpp/repo_management.py @@ -0,0 +1,42 @@ +import argparse +from pathlib import Path +import shlex +import subprocess + +TAG_UPSTREAM_DIFFBASE = "THEROCK_UPSTREAM_DIFFBASE" + + +def exec(args: list[str | Path], cwd: Path, *, stdout_devnull: bool = False): + args = [str(arg) for arg in args] + print(f"++ Exec [{cwd}]$ {shlex.join(args)}") + subprocess.check_call( + args, + cwd=str(cwd), + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL if stdout_devnull else None, + ) + + +def do_checkout(args: argparse.Namespace): + repo_dir: Path = args.repo + check_git_dir = repo_dir / ".git" + + if check_git_dir.exists(): + print(f"Not cloning repository ({check_git_dir} exists)") + exec(["git", "remote", "set-url", "origin", args.gitrepo_origin], cwd=repo_dir) + else: + print(f"Cloning repository at {args.repo_hashtag}") + repo_dir.mkdir(parents=True, exist_ok=True) + exec(["git", "init", "--initial-branch=main"], cwd=repo_dir) + exec(["git", "config", "advice.detachedHead", "false"], cwd=repo_dir) + exec(["git", "remote", "add", "origin", args.gitrepo_origin], cwd=repo_dir) + + fetch_args = [] + if args.depth is not None: + fetch_args.extend(["--depth", str(args.depth)]) + if args.jobs: + fetch_args.extend(["-j", str(args.jobs)]) + + exec(["git", "fetch"] + fetch_args + ["origin", args.repo_hashtag], cwd=repo_dir) + exec(["git", "checkout", "FETCH_HEAD"], cwd=repo_dir) + exec(["git", "tag", "-f", TAG_UPSTREAM_DIFFBASE, "--no-sign"], cwd=repo_dir)