From c91102937a8a89b2aa820fae11598ef1bec991a0 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 8 Nov 2026 16:29:01 +1100 Subject: [PATCH 1/4] of.sh: detect MSYS2 shells as msys2, pick MSYS2 env in the menu Fixes #8640. Running of.sh from an MSYS2 UCRT64 / CLANG64 / CLANGARM64 shell was detected as "vs / 64" and setup downloaded the Visual Studio libs. - Git for Windows has git-bash.exe at its root, MSYS2 doesn't. Git Bash (which also sets MSYSTEM=MINGW64) stays vs; an MSYS2 shell (pacman, or MSYSTEM ucrt64|clang64|clangarm64|mingw64) is now msys2 with OF_ARCH set to that environment. The plain MSYS shell falls back by CPU: clangarm64 on ARM64 (read from the registry, since MSYS2's own bash is emulated x64 there), ucrt64 on x64. - Choosing msys2 in the libs menu now asks for the MSYS2 environment: the detected one first (default), then clangarm64, ucrt64, clang64. - runLibsDownload passes the msys2 env to download_libs as -a, so the package matches the chosen env even from Git Bash, scoped to that download so it doesn't leak into a later vs download in the same session. --- scripts/of.sh | 113 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/scripts/of.sh b/scripts/of.sh index f20e7c4811a..deba6367644 100755 --- a/scripts/of.sh +++ b/scripts/of.sh @@ -154,6 +154,50 @@ detectHostVsArch(){ esac } +# MSYS2 toolchain environments that have openFrameworks lib packages +OF_MSYS2_ENVS=(ucrt64 clang64 clangarm64 mingw64) + +# Git for Windows ships git-bash.exe at its root; MSYS2 doesn't (#8640). +# Git Bash also sets MSYSTEM=MINGW64, so MSYSTEM alone can't tell them apart. +isGitBash(){ + [[ -f /git-bash.exe || -f /git-cmd.exe ]] +} + +isMsys2Shell(){ + isGitBash && return 1 + [[ -x /usr/bin/pacman ]] && return 0 + case "$(echo "${MSYSTEM:-}" | tr '[:upper:]' '[:lower:]')" in + ucrt64|clang64|clangarm64|mingw64) return 0 ;; + esac + return 1 +} + +# Native Windows CPU, also from emulated x64 shells (MSYS2's own bash is x64 +# on Windows on ARM and reports PROCESSOR_ARCHITECTURE=AMD64 / x86_64) +hostIsArm64(){ + local reg="/proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/PROCESSOR_ARCHITECTURE" + [[ -r "$reg" && "$(tr -d '\0\r\n ' < "$reg")" == "ARM64" ]] && return 0 + [[ "$(detectHostVsArch)" == "arm64" ]] && return 0 + [[ "$(uname -s 2>/dev/null)" == *ARM64* ]] +} + +# MSYS2 env for libs: the shell's own MSYSTEM when it is one of OF_MSYS2_ENVS +# (and this is really MSYS2), else by CPU — clangarm64 on ARM64, ucrt64 on x64 +detectMsys2Env(){ + local env + env=$(echo "${MSYSTEM:-}" | tr '[:upper:]' '[:lower:]') + if ! isGitBash; then + case "$env" in + ucrt64|clang64|clangarm64|mingw64) echo "$env"; return 0 ;; + esac + fi + if hostIsArm64; then + echo "clangarm64" + else + echo "ucrt64" + fi +} + autoDetectOS(){ if [[ -z "$PLATFORM" ]]; then export OF_OS OF_PLATFORM OF_ARCH @@ -162,10 +206,16 @@ autoDetectOS(){ darwin|dawin) OF_PLATFORM="osx"; OF_ARCH=$(uname -m) ;; linux) OF_PLATFORM="linux"; OF_ARCH=$(uname -m) ;; mingw*|cygwin*|msys*) - # Prefer VS toolchain path on Windows shells; arch is CPU not MSYSTEM name - OF_PLATFORM="vs" - OF_ARCH=$(detectHostVsArch) export OF_MSYSTEM="${MSYSTEM:-}" + if isMsys2Shell; then + # MSYS2 shell (UCRT64 / CLANG64 / CLANGARM64 / MINGW64) → msys2 libs + OF_PLATFORM="msys2" + OF_ARCH=$(detectMsys2Env) + else + # Git Bash / other Windows shells → Visual Studio; arch is CPU + OF_PLATFORM="vs" + OF_ARCH=$(detectHostVsArch) + fi ;; *) echoError "Unsupported platform: $OF_OS"; exit 1 ;; esac @@ -176,6 +226,8 @@ autoDetectOS(){ OF_ARCH="" if [[ "$OF_PLATFORM" == "vs" ]]; then OF_ARCH=$(detectHostVsArch) + elif [[ "$OF_PLATFORM" == "msys2" ]]; then + OF_ARCH=$(detectMsys2Env) fi fi } @@ -983,7 +1035,8 @@ printHelp(){ LIB_SOURCE=apothecary|oflibs|archive LIB_TAG=latest|v12.1.0|v0.11.2 LIB_LIBS=core|all|"glfw glm" - LIB_ARCH=64|arm64|arm64ec|all passed as -a to download_libs (vs/linux/msys2) + LIB_ARCH=64|arm64|arm64ec|all passed as -a to download_libs (vs/linux) + LIB_ARCH=ucrt64|clang64|clangarm64|mingw64 msys2 env (default: detected MSYSTEM) LIB_CLEAN_MODE=platform|merge|full # default platform: only lib/ DOWNLOAD_LIBS_ARGS="-vs --full-clean" extra flags to download_libs.sh OF_VS_2026=1 VS 2026 package names (_vs_2026_) @@ -1265,6 +1318,26 @@ assessPGState(){ # Download libs using current LIB_SOURCE / LIB_TAG (no banner) runLibsDownload(){ local platformDir="${1:-$OF_PLATFORM}" + # msys2 package = toolchain env; pass it explicitly — from Git Bash the + # wrapper would otherwise read MSYSTEM=MINGW64. Function-local export so + # it never leaks into a later vs/linux download in the same menu session. + if [[ "$platformDir" == "msys2" ]]; then + local msysEnv="${LIB_ARCH:-}" + case "$msysEnv" in + ucrt64|clang64|clangarm64|mingw64) ;; + *) + if [[ -n "${OF_MSYS2_LIB_ENV:-}" ]]; then + msysEnv="$OF_MSYS2_LIB_ENV" + elif [[ "$OF_PLATFORM" == "msys2" && -n "$OF_ARCH" ]]; then + msysEnv="$OF_ARCH" + else + msysEnv=$(detectMsys2Env) + fi + ;; + esac + unset OF_MSYS2_LIB_ENV + local -x LIB_ARCH="$msysEnv" + fi local source="${LIB_SOURCE:-apothecary}" local tag="${LIB_TAG:-latest}" local libsSel="${LIB_LIBS:-core}" @@ -1493,6 +1566,38 @@ menuPickLibPlatform(){ menuPick "Libraries for which platform?" "${opts[@]}" || return 1 LIB_PLATFORM="$UI_MENU_RESULT" export LIB_PLATFORM + if [[ "$LIB_PLATFORM" == "msys2" ]]; then + menuPickMsys2Env || return 1 + fi +} + +# MSYS2 environment → LIB_ARCH; the detected env is listed first (default) +menuPickMsys2Env(){ + local detected env label tag="(detected)" + local -a opts=() + if [[ "$OF_PLATFORM" == "msys2" && -n "$OF_ARCH" ]]; then + detected="$OF_ARCH" + else + # not in an MSYS2 shell (e.g. Git Bash) — CPU default, not detected + detected=$(detectMsys2Env) + tag="(default)" + fi + for env in "$detected" clangarm64 ucrt64 clang64; do + [[ " ${opts[*]} " == *"|${env} "* ]] && continue + case "$env" in + ucrt64) label="ucrt64 — GCC, UCRT (x64)" ;; + clang64) label="clang64 — Clang, UCRT (x64)" ;; + clangarm64) label="clangarm64 — Clang, UCRT (ARM64)" ;; + mingw64) label="mingw64 — GCC, MSVCRT (x64)" ;; + *) continue ;; + esac + [[ "$env" == "$detected" ]] && label+=" ${tag}" + opts+=("${label}|${env}") + done + menuPick "MSYS2 environment?" "${opts[@]}" || return 1 + # consumed (and cleared) by runLibsDownload + OF_MSYS2_LIB_ENV="$UI_MENU_RESULT" + echoSuccess "msys2 env → $OF_MSYS2_LIB_ENV" } menuPickLibTag(){ From 74d32dd141a6ebb05b8cdab532d3f597b8ae22cb Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 8 Nov 2026 16:31:16 +1100 Subject: [PATCH 2/4] of.sh: detect native ARM64 from emulated x64 Windows shells Git Bash (and MSYS2's own bash) run as emulated x64 on Windows on ARM and report PROCESSOR_ARCHITECTURE=AMD64 / uname -m x86_64, so ofMenu showed "vs / 64" on ARM64 machines. hostIsArm64 now reads the native CPU from the registry first (not emulated), then MSYSTEM / PROCESSOR_ARCHITEW6432 / uname; detectHostVsArch and the msys2 env default both use it. On ARM64 the vs libs default becomes arm64 (download_libs pulls x64 + arm64 + arm64ec). --- scripts/of.sh | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/scripts/of.sh b/scripts/of.sh index deba6367644..37a00b2b863 100755 --- a/scripts/of.sh +++ b/scripts/of.sh @@ -137,21 +137,28 @@ resolveApothecary # Host VS package arch: 64 | arm64 (for download_libs / status display) detectHostVsArch(){ - local m pa - pa="${PROCESSOR_ARCHITECTURE:-}" - [[ -n "${PROCESSOR_ARCHITEW6432:-}" ]] && pa="${PROCESSOR_ARCHITEW6432}" + if hostIsArm64; then echo "arm64"; else echo "64"; fi +} + +# Native Windows CPU, also from emulated x64 shells: Git Bash and MSYS2's own +# bash are x64 on Windows on ARM and report PROCESSOR_ARCHITECTURE=AMD64 / +# uname -m x86_64, so the registry (not emulated) is checked first +hostIsArm64(){ + local reg="/proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/PROCESSOR_ARCHITECTURE" + local pa + [[ -r "$reg" && "$(tr -d '\0\r\n ' < "$reg")" == "ARM64" ]] && return 0 case "${MSYSTEM:-}" in - CLANGARM64|clangarm64) echo "arm64"; return 0 ;; + CLANGARM64|clangarm64) return 0 ;; esac - case "$(echo "$pa" | tr '[:lower:]' '[:upper:]')" in - ARM64) echo "arm64"; return 0 ;; - AMD64|X86) echo "64"; return 0 ;; + pa="${PROCESSOR_ARCHITEW6432:-${PROCESSOR_ARCHITECTURE:-}}" + [[ "$(echo "$pa" | tr '[:lower:]' '[:upper:]')" == "ARM64" ]] && return 0 + case "$(uname -s 2>/dev/null)" in + *ARM64*) return 0 ;; esac - m=$(uname -m 2>/dev/null || echo "") - case "$m" in - aarch64|arm64|ARM64) echo "arm64" ;; - *) echo "64" ;; + case "$(uname -m 2>/dev/null)" in + aarch64|arm64|ARM64) return 0 ;; esac + return 1 } # MSYS2 toolchain environments that have openFrameworks lib packages @@ -172,15 +179,6 @@ isMsys2Shell(){ return 1 } -# Native Windows CPU, also from emulated x64 shells (MSYS2's own bash is x64 -# on Windows on ARM and reports PROCESSOR_ARCHITECTURE=AMD64 / x86_64) -hostIsArm64(){ - local reg="/proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment/PROCESSOR_ARCHITECTURE" - [[ -r "$reg" && "$(tr -d '\0\r\n ' < "$reg")" == "ARM64" ]] && return 0 - [[ "$(detectHostVsArch)" == "arm64" ]] && return 0 - [[ "$(uname -s 2>/dev/null)" == *ARM64* ]] -} - # MSYS2 env for libs: the shell's own MSYSTEM when it is one of OF_MSYS2_ENVS # (and this is really MSYS2), else by CPU — clangarm64 on ARM64, ucrt64 on x64 detectMsys2Env(){ From 6687241d104665491af0eef66a51d98ecf12bd1c Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 8 Nov 2026 16:39:18 +1100 Subject: [PATCH 3/4] of.sh: bump OF menu version to 0.4.6 --- scripts/of.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/of.sh b/scripts/of.sh index 37a00b2b863..d039854d6ed 100755 --- a/scripts/of.sh +++ b/scripts/of.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # of.sh - openFrameworks CLI | Dan Rosser 2025 -OF_SCRIPT_VERSION=0.4.5 +OF_SCRIPT_VERSION=0.4.6 OF_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" OF_DIR="$(realpath "$OF_DIR/../")" From 1c20c064cc10101e3831d470c5f5e491c8a2c7c1 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sun, 8 Nov 2026 16:42:08 +1100 Subject: [PATCH 4/4] =?UTF-8?q?of.sh:=20MSYS2=20without=20git=20=E2=80=94?= =?UTF-8?q?=20use=20Git=20for=20Windows,=20don't=20offer=20clone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MSYS2 doesn't ship git, so ofHasGit failed and the menu showed a git checkout as "nightly package (no .git)" and offered clone. When git isn't on PATH, fall back to Git for Windows' git.exe (Program Files or the per-user install). Without any git, a folder with .git is reported as "git checkout (git not found — pacman -S git)" and clone is only offered when there is no .git at all. The vsconfig() menu entry is now shown for the vs platform only, not in MSYS2 shells (`of vsconfig` still works from any Windows shell). --- scripts/of.sh | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/scripts/of.sh b/scripts/of.sh index d039854d6ed..def1e11a0b3 100755 --- a/scripts/of.sh +++ b/scripts/of.sh @@ -913,7 +913,7 @@ cmdStatus(){ if ofHasGit; then echoKV "git" "$(ofGitSummary)" else - echoKV "git" "nightly package (no .git)" + echoKV "git" "$(ofNoGitLabel)" echoNote "of clone — attach openFrameworks.git" fi local stSha stShaN @@ -1078,7 +1078,7 @@ cmdVersion(){ if ofHasGit; then echoKV "git" "$(ofGitSummary)" else - echoKV "git" "nightly package (no .git)" + echoKV "git" "$(ofNoGitLabel)" fi printf '\n' } @@ -1112,7 +1112,7 @@ menuVersion(){ if ofHasGit; then echoKV "git" "$(ofGitSummary)" else - echoKV "git" "nightly package (no .git)" + echoKV "git" "$(ofNoGitLabel)" fi printf '\n' if [[ -n "$bin" ]]; then @@ -2187,15 +2187,40 @@ pathIsGitTracked(){ [[ -n "$(git -C "$OF_DIR" ls-files -- "$p" 2>/dev/null | head -1)" ]] } +# MSYS2 doesn't ship git (pacman -S git). When git isn't on PATH but Git for +# Windows is installed, use its git.exe — MSYS2 converts the /c/… path args. +if ! command -v git >/dev/null 2>&1; then + for _ofGitExe in \ + "$(cygpath -u "$(printenv ProgramW6432 2>/dev/null || printenv ProgramFiles 2>/dev/null)" 2>/dev/null)/Git/cmd/git.exe" \ + "/c/Program Files/Git/cmd/git.exe" \ + "$(cygpath -u "$(printenv LOCALAPPDATA 2>/dev/null)" 2>/dev/null)/Programs/Git/cmd/git.exe"; do + if [[ -x "$_ofGitExe" ]]; then + OF_GIT_EXE="$_ofGitExe" + git(){ "$OF_GIT_EXE" "$@"; } + break + fi + done + unset _ofGitExe +fi + ofHasGit(){ command -v git >/dev/null 2>&1 || return 1 [[ -e "$OF_DIR/.git" ]] || return 1 git -C "$OF_DIR" rev-parse --verify HEAD >/dev/null 2>&1 } +# git line when ofHasGit fails: a real nightly, or a checkout without git +ofNoGitLabel(){ + if [[ -e "$OF_DIR/.git" ]]; then + printf 'git checkout (git not found — pacman -S git)' + else + printf 'nightly package (no .git)' + fi +} + ofGitSummary(){ local branch sha - ofHasGit || { printf 'nightly package (no .git)'; return 1; } + ofHasGit || { ofNoGitLabel; return 1; } branch=$(git -C "$OF_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "?") sha=$(git -C "$OF_DIR" rev-parse --short HEAD 2>/dev/null || echo "?") printf '%s @ %s' "$branch" "$sha" @@ -2213,6 +2238,11 @@ cmdClone(){ echoKV "remote" "$(git -C "$OF_DIR" remote get-url origin 2>/dev/null || echo none)" return 0 fi + if [[ -e "$OF_DIR/.git" ]]; then + echoWarning "already a git checkout, but git can't read it" + echoKV "git" "$(ofNoGitLabel)" + return 1 + fi if ! command -v git >/dev/null 2>&1; then echoError "git not found" echoNote "install git, then re-run: of clone" @@ -2747,7 +2777,7 @@ cmdMenu(){ if ofHasGit; then echoKV "git" "$(ofGitSummary)" else - echoKV "git" "nightly package (no .git)" + echoKV "git" "$(ofNoGitLabel)" fi printf '\n' @@ -2757,7 +2787,7 @@ cmdMenu(){ "status() — checker (cmdStatus)|status" "setup() — install (cmdSetup)|setup" ) - if [[ "$OF_PLATFORM" == "vs" || "$OF_PLATFORM" == "msys2" ]]; then + if [[ "$OF_PLATFORM" == "vs" ]]; then opts+=("vsconfig() — VS components (cmdVsConfig)|vsconfig") fi opts+=( @@ -2767,7 +2797,7 @@ cmdMenu(){ "version() — info (menuVersion)|version" "test() — smoke tests (menuTest)|test" ) - if ! ofHasGit; then + if [[ ! -e "$OF_DIR/.git" ]]; then opts+=("clone() — nightly → git clone (cmdClone)|clone") fi opts+=("exit()|exit")