char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
d30500b83
char/aliceai-t5-moe
1#!/usr/bin/env python3 2# 3# Build llama.cpp for Snapdragon (via Docker or natively) and push to device. 4# 5 6import sys 7import os 8import argparse 9import subprocess 10import platform 11import shutil 12import logging 13 14from sdk import validate_windows_sdks 15 16logger = logging .getLogger ("build" ) 17 18 19def parse_target (target_str ): 20if not target_str : 21return None ,None 22if target_str .startswith ("adb" )or target_str .startswith ("android" ): 23parts = target_str .split (":" ,1 ) 24serial = parts [1 ]if len (parts )> 1 else None 25return "android" ,serial 26elif target_str .startswith ("lnx" )or target_str .startswith ("linux" )or target_str .startswith ("ubuntu" ): 27parts = target_str .split (":" ,1 ) 28host = parts [1 ]if len (parts )> 1 else None 29return "linux" ,host 30elif target_str in ("wos" ,"windows" ): 31return "windows" ,None 32else : 33return None ,None 34 35 36def get_uid_gid (): 37if platform .system ()!= "Windows" : 38return [f" { os . getuid () } : { os . getgid () } " ] 39return [] 40 41 42def main (): 43logging .basicConfig (level = logging .INFO ,format = '%(message)s' ) 44parser = argparse .ArgumentParser ( 45description = "Build llama.cpp for Snapdragon using cross-compilation docker containers or natively." 46 ) 47parser .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)" ) 48parser .add_argument ("--build-dir" ,help = "Build directory name (defaults to build-TARGET[-dbg], e.g. build-android)" ) 49parser .add_argument ("--install-dir" ,help = "Install directory name (defaults to pkg-TARGET[-dbg], e.g. pkg-android)" ) 50parser .add_argument ("--jobs" ,"-j" ,type = int ,help = "Number of build jobs (defaults to CPU thread count)" ) 51parser .add_argument ("--no-docker" ,action = "store_true" ,help = "Build natively on the host instead of in a docker container" ) 52parser .add_argument ("--preset" ,help = "Override the CMake preset to use" ) 53parser .add_argument ("--debug" ,action = "store_true" ,help = "Build in debug mode (uses -debug presets instead of -release)" ) 54 55# Push options 56parser .add_argument ("--push" ,action = "store_true" ,help = "Push built package to the target device via ADB or SSH/SCP" ) 57parser .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 options 60parser .add_argument ("--toolchain-version" ,default = "v0.7" ,help = "Docker toolchain image version/tag (default: v0.7)" ) 61parser .add_argument ("--toolchain-url" ,default = "ghcr.io/snapdragon-toolchain" ,help = "Docker toolchain registry URL/namespace (default: ghcr.io/snapdragon-toolchain)" ) 62 63args = parser .parse_args () 64 65target_type ,target_val = parse_target (args .target ) 66if not target_type : 67logger .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." ) 68sys .exit (1 ) 69 70if target_type == "windows" : 71logger .info ("Windows target selected. Forcing native compilation..." ) 72args .no_docker = True 73if platform .system ()!= "Windows" : 74logger .warning ("Warning: Windows compilation is intended to run on Windows arm64 hosts." ) 75validate_windows_sdks () 76 77# Determine preset and check if it's debug 78preset = args .preset 79if preset : 80is_debug = args .debug or ("debug" in preset .lower ()) 81else : 82is_debug = args .debug 83config_type = "debug" if is_debug else "release" 84if args .no_docker : 85if target_type == "windows" or platform .system ()== "Windows" : 86preset = f"arm64-windows-snapdragon- { config_type } " 87elif target_type == "linux" : 88preset = f"arm64-linux-snapdragon- { config_type } " 89else : 90preset = f"arm64-android-snapdragon- { config_type } " 91else : 92preset = f"arm64-linux-snapdragon- { config_type } " if target_type == "linux" else f"arm64-android-snapdragon- { config_type } " 93 94target_prefix = args .target .split (":" ,1 )[0 ] 95suffix = "-dbg" if is_debug else "" 96 97build_dir = args .build_dir 98if not build_dir : 99build_dir = f"build- { target_prefix } { suffix } " 100 101install_dir = args .install_dir 102if not install_dir : 103install_dir = f"pkg- { target_prefix } { suffix } " 104 105repo_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 newer 108preset_src = os .path .join (repo_root ,"docs" ,"backend" ,"snapdragon" ,"CMakeUserPresets.json" ) 109preset_dst = os .path .join (repo_root ,"CMakeUserPresets.json" ) 110if os .path .exists (preset_src ): 111should_copy = False 112if not os .path .exists (preset_dst ): 113should_copy = True 114else : 115# Check modification times 116src_mtime = os .path .getmtime (preset_src ) 117dst_mtime = os .path .getmtime (preset_dst ) 118if src_mtime > dst_mtime : 119preset_bak = preset_dst + ".bak" 120logger .info (f"Docs CMakeUserPresets.json is newer. Backing up existing { preset_dst } to { preset_bak } " ) 121shutil .copy2 (preset_dst ,preset_bak ) 122should_copy = True 123 124if should_copy : 125logger .info (f"Copying CMakeUserPresets.json from { preset_src } to { preset_dst } " ) 126shutil .copy2 (preset_src ,preset_dst ) 127else : 128logger .warning ("Warning: CMakeUserPresets.json not found in docs/backend/snapdragon/." ) 129 130jobs = args .jobs if args .jobs else os .cpu_count ()or 4 131 132if args .no_docker : 133# Native/local host build 134logger .info ("Running native/local CMake build..." ) 135install_prefix = os .path .join (repo_root ,install_dir ,"llama.cpp" ) 136 137# Configure 138configure_cmd = ["cmake" ,f"--preset= { preset } " ,"-B" ,build_dir ] 139logger .info (f"+ { ' ' . join ( configure_cmd ) } " ) 140res = subprocess .run (configure_cmd ,cwd = repo_root ) 141if res .returncode != 0 : 142logger .error ("CMake configuration failed." ) 143sys .exit (res .returncode ) 144 145# Build 146build_cmd = ["cmake" ,"--build" ,build_dir ,"-j" ,str (jobs )] 147logger .info (f"+ { ' ' . join ( build_cmd ) } " ) 148res = subprocess .run (build_cmd ,cwd = repo_root ) 149if res .returncode != 0 : 150logger .error ("CMake build failed." ) 151sys .exit (res .returncode ) 152 153# Install 154install_cmd = ["cmake" ,"--install" ,build_dir ,"--prefix" ,install_prefix ] 155logger .info (f"+ { ' ' . join ( install_cmd ) } " ) 156res = subprocess .run (install_cmd ,cwd = repo_root ) 157if res .returncode != 0 : 158logger .error ("CMake install failed." ) 159sys .exit (res .returncode ) 160else : 161# Docker-based build 162logger .info ("Running Docker-based cross-compilation build..." ) 163image_name = "arm64-linux" if target_type == "linux" else "arm64-android" 164image = f" { args . toolchain_url } / { image_name } : { args . toolchain_version } " 165 166install_prefix_container = f"/workspace/ { install_dir } /llama.cpp" 167 168build_sh_cmd = ( 169f"cmake --preset { preset } -B /workspace/ { build_dir } && " 170f"cmake --build /workspace/ { build_dir } -j { jobs } && " 171f"cmake --install /workspace/ { build_dir } --prefix { install_prefix_container } " 172 ) 173 174docker_cmd = [ 175"docker" ,"run" ,"--rm" , 176"--volume" ,f" { repo_root } :/workspace" , 177"--workdir" ,"/workspace" , 178"--platform" ,"linux/amd64" 179 ] 180uid_gid = get_uid_gid () 181if uid_gid : 182docker_cmd += ["-u" ,uid_gid [0 ]] 183 184docker_cmd += [image ,"bash" ,"-c" ,build_sh_cmd ] 185 186logger .info (f"+ { ' ' . join ( docker_cmd ) } " ) 187res = subprocess .run (docker_cmd ,cwd = repo_root ) 188if res .returncode != 0 : 189logger .error ("Docker-based build failed." ) 190sys .exit (res .returncode ) 191 192logger .info ("\nBuild and installation completed successfully!" ) 193 194# Push/deploy if requested 195if args .push : 196src_path = os .path .join (repo_root ,install_dir ,"llama.cpp" ) 197if not os .path .exists (src_path ): 198logger .error (f"Error: installation directory { src_path } does not exist. Cannot deploy." ) 199sys .exit (1 ) 200 201# Resolve target directory on device 202target_dir = args .target_dir 203if not target_dir : 204target_dir = "/data/local/tmp/llama.cpp" if target_type == "android" else "~/llama.cpp" 205target_dir = target_dir .rstrip ("/" ) 206 207sub_items = [item for item in os .listdir (src_path )if not item .startswith ("." )] 208 209if target_type == "android" : 210logger .info ("\nPushing built artifacts to Android device via ADB..." ) 211adb_cmd = ["adb" ] 212if target_val :# serial 213adb_cmd += ["-s" ,target_val ] 214 215# Clean stale package files on device 216if sub_items : 217clean_paths = " " .join (f" { target_dir } / { item } " for item in sub_items ) 218clean_cmd = adb_cmd + ["shell" ,f"rm -rf { clean_paths } " ] 219logger .info (f"+ { ' ' . join ( clean_cmd ) } " ) 220subprocess .run (clean_cmd ) 221 222# Android destination directory is target_dir 223push_cmd = adb_cmd + ["push" ,os .path .join (src_path ,"." ),target_dir ] 224logger .info (f"+ { ' ' . join ( push_cmd ) } " ) 225res = subprocess .run (push_cmd ) 226if res .returncode != 0 : 227logger .error ("ADB push failed." ) 228sys .exit (res .returncode ) 229 230chmod_cmd = adb_cmd + ["shell" ,f"chmod -R 755 { target_dir } /bin 2>/dev/null || true" ] 231logger .info (f"+ { ' ' . join ( chmod_cmd ) } " ) 232subprocess .run (chmod_cmd ) 233logger .info ("ADB push completed successfully!" ) 234 235elif target_type == "linux" : 236ssh_host = target_val 237if not ssh_host : 238logger .error ("Error: SSH host not specified in target (e.g. use linux:user@host, lnx:user@host, or ubuntu:user@host). Cannot deploy." ) 239sys .exit (1 ) 240logger .info (f"\nDeploying built artifacts to Linux device { ssh_host } via SSH/SCP..." ) 241 242# Clean stale package files on remote host 243if sub_items : 244clean_paths = " " .join (f" { target_dir } / { item } " for item in sub_items ) 245clean_cmd = ["ssh" ,ssh_host ,f"rm -rf { clean_paths } " ] 246logger .info (f"+ { ' ' . join ( clean_cmd ) } " ) 247subprocess .run (clean_cmd ) 248 249# Deploy to target_dir 250deploy_cmd = ["scp" ,"-r" ,os .path .join (src_path ,"." ),f" { ssh_host } : { target_dir } " ] 251logger .info (f"+ { ' ' . join ( deploy_cmd ) } " ) 252res = subprocess .run (deploy_cmd ) 253if res .returncode != 0 : 254logger .error ("SSH/SCP deploy failed." ) 255sys .exit (res .returncode ) 256 257chmod_cmd = ["ssh" ,ssh_host ,f"chmod -R 755 { target_dir } /bin 2>/dev/null || true" ] 258logger .info (f"+ { ' ' . join ( chmod_cmd ) } " ) 259subprocess .run (chmod_cmd ) 260logger .info ("SSH/SCP deploy completed successfully!" ) 261 262elif target_type == "windows" : 263logger .info ("\nPush for Windows on Snapdragon (windows) target is currently a stub." ) 264 265 266if __name__ == "__main__" : 267try : 268main () 269except KeyboardInterrupt : 270logger .info ("\nInterrupted by user." ) 271sys .exit (130 ) 272except RuntimeError as err : 273logger .error ("Error: %s" ,err ) 274sys .exit (1 )