env-bootstrap.sh (26454B)
1 #!/usr/bin/env sh 2 # Copyright 2025 Nick Brassel (@tzarc) 3 # SPDX-License-Identifier: GPL-2.0-or-later 4 5 ################################################################################ 6 # This script will install the QMK CLI, toolchains, and flashing utilities. 7 ################################################################################ 8 # Environment variables: 9 # CONFIRM: Skip the pre-install delay. (or: --confirm) 10 # QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...) 11 # UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...) 12 # UV_TOOL_DIR: The directory to install `uv` tools to. (or: --uv-tool-dir=...) 13 # SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean) 14 # SKIP_PACKAGE_MANAGER: Skip installing the necessary packages for the package manager. (or: --skip-package-manager) 15 # SKIP_UV: Skip installing `uv`. (or: --skip-uv) 16 # SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli) 17 # SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains) 18 # SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils) 19 # SKIP_UDEV_RULES: Skip installing the udev rules for Linux. (or: --skip-udev-rules) 20 # SKIP_WINDOWS_DRIVERS: Skip installing the Windows drivers for the flashing utilities. (or: --skip-windows-drivers) 21 # 22 # Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`). 23 ################################################################################ 24 # Usage: 25 # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh 26 # 27 # Help: 28 # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --help 29 # 30 # An example which skips installing `uv` using environment variables: 31 # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | SKIP_UV=1 sh 32 # 33 # ...or by using command line arguments: 34 # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --skip-uv 35 # 36 # Any other configurable items listed above may be specified in the same way. 37 ################################################################################ 38 39 { # this ensures the entire script is downloaded # 40 set -eu 41 42 BOOTSTRAP_TMPDIR="$(mktemp -d /tmp/qmk-bootstrap-failure.XXXXXX)" 43 trap 'rm -rf "$BOOTSTRAP_TMPDIR" >/dev/null 2>&1 || true' EXIT 44 FAILURE_FILE="${BOOTSTRAP_TMPDIR}/fail" 45 46 # Work out which `sed` to use 47 command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed 48 49 script_args() { 50 cat <<__EOT__ 51 --help -- Shows this help text 52 --confirm -- Skips the delay before installation 53 --uv-install-dir={path} -- The directory to install \`uv\` into 54 --uv-tool-dir={path} -- The directory to install \`uv\` tools into 55 --qmk-distrib-dir={path} -- The directory to install the QMK distribution into 56 --skip-clean -- Skip cleaning the QMK distribution directory 57 --skip-package-manager -- Skip installing the necessary packages for the package manager 58 --skip-uv -- Skip installing \`uv\` 59 --skip-qmk-cli -- Skip installing the QMK CLI 60 --skip-qmk-toolchains -- Skip installing the QMK toolchains 61 --skip-qmk-flashutils -- Skip installing the QMK flashing utilities 62 --skip-udev-rules -- Skip installing the udev rules for Linux 63 --skip-windows-drivers -- Skip installing the Windows drivers for the flashing utilities 64 __EOT__ 65 # Hidden: 66 # --wsl-install -- Installs the WSL variant of qmk_flashutils 67 } 68 69 signal_execution_failure() { 70 touch "$FAILURE_FILE" >/dev/null 2>&1 || true 71 } 72 73 exit_if_execution_failed() { 74 if [ -e "$FAILURE_FILE" ]; then 75 exit 1 76 fi 77 } 78 79 script_help() { 80 echo "$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\s*@@g' -e 's@\s\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\n' ' ')" 81 echo 82 echo "Arguments:" 83 script_args 84 echo 85 echo "Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean')." 86 } 87 88 script_parse_args() { 89 local N 90 local V 91 while [ ! -z "${1:-}" ]; do 92 case "$1" in 93 --help) 94 script_help 95 exit 0 96 ;; 97 --*=*) 98 N=${1%%=*} 99 N=${N##--} 100 N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') 101 V=${1##*=} 102 export $N="$V" 103 ;; 104 --no-*) 105 N=${1##--no-} 106 N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') 107 unset $N 108 ;; 109 --*) 110 N=${1##--} 111 N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') 112 export $N=true 113 ;; 114 *) 115 echo "Unknown argument: '$1'" >&2 116 echo 117 script_help >&2 118 exit 1 119 ;; 120 esac 121 shift 122 unset N 123 unset V 124 done 125 } 126 127 nsudo() { 128 if [ "$(fn_os)" = "windows" ]; then 129 # No need for sudo under QMK MSYS 130 return 131 elif [ $(id -u) -ne 0 ]; then 132 if [ -n "$(command -v sudo 2>/dev/null || true)" ]; then 133 echo "sudo" 134 elif [ -n "$(command -v doas 2>/dev/null || true)" ]; then 135 echo "doas" 136 else 137 echo "Please install 'sudo' or 'doas' to continue." >&2 138 exit 1 139 fi 140 fi 141 true 142 } 143 144 download_url() { 145 local url=$1 146 local filename=${2:-$(basename "$url")} 147 local quiet='' 148 if [ -n "$(command -v curl 2>/dev/null || true)" ]; then 149 [ "$filename" = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 150 curl -LSf $quiet -o "$filename" "$url" 151 elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then 152 [ "$filename" = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 153 wget $quiet "-O$filename" "$url" 154 else 155 echo "Please install 'curl' to continue." >&2 156 exit 1 157 fi 158 } 159 160 github_api_call() { 161 local url="$1" 162 local token="${GITHUB_TOKEN:-${GH_TOKEN:-}}" 163 if [ -n "${token:-}" ]; then 164 if [ -n "$(command -v curl 2>/dev/null || true)" ]; then 165 curl -fsSL -H "Authorization: token $token" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/$url" 166 elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then 167 wget -q --header="Authorization: token $token" --header="Accept: application/vnd.github.v3+json" "https://api.github.com/$url" -O - 168 fi 169 else 170 download_url "https://api.github.com/$url" - 171 fi 172 } 173 174 fn_os() { 175 local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z') 176 if [ -z "$os_name" ]; then 177 os_name=$(uname -s | tr 'A-Z' 'a-z') 178 fi 179 case "$os_name" in 180 *darwin* | *macos* | *apple*) 181 echo macos 182 ;; 183 *windows* | *mingw* | *msys*) 184 echo windows 185 ;; 186 *linux*) 187 echo linux 188 ;; 189 *) 190 echo unknown 191 ;; 192 esac 193 } 194 195 fn_arch() { 196 local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z') 197 if [ -z "$arch_name" ]; then 198 arch_name=$(uname -m | tr 'A-Z' 'a-z') 199 fi 200 case "$arch_name" in 201 *arm64* | *aarch64*) 202 echo ARM64 203 ;; 204 *riscv64*) 205 echo RV64 206 ;; 207 *x86_64* | *x64*) 208 echo X64 209 ;; 210 *) 211 echo unknown 212 ;; 213 esac 214 } 215 216 preinstall_delay() { 217 [ -z "${CONFIRM:-}" ] || return 0 218 echo >&2 219 echo "Waiting 10 seconds before proceeding. Press Ctrl+C to cancel installation." >&2 220 sleep 10 221 } 222 223 get_package_manager_deps() { 224 case $(fn_os) in 225 macos) echo "zstd clang-format make hidapi libusb dos2unix git" ;; 226 windows) echo "base-devel: zstd:p toolchain:p clang:p hidapi:p dos2unix: git: unzip:" ;; 227 linux) 228 case $(grep ID /etc/os-release) in 229 *arch* | *manjaro* | *cachyos*) echo "zstd base-devel clang diffutils wget unzip zip hidapi dos2unix git" ;; 230 *debian* | *ubuntu*) echo "zstd build-essential clang-format diffutils wget unzip zip libhidapi-hidraw0 dos2unix git" ;; 231 *fedora*) echo "zstd clang diffutils which gcc git wget unzip zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git epel-release" ;; 232 *suse*) echo "zstd clang diffutils wget unzip zip libhidapi-hidraw0 dos2unix git libusb-1_0-devel gzip which" ;; 233 *gentoo*) echo "zstd sys-apps/diffutils wget unzip zip dev-libs/hidapi dos2unix dev-vcs/git dev-libs/libusb app-arch/gzip which" ;; 234 *) 235 echo >&2 236 echo "Sorry, we don't recognize your distribution." >&2 237 echo >&2 238 echo "Proceeding with the installation, however you will need to install at least the following tools manually:" >&2 239 echo " - make, git, curl, zstd, unzip, [lib]hidapi" >&2 240 echo "Other tools may be required depending on your distribution." >&2 241 echo >&2 242 echo "Alternatively, if you prefer Docker, try using the docker image instead:" >&2 243 echo " - https://docs.qmk.fm/#/getting_started_docker" >&2 244 ;; 245 esac 246 ;; 247 *) 248 # We can only really support macOS, Windows, and Linux at this time due to `uv` requirements. 249 echo >&2 250 echo "Sorry, we don't recognize your OS. Try using a compatible OS instead:" >&2 251 echo " - https://docs.qmk.fm/newbs_getting_started#set-up-your-environment" >&2 252 echo >&2 253 echo "If you cannot use a compatible OS, you can try installing the \`qmk\` Python package manually using \`pip\`, most likely requiring a virtual environment:" >&2 254 echo " % python3 -m pip install qmk" >&2 255 echo >&2 256 echo "All other dependencies will need to be installed manually, such as make, git, AVR and ARM toolchains, and associated flashing utilities." >&2 257 echo >&2 258 echo "**NOTE**: QMK does not provide official support for your environment. Here be dragons, you are on your own." >&2 259 signal_execution_failure 260 ;; 261 esac 262 } 263 264 print_package_manager_deps_and_delay() { 265 get_package_manager_deps | tr ' ' '\n' | sort | xargs -I'{}' echo " - {}" >&2 266 exit_if_execution_failed 267 preinstall_delay || exit 1 268 } 269 270 install_package_manager_deps() { 271 # Install the necessary packages for the package manager 272 case $(fn_os) in 273 macos) 274 if [ -n "$(command -v brew 2>/dev/null || true)" ]; then 275 echo "It will also install the following system packages using 'brew':" >&2 276 print_package_manager_deps_and_delay 277 278 brew update 279 280 local existing="" 281 local new="" 282 for dep in $(get_package_manager_deps); do 283 if brew list --formula | grep -q "^${dep}\$"; then 284 existing="${existing:-} $dep" 285 else 286 new="${new:-} $dep" 287 fi 288 done 289 290 if [ -n "${existing:-}" ]; then 291 brew upgrade $existing 292 fi 293 if [ -n "${new:-}" ]; then 294 brew install $new 295 fi 296 else 297 echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 298 exit 1 299 fi 300 ;; 301 windows) 302 echo "It will also install the following packages using 'pacman'/'pacboy':" >&2 303 print_package_manager_deps_and_delay 304 $(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys 305 $(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps) 306 ;; 307 linux) 308 case $(grep ID /etc/os-release) in 309 *arch* | *manjaro* | *cachyos*) 310 echo "It will also install the following system packages using 'pacman':" >&2 311 print_package_manager_deps_and_delay 312 $(nsudo) pacman --needed --noconfirm -S $(get_package_manager_deps) 313 ;; 314 *debian* | *ubuntu*) 315 echo "It will also install the following system packages using 'apt':" >&2 316 print_package_manager_deps_and_delay 317 $(nsudo) apt-get update 318 DEBIAN_FRONTEND=noninteractive \ 319 $(nsudo) apt-get --quiet --yes install $(get_package_manager_deps) 320 ;; 321 *fedora*) 322 echo "It will also install the following system packages using 'dnf':" >&2 323 print_package_manager_deps_and_delay 324 # Some RHEL-likes need EPEL for hidapi 325 $(nsudo) dnf -y install epel-release 2>/dev/null || true 326 # RHEL-likes have some naming differences in libusb packages, so manually handle those 327 $(nsudo) dnf -y install $(get_package_manager_deps | tr ' ' '\n' | grep -v 'epel-release' | grep -v libusb | tr '\n' ' ') 328 for pkg in $(get_package_manager_deps | tr ' ' '\n' | grep libusb); do 329 $(nsudo) dnf -y install "$pkg" 2>/dev/null || true 330 done 331 ;; 332 *opensuse* | *suse*) 333 echo "It will also install development tools as well as the following system packages using 'zypper':" >&2 334 print_package_manager_deps_and_delay 335 $(nsudo) zypper --non-interactive refresh 336 $(nsudo) zypper --non-interactive install -t pattern devel_basis devel_C_C++ 337 $(nsudo) zypper --non-interactive install $(get_package_manager_deps) 338 ;; 339 *gentoo*) 340 echo "It will also install the following system packages using 'emerge':" >&2 341 print_package_manager_deps_and_delay 342 $(nsudo) emerge --sync 343 $(nsudo) emerge --noreplace --ask=n $(get_package_manager_deps | tr ' ' '\n') || signal_execution_failure 344 exit_if_execution_failed 345 ;; 346 *) 347 print_package_manager_deps_and_delay 348 echo "Proceeding with the installation, you will need to ensure prerequisites are installed." >&2 349 ;; 350 esac 351 ;; 352 *) 353 print_package_manager_deps_and_delay 354 ;; 355 esac 356 } 357 358 install_uv() { 359 # Install `uv` (or update as necessary) 360 download_url https://astral.sh/uv/install.sh - | TMPDIR="$(windows_ish_path "${TMPDIR:-}")" UV_INSTALL_DIR="$(windows_ish_path "${UV_INSTALL_DIR:-}")" sh 361 } 362 363 setup_paths() { 364 # Set up the paths for any of the locations `uv` expects 365 if [ -n "${XDG_BIN_HOME:-}" ]; then 366 export PATH="$XDG_BIN_HOME:$PATH" 367 fi 368 if [ -n "${XDG_DATA_HOME:-}" ]; then 369 export PATH="$XDG_DATA_HOME/../bin:$PATH" 370 fi 371 [ ! -d "$HOME/.local/bin" ] || export PATH="$HOME/.local/bin:$PATH" 372 373 if [ -n "${UV_INSTALL_DIR:-}" ]; then 374 export PATH="$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH" # cater for both "flat" and "hierarchical" installs of `uv` 375 fi 376 377 if [ -n "${UV_TOOL_BIN_DIR:-}" ]; then 378 export PATH="$UV_TOOL_BIN_DIR:$PATH" 379 fi 380 } 381 382 uv_command() { 383 if [ "$(fn_os)" = "windows" ]; then 384 UV_TOOL_DIR="$(windows_ish_path "${UV_TOOL_DIR:-}")" \ 385 UV_TOOL_BIN_DIR="$(windows_ish_path "${UV_TOOL_BIN_DIR:-}")" \ 386 uv "$@" 387 else 388 uv "$@" 389 fi 390 } 391 392 install_qmk_cli() { 393 # Install the QMK CLI 394 uv_command tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk 395 396 # QMK is installed to... 397 local qmk_tooldir="$(posix_ish_path "$(uv_command tool dir)/qmk")" 398 399 # Activate the environment 400 if [ -e "$qmk_tooldir/bin" ]; then 401 . "$qmk_tooldir/bin/activate" 402 elif [ -e "$qmk_tooldir/Scripts" ]; then 403 . "$qmk_tooldir/Scripts/activate" 404 else 405 echo "Could not find the QMK environment to activate." >&2 406 exit 1 407 fi 408 409 # Install the QMK dependencies 410 uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt 411 uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt 412 413 # Deactivate the environment 414 deactivate 415 } 416 417 install_toolchains() { 418 # Get the latest toolchain release from https://github.com/qmk/qmk_toolchains 419 local latest_toolchains_release=$(github_api_call repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') 420 # Download the specific release asset with a matching keyword 421 local toolchain_url=$(github_api_call repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) 422 if [ -z "$toolchain_url" ]; then 423 echo "No toolchain found for this OS/Arch combination." >&2 424 exit 1 425 fi 426 427 # Download the toolchain release to the toolchains location 428 echo "Downloading compiler toolchains..." >&2 429 local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")" 430 download_url "$toolchain_url" "$target_file" 431 432 # Extract the toolchain 433 echo "Extracting compiler toolchains to '$QMK_DISTRIB_DIR'..." >&2 434 zstdcat "$target_file" | tar xf - -C "$QMK_DISTRIB_DIR" --strip-components=1 435 } 436 437 install_flashing_tools() { 438 local osarchvariant="$(fn_os)$(fn_arch)" 439 440 # Special case for WSL 441 if [ -n "${WSL_INSTALL:-}" ] || [ -n "${WSL_DISTRO_NAME:-}" ] || [ -f /proc/sys/fs/binfmt_misc/WSLInterop ]; then 442 osarchvariant="windowsWSL" 443 fi 444 445 # Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils 446 local latest_flashutils_release=$(github_api_call repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') 447 # Download the specific release asset with a matching keyword 448 local flashutils_url=$(github_api_call repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep "$osarchvariant") 449 if [ -z "$flashutils_url" ]; then 450 echo "No flashing tools found for this OS/Arch combination." >&2 451 exit 1 452 fi 453 454 # Download the flashing tools release to the toolchains location 455 echo "Downloading flashing tools..." >&2 456 local target_file="$QMK_DISTRIB_DIR/$(basename "$flashutils_url")" 457 download_url "$flashutils_url" "$target_file" 458 459 # Extract the flashing tools 460 echo "Extracting flashing tools to '$QMK_DISTRIB_DIR'..." >&2 461 zstdcat "$target_file" | tar xf - -C "$QMK_DISTRIB_DIR/bin" 462 # Move the release file to etc 463 mv "$QMK_DISTRIB_DIR/bin/flashutils_release"* "$QMK_DISTRIB_DIR/etc" 464 } 465 466 install_linux_udev_rules() { 467 # Download the udev rules to the toolchains location 468 echo "Downloading QMK udev rules file..." >&2 469 local qmk_rules_target_file="$QMK_DISTRIB_DIR/50-qmk.rules" 470 download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules" "$qmk_rules_target_file" 471 472 # Install the udev rules -- path list is aligned with qmk doctor's linux.py 473 local udev_rules_paths=" 474 /usr/lib/udev/rules.d 475 /usr/local/lib/udev/rules.d 476 /run/udev/rules.d 477 /etc/udev/rules.d 478 " 479 for udev_rules_dir in $udev_rules_paths; do 480 if [ -d "$udev_rules_dir" ]; then 481 echo "Installing udev rules to $udev_rules_dir/50-qmk.rules ..." >&2 482 $(nsudo) mv "$qmk_rules_target_file" "$udev_rules_dir" 483 $(nsudo) chown 0:0 "$udev_rules_dir/50-qmk.rules" 484 $(nsudo) chmod 644 "$udev_rules_dir/50-qmk.rules" 485 break 486 fi 487 done 488 489 # Reload udev rules 490 if command -v udevadm >/dev/null 2>&1; then 491 echo "Reloading udev rules..." >&2 492 $(nsudo) udevadm control --reload-rules || true 493 $(nsudo) udevadm trigger || true 494 else 495 echo "udevadm not found, skipping udev rules reload." >&2 496 fi 497 } 498 499 install_windows_drivers() { 500 # Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer 501 local latest_driver_installer_release=$(github_api_call repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') 502 # Download the specific release asset 503 local driver_installer_url=$(github_api_call repos/qmk/qmk_driver_installer/releases/tags/$latest_driver_installer_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep '\.exe') 504 if [ -z "$driver_installer_url" ]; then 505 echo "No driver installer found." >&2 506 exit 1 507 fi 508 # Download the driver installer release to the toolchains location 509 echo "Downloading driver installer..." >&2 510 local target_file="$QMK_DISTRIB_DIR/$(basename "$driver_installer_url")" 511 download_url "$driver_installer_url" "$target_file" 512 # Download the drivers list 513 download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/drivers.txt" "$QMK_DISTRIB_DIR/drivers.txt" 514 # Execute the driver installer 515 cd "$QMK_DISTRIB_DIR" 516 cmd.exe //c "qmk_driver_installer.exe --all --force drivers.txt" 517 cd - 518 # Remove the temporary files 519 rm -f "$QMK_DISTRIB_DIR/qmk_driver_installer.exe" "$QMK_DISTRIB_DIR/drivers.txt" || true 520 } 521 522 clean_tarballs() { 523 # Clean up the tarballs 524 rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true 525 } 526 527 windows_ish_path() { 528 [ -n "$1" ] || return 0 529 [ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -w "$1" || echo "$1" 530 } 531 532 posix_ish_path() { 533 [ -n "$1" ] || return 0 534 [ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -u "$1" || echo "$1" 535 } 536 537 # Set the Python version we want to use with the QMK CLI 538 export PYTHON_TARGET_VERSION=${PYTHON_TARGET_VERSION:-3.14} 539 540 # Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. 541 # Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. 542 if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then 543 export TMPDIR="$(posix_ish_path "$TMP")" 544 export UV_INSTALL_DIR="$(posix_ish_path "${UV_INSTALL_DIR:-/opt/uv}")" 545 export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-/opt/qmk}")" 546 export UV_TOOL_DIR="$(posix_ish_path "${UV_TOOL_DIR:-"$UV_INSTALL_DIR/tools"}")" 547 export UV_TOOL_BIN_DIR="$(posix_ish_path "$UV_TOOL_DIR/bin")" 548 fi 549 550 script_parse_args "$@" 551 552 echo "This QMK CLI installation script will install \`uv\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities." >&2 553 [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } 554 [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps 555 [ -n "${SKIP_UV:-}" ] || install_uv 556 557 # Make sure the usual `uv` and other associated directories are on the $PATH 558 setup_paths 559 560 # Work out where we want to install the distribution and tools now that `uv` is installed 561 export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv_command run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)}")" 562 563 # Clear out the distrib directory if necessary 564 if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then 565 if [ -d "$QMK_DISTRIB_DIR" ]; then 566 echo "Removing old QMK distribution..." >&2 567 rm -rf "$QMK_DISTRIB_DIR" 568 fi 569 fi 570 mkdir -p "$QMK_DISTRIB_DIR" 571 572 [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli 573 [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains 574 [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools 575 if [ "$(uname -s 2>/dev/null || true)" = "Linux" ]; then 576 [ -n "${SKIP_UDEV_RULES:-}" ] || install_linux_udev_rules 577 fi 578 if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then 579 [ -n "${SKIP_WINDOWS_DRIVERS:-}" ] || install_windows_drivers 580 fi 581 clean_tarballs 582 583 # Notify the user that they may need to restart their shell to get the `qmk` command 584 echo >&2 585 echo "QMK CLI installation complete." >&2 586 echo "The QMK CLI has been installed to '$(posix_ish_path "$(dirname "$(command -v qmk)")")'." >&2 587 echo "The QMK CLI venv has been created at '$(posix_ish_path "$(uv_command tool dir)/qmk")'." >&2 588 echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." >&2 589 echo >&2 590 echo "You may need to restart your shell to gain access to the 'qmk' command." >&2 591 echo "Alternatively, add "$(posix_ish_path "$(dirname "$(command -v qmk)")")" to your \$PATH:" >&2 592 echo " export PATH=\"$(posix_ish_path "$(dirname "$(command -v qmk)")"):\$PATH\"" >&2 593 594 } # this ensures the entire script is downloaded #