char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
d7bd3bfca
char/aliceai-t5-moe
1#!/usr/bin/env python3 2# 3# Install Windows on Snapdragon SDKs for llama.cpp. 4# 5 6import sys 7import os 8import argparse 9import shutil 10import logging 11import json 12import hashlib 13import tarfile 14import tempfile 15from pathlib import Path 16from urllib .error import HTTPError ,URLError 17from urllib .request import Request ,urlopen 18 19from sdk import SDK_CONFIGS ,get_hexagon_tools_dir ,is_valid_sdk 20 21 22logger = logging .getLogger ("setup_sdk" ) 23 24DEFAULT_SDK_BASE_DIR = r"C:\Qualcomm" 25 26 27def get_sdk_releases (config ): 28request = Request ( 29f"https://api.github.com/repos/ { config [ 'repo' ] } /releases?per_page=100" , 30headers = {"Accept" :"application/vnd.github+json" ,"User-Agent" :"llama.cpp" }, 31 ) 32try : 33with urlopen (request ,timeout = 30 )as response : 34releases = json .load (response ) 35except (HTTPError ,URLError ,TimeoutError )as err : 36raise RuntimeError (f"Cannot query { config [ 'name' ] } releases: { err } " )from err 37 38result = [] 39for release in releases : 40if release ["draft" ]or release ["prerelease" ]: 41continue 42version = release ["tag_name" ].removeprefix ("v" ) 43archive_name = f" { config [ 'archive_prefix' ] } { version } -arm64-wos.tar.xz" 44for asset in release ["assets" ]: 45if asset ["name" ]!= archive_name : 46continue 47result .append ({ 48"version" :version , 49"name" :asset ["name" ], 50"url" :asset ["browser_download_url" ], 51"sha256" : (asset .get ("digest" )or "" ).removeprefix ("sha256:" ), 52 }) 53return result 54 55 56def list_sdk_releases (): 57for config in SDK_CONFIGS : 58logger .info ("%s:" ,config ["name" ]) 59releases = get_sdk_releases (config ) 60if not releases : 61logger .info (" no Windows on Snapdragon releases found" ) 62continue 63for release in releases : 64logger .info (" %s: %s" ,release ["version" ],release ["name" ]) 65 66 67def get_sdk_release (config ,version ): 68version = version or config ["default_version" ] 69version = version .removeprefix ("v" ) 70for release in get_sdk_releases (config ): 71if release ["version" ]== version : 72if not release ["sha256" ]: 73raise RuntimeError (f" { config [ 'name' ] } { version } does not provide a SHA-256 digest" ) 74return release 75raise RuntimeError ( 76f"No Windows on Snapdragon release for { config [ 'name' ] } { version } . " 77"Run scripts/snapdragon/setup-sdk.py --list-sdk-releases to see available versions." 78 ) 79 80 81def sha256sum (path ): 82digest = hashlib .sha256 () 83with open (path ,"rb" )as file : 84for chunk in iter (lambda :file .read (1024 * 1024 ),b"" ): 85digest .update (chunk ) 86return digest .hexdigest () 87 88 89def download_sdk (release ,archive ): 90while True : 91if archive .exists ()and sha256sum (archive )== release ["sha256" ]: 92logger .info ("Using existing archive %s" ,archive ) 93return 94 95offset = archive .stat ().st_size if archive .exists ()else 0 96headers = {"User-Agent" :"llama.cpp" } 97if offset : 98headers ["Range" ]= f"bytes= { offset } -" 99logger .info ("Resuming download of %s at %d MiB" ,release ["name" ],offset // (1024 * 1024 )) 100else : 101logger .info ("Downloading %s" ,release ["name" ]) 102 103try : 104with urlopen (Request (release ["url" ],headers = headers ),timeout = 30 )as response : 105mode = "ab" if offset and response .status == 206 else "wb" 106with open (archive ,mode )as file : 107shutil .copyfileobj (response ,file ) 108except HTTPError as err : 109if err .code != 416 : 110raise RuntimeError (f"Cannot download { release [ 'name' ] } : { err } " )from err 111archive .unlink (missing_ok = True ) 112continue 113except (URLError ,TimeoutError )as err : 114raise RuntimeError (f"Cannot download { release [ 'name' ] } : { err } " )from err 115 116if sha256sum (archive )== release ["sha256" ]: 117return 118raise RuntimeError (f"SHA-256 mismatch for { archive } . Re-run the command to resume the download." ) 119 120 121def extract_sdk (config ,archive ,target_dir ): 122if not hasattr (tarfile ,"data_filter" ): 123raise RuntimeError ("SDK extraction requires Python 3.10.12 or later" ) 124 125with tempfile .TemporaryDirectory (prefix = f". { target_dir . name } .tmp-" ,dir = target_dir .parent )as staging_path : 126staging_dir = Path (staging_path ) 127with tarfile .open (archive ,"r:xz" )as tar : 128tar .extractall (staging_dir ,filter = tarfile .data_filter ) 129 130candidates = [staging_dir ]+ [path for path in staging_dir .iterdir ()if path .is_dir ()] 131extracted_dirs = [path for path in candidates if is_valid_sdk (config ,path )] 132if len (extracted_dirs )!= 1 : 133raise RuntimeError (f" { config [ 'name' ] } archive does not contain the expected files" ) 134extracted_dir = extracted_dirs [0 ] 135 136backup_dir = None 137if target_dir .exists (): 138backup_dir = target_dir .parent / f". { target_dir . name } .backup" 139if backup_dir .exists (): 140raise RuntimeError (f"Cannot replace { target_dir } : backup directory { backup_dir } already exists" ) 141target_dir .replace (backup_dir ) 142try : 143extracted_dir .replace (target_dir ) 144except Exception : 145if backup_dir : 146backup_dir .replace (target_dir ) 147raise 148if backup_dir : 149shutil .rmtree (backup_dir ) 150 151 152def install_sdk (config ,version ,base_dir ,force ): 153version = (version or config ["default_version" ]).removeprefix ("v" ) 154target_dir = base_dir / config ["parent_dir" ]/ version 155if is_valid_sdk (config ,target_dir )and not force : 156logger .info ("Using existing %s at %s" ,config ["name" ],target_dir ) 157return target_dir 158 159release = get_sdk_release (config ,version ) 160target_dir .parent .mkdir (parents = True ,exist_ok = True ) 161archive = target_dir .parent / release ["name" ] 162download_sdk (release ,archive ) 163logger .info ("Extracting %s to %s" ,config ["name" ],target_dir ) 164extract_sdk (config ,archive ,target_dir ) 165archive .unlink (missing_ok = True ) 166return target_dir 167 168 169def set_user_environment (values ): 170if os .name != "nt" : 171raise RuntimeError ("SDK setup must run on Windows" ) 172 173import winreg 174 175with winreg .CreateKey (winreg .HKEY_CURRENT_USER ,"Environment" )as key : 176for name ,value in values .items (): 177winreg .SetValueEx (key ,name ,0 ,winreg .REG_SZ ,str (value )) 178os .environ [name ]= str (value ) 179 180import ctypes 181 182result = ctypes .c_ulong () 183ctypes .windll .user32 .SendMessageTimeoutW (0xffff ,0x001a ,0 ,"Environment" ,0x0002 ,5000 ,ctypes .byref (result )) 184 185 186def setup_sdks (args ): 187base_dir = Path (args .sdk_base_dir ).expanduser ().resolve () 188hexagon_config ,opencl_config = SDK_CONFIGS 189environment = {} 190 191if args .hexagon is not None : 192hexagon_dir = install_sdk (hexagon_config ,args .hexagon ,base_dir ,args .force ) 193environment ["HEXAGON_SDK_ROOT" ]= hexagon_dir 194environment ["HEXAGON_TOOLS_ROOT" ]= get_hexagon_tools_dir (hexagon_dir ) 195if args .opencl is not None : 196opencl_dir = install_sdk (opencl_config ,args .opencl ,base_dir ,args .force ) 197environment ["OPENCL_SDK_ROOT" ]= opencl_dir 198 199set_user_environment (environment ) 200logger .info ("SDK environment variables were updated. Start a new terminal before building." ) 201 202 203def main (): 204logging .basicConfig (level = logging .INFO ,format = "%(message)s" ) 205parser = argparse .ArgumentParser (description = "Install Windows on Snapdragon SDKs for llama.cpp." ) 206parser .add_argument ("--list-sdk-releases" ,action = "store_true" ,help = "List available Windows on Snapdragon SDK releases" ) 207parser .add_argument ("--sdk-base-dir" ,default = DEFAULT_SDK_BASE_DIR ,help = r"SDK installation directory (default: C:\Qualcomm)" ) 208parser .add_argument ("--hexagon" ,nargs = "?" ,const = SDK_CONFIGS [0 ]["default_version" ],metavar = "VERSION" ,help = "Install the Hexagon SDK, optionally selecting a version" ) 209parser .add_argument ("--opencl" ,nargs = "?" ,const = SDK_CONFIGS [1 ]["default_version" ],metavar = "VERSION" ,help = "Install the OpenCL SDK, optionally selecting a version" ) 210parser .add_argument ("--force" ,action = "store_true" ,help = "Reinstall selected SDKs even when they already exist" ) 211args = parser .parse_args () 212 213if args .list_sdk_releases : 214if args .sdk_base_dir != DEFAULT_SDK_BASE_DIR or args .hexagon is not None or args .opencl is not None or args .force : 215parser .error ("Installation options cannot be combined with --list-sdk-releases" ) 216list_sdk_releases () 217return 218if args .hexagon is None and args .opencl is None : 219parser .error ("Select at least one SDK with --hexagon or --opencl" ) 220if os .name != "nt" : 221parser .error ("SDK setup must run on Windows" ) 222setup_sdks (args ) 223 224 225if __name__ == "__main__" : 226try : 227main () 228except KeyboardInterrupt : 229logger .info ("\nInterrupted by user." ) 230sys .exit (130 ) 231except RuntimeError as err : 232logger .error ("Error: %s" ,err ) 233sys .exit (1 )