Team Ai
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 21d agoView on Hugging Face
0likes1.2kdownloads
build.py275 linesDownload Raw Back to snapdragon
1#!/usr/bin/env python32#3# Build llama.cpp for Snapdragon (via Docker or natively) and push to device.4#5 6import sys7import os8import argparse9import subprocess10import platform11import shutil12import logging13 14from sdk import validate_windows_sdks15 16logger = logging.getLogger("build")17 18 19def parse_target(target_str):20    if not target_str:21        return None, None22    if target_str.startswith("adb") or target_str.startswith("android"):23        parts = target_str.split(":", 1)24        serial = parts[1] if len(parts) > 1 else None25        return "android", serial26    elif target_str.startswith("lnx") or target_str.startswith("linux") or target_str.startswith("ubuntu"):27        parts = target_str.split(":", 1)28        host = parts[1] if len(parts) > 1 else None29        return "linux", host30    elif target_str in ("wos", "windows"):31        return "windows", None32    else:33        return None, None34 35 36def get_uid_gid():37    if platform.system() != "Windows":38        return [f"{os.getuid()}:{os.getgid()}"]39    return []40 41 42def main():43    logging.basicConfig(level=logging.INFO, format='%(message)s')44    parser = argparse.ArgumentParser(45        description="Build llama.cpp for Snapdragon using cross-compilation docker containers or natively."46    )47    parser.add_argument("--target", default="android", help="Compilation target and deployment definition (e.g. android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, windows/wos) (default: android)")48    parser.add_argument("--build-dir", help="Build directory name (defaults to build-TARGET[-dbg], e.g. build-android)")49    parser.add_argument("--install-dir", help="Install directory name (defaults to pkg-TARGET[-dbg], e.g. pkg-android)")50    parser.add_argument("--jobs", "-j", type=int, help="Number of build jobs (defaults to CPU thread count)")51    parser.add_argument("--no-docker", action="store_true", help="Build natively on the host instead of in a docker container")52    parser.add_argument("--preset", help="Override the CMake preset to use")53    parser.add_argument("--debug", action="store_true", help="Build in debug mode (uses -debug presets instead of -release)")54 55    # Push options56    parser.add_argument("--push", action="store_true", help="Push built package to the target device via ADB or SSH/SCP")57    parser.add_argument("--target-dir", help="Target directory on the device (default: /data/local/tmp/llama.cpp for Android, ~/llama.cpp for Linux)")58 59    # Toolchain options60    parser.add_argument("--toolchain-version", default="v0.7", help="Docker toolchain image version/tag (default: v0.7)")61    parser.add_argument("--toolchain-url", default="ghcr.io/snapdragon-toolchain", help="Docker toolchain registry URL/namespace (default: ghcr.io/snapdragon-toolchain)")62 63    args = parser.parse_args()64 65    target_type, target_val = parse_target(args.target)66    if not target_type:67        logger.error(f"Error: Invalid target format '{args.target}'. Must be android[:serial]/adb[:serial], linux:[user@]host/lnx:[user@]host/ubuntu:[user@]host, or windows/wos.")68        sys.exit(1)69 70    if target_type == "windows":71        logger.info("Windows target selected. Forcing native compilation...")72        args.no_docker = True73        if platform.system() != "Windows":74            logger.warning("Warning: Windows compilation is intended to run on Windows arm64 hosts.")75        validate_windows_sdks()76 77    # Determine preset and check if it's debug78    preset = args.preset79    if preset:80        is_debug = args.debug or ("debug" in preset.lower())81    else:82        is_debug = args.debug83        config_type = "debug" if is_debug else "release"84        if args.no_docker:85            if target_type == "windows" or platform.system() == "Windows":86                preset = f"arm64-windows-snapdragon-{config_type}"87            elif target_type == "linux":88                preset = f"arm64-linux-snapdragon-{config_type}"89            else:90                preset = f"arm64-android-snapdragon-{config_type}"91        else:92            preset = f"arm64-linux-snapdragon-{config_type}" if target_type == "linux" else f"arm64-android-snapdragon-{config_type}"93 94    target_prefix = args.target.split(":", 1)[0]95    suffix = "-dbg" if is_debug else ""96 97    build_dir = args.build_dir98    if not build_dir:99        build_dir = f"build-{target_prefix}{suffix}"100 101    install_dir = args.install_dir102    if not install_dir:103        install_dir = f"pkg-{target_prefix}{suffix}"104 105    repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))106 107    # Ensure CMakeUserPresets.json is in the workspace root, update if docs version is newer108    preset_src = os.path.join(repo_root, "docs", "backend", "snapdragon", "CMakeUserPresets.json")109    preset_dst = os.path.join(repo_root, "CMakeUserPresets.json")110    if os.path.exists(preset_src):111        should_copy = False112        if not os.path.exists(preset_dst):113            should_copy = True114        else:115            # Check modification times116            src_mtime = os.path.getmtime(preset_src)117            dst_mtime = os.path.getmtime(preset_dst)118            if src_mtime > dst_mtime:119                preset_bak = preset_dst + ".bak"120                logger.info(f"Docs CMakeUserPresets.json is newer. Backing up existing {preset_dst} to {preset_bak}")121                shutil.copy2(preset_dst, preset_bak)122                should_copy = True123 124        if should_copy:125            logger.info(f"Copying CMakeUserPresets.json from {preset_src} to {preset_dst}")126            shutil.copy2(preset_src, preset_dst)127    else:128        logger.warning("Warning: CMakeUserPresets.json not found in docs/backend/snapdragon/.")129 130    jobs = args.jobs if args.jobs else os.cpu_count() or 4131 132    if args.no_docker:133        # Native/local host build134        logger.info("Running native/local CMake build...")135        install_prefix = os.path.join(repo_root, install_dir, "llama.cpp")136 137        # Configure138        configure_cmd = ["cmake", f"--preset={preset}", "-B", build_dir]139        logger.info(f"+ {' '.join(configure_cmd)}")140        res = subprocess.run(configure_cmd, cwd=repo_root)141        if res.returncode != 0:142            logger.error("CMake configuration failed.")143            sys.exit(res.returncode)144 145        # Build146        build_cmd = ["cmake", "--build", build_dir, "-j", str(jobs)]147        logger.info(f"+ {' '.join(build_cmd)}")148        res = subprocess.run(build_cmd, cwd=repo_root)149        if res.returncode != 0:150            logger.error("CMake build failed.")151            sys.exit(res.returncode)152 153        # Install154        install_cmd = ["cmake", "--install", build_dir, "--prefix", install_prefix]155        logger.info(f"+ {' '.join(install_cmd)}")156        res = subprocess.run(install_cmd, cwd=repo_root)157        if res.returncode != 0:158            logger.error("CMake install failed.")159            sys.exit(res.returncode)160    else:161        # Docker-based build162        logger.info("Running Docker-based cross-compilation build...")163        image_name = "arm64-linux" if target_type == "linux" else "arm64-android"164        image = f"{args.toolchain_url}/{image_name}:{args.toolchain_version}"165 166        install_prefix_container = f"/workspace/{install_dir}/llama.cpp"167 168        build_sh_cmd = (169            f"cmake --preset {preset} -B /workspace/{build_dir} && "170            f"cmake --build /workspace/{build_dir} -j {jobs} && "171            f"cmake --install /workspace/{build_dir} --prefix {install_prefix_container}"172        )173 174        docker_cmd = [175            "docker", "run", "--rm",176            "--volume", f"{repo_root}:/workspace",177            "--workdir", "/workspace",178            "--platform", "linux/amd64"179        ]180        uid_gid = get_uid_gid()181        if uid_gid:182            docker_cmd += ["-u", uid_gid[0]]183 184        docker_cmd += [image, "bash", "-c", build_sh_cmd]185 186        logger.info(f"+ {' '.join(docker_cmd)}")187        res = subprocess.run(docker_cmd, cwd=repo_root)188        if res.returncode != 0:189            logger.error("Docker-based build failed.")190            sys.exit(res.returncode)191 192    logger.info("\nBuild and installation completed successfully!")193 194    # Push/deploy if requested195    if args.push:196        src_path = os.path.join(repo_root, install_dir, "llama.cpp")197        if not os.path.exists(src_path):198            logger.error(f"Error: installation directory {src_path} does not exist. Cannot deploy.")199            sys.exit(1)200 201        # Resolve target directory on device202        target_dir = args.target_dir203        if not target_dir:204            target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp"205        target_dir = target_dir.rstrip("/")206 207        sub_items = [item for item in os.listdir(src_path) if not item.startswith(".")]208 209        if target_type == "android":210            logger.info("\nPushing built artifacts to Android device via ADB...")211            adb_cmd = ["adb"]212            if target_val: # serial213                adb_cmd += ["-s", target_val]214 215            # Clean stale package files on device216            if sub_items:217                clean_paths = " ".join(f"{target_dir}/{item}" for item in sub_items)218                clean_cmd = adb_cmd + ["shell", f"rm -rf {clean_paths}"]219                logger.info(f"+ {' '.join(clean_cmd)}")220                subprocess.run(clean_cmd)221 222            # Android destination directory is target_dir223            push_cmd = adb_cmd + ["push", os.path.join(src_path, "."), target_dir]224            logger.info(f"+ {' '.join(push_cmd)}")225            res = subprocess.run(push_cmd)226            if res.returncode != 0:227                logger.error("ADB push failed.")228                sys.exit(res.returncode)229 230            chmod_cmd = adb_cmd + ["shell", f"chmod -R 755 {target_dir}/bin 2>/dev/null || true"]231            logger.info(f"+ {' '.join(chmod_cmd)}")232            subprocess.run(chmod_cmd)233            logger.info("ADB push completed successfully!")234 235        elif target_type == "linux":236            ssh_host = target_val237            if not ssh_host:238                logger.error("Error: SSH host not specified in target (e.g. use linux:user@host, lnx:user@host, or ubuntu:user@host). Cannot deploy.")239                sys.exit(1)240            logger.info(f"\nDeploying built artifacts to Linux device {ssh_host} via SSH/SCP...")241 242            # Clean stale package files on remote host243            if sub_items:244                clean_paths = " ".join(f"{target_dir}/{item}" for item in sub_items)245                clean_cmd = ["ssh", ssh_host, f"rm -rf {clean_paths}"]246                logger.info(f"+ {' '.join(clean_cmd)}")247                subprocess.run(clean_cmd)248 249            # Deploy to target_dir250            deploy_cmd = ["scp", "-r", os.path.join(src_path, "."), f"{ssh_host}:{target_dir}"]251            logger.info(f"+ {' '.join(deploy_cmd)}")252            res = subprocess.run(deploy_cmd)253            if res.returncode != 0:254                logger.error("SSH/SCP deploy failed.")255                sys.exit(res.returncode)256 257            chmod_cmd = ["ssh", ssh_host, f"chmod -R 755 {target_dir}/bin 2>/dev/null || true"]258            logger.info(f"+ {' '.join(chmod_cmd)}")259            subprocess.run(chmod_cmd)260            logger.info("SSH/SCP deploy completed successfully!")261 262        elif target_type == "windows":263            logger.info("\nPush for Windows on Snapdragon (windows) target is currently a stub.")264 265 266if __name__ == "__main__":267    try:268        main()269    except KeyboardInterrupt:270        logger.info("\nInterrupted by user.")271        sys.exit(130)272    except RuntimeError as err:273        logger.error("Error: %s", err)274        sys.exit(1)275