joeylib2/scripts/lib/joeyEnv.sh
2026-09-29 19:31:34 -05:00

157 lines
5 KiB
Bash

# Sourced by the scripts in scripts/ (and tools/) so every one of them finds
# toolchains the same way: from the environment ./install.sh generates. A script calls joeyNeed for each tool it uses, and gets a clear
# "install this" message (exit 2, which the gate runners treat as SKIP)
# instead of failing halfway through with a missing binary.
JOEY_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
if [ -f "$JOEY_REPO/toolchains/env.sh" ]; then
# shellcheck source=/dev/null
source "$JOEY_REPO/toolchains/env.sh"
fi
# joeyNeed VAR INSTALL-TARGET -- exit 2 unless VAR names an existing path.
joeyNeed() {
local var="$1"
local target="$2"
if [ -z "${!var:-}" ] || [ ! -e "${!var}" ]; then
echo "$(basename "$0"): $var is not available; run: ./install.sh install $target" >&2
exit 2
fi
}
# joeyNeedFile PATH INSTALL-TARGET -- exit 2 unless PATH exists.
joeyNeedFile() {
if [ ! -e "$1" ]; then
echo "$(basename "$0"): $1 is missing; run: ./install.sh install $2" >&2
exit 2
fi
}
# DOSBox Staging must never read or write the user's own DOSBox setup, even
# ~/.config/dosbox (it creates that tree even with --noprimaryconf). Launch
# it as
# XDG_CONFIG_HOME="$work/xdg" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" ...
JOEY_DOSBOX_ISOLATE=(--noprimaryconf --nolocalconf)
# joeyExampleProgram NAME PORT -- the built program for an example name. A
# program that ships in its own folder with its DATA (Space Taxi on every
# port but the IIgs) is found inside that folder. When nothing is built yet
# it prints the flat path, so the caller's "not built" message names it.
joeyExampleProgram() {
local name="$1"
local bin="$JOEY_REPO/build/$2/bin"
local up="${name^^}"
local ext=""
local candidate
[ "$up" = "SPACETAXI" ] && up=STAXI
case "$2" in
iigs)
printf '%s\n' "$bin/$up"
return 0
;;
amiga)
# Mixed-case names, and Space Taxi is called Taxi there.
up="${name,,}"
case "$up" in
staxi|spacetaxi) up=taxi ;;
esac
up="${up^}"
;;
atarist) ext=".PRG" ;;
dos) ext=".EXE" ;;
x68000) ext=".X" ;;
dvx)
# Every program is a folder of its own, as it installs in DVX.
up="${up,,}"
printf '%s\n' "$bin/apps/joeylib/$up/$up.app"
return 0
;;
*)
echo "$(basename "$0"): unknown port '$2' (iigs, amiga, atarist, dos, x68000 or dvx)" >&2
return 2
;;
esac
for candidate in "$bin/$up/$up$ext" "$bin/$up$ext"; do
if [ -f "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
done
# A mixed-case Amiga name (SerTest) is not derivable from the example
# name, so take the file in any case.
candidate="$(find "$bin" -maxdepth 1 -type f -iname "$up$ext" -print -quit 2>/dev/null)"
if [ -n "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
printf '%s\n' "$bin/$up$ext"
}
# joeyFindWindow DISPLAY PID -- print the X window owned by process PID.
# More robust than matching titles, which differ between emulator builds.
joeyFindWindow() {
local win
for win in $(DISPLAY="$1" xdotool search --name -- "." 2>/dev/null); do
if [ "$(DISPLAY="$1" xdotool getwindowpid "$win" 2>/dev/null)" = "$2" ]; then
printf '%s\n' "$win"
return 0
fi
done
return 1
}
# joeyAlive PID -- PID is running. A zombie (exited, not yet reaped) is not,
# although `kill -0` still succeeds on it.
joeyAlive() {
local stat
stat="$(ps -o stat= -p "$1" 2>/dev/null)" || return 1
[[ "$stat" != Z* ]]
}
# joeyChild PID -- print the child of PID: the emulator a `timeout -s KILL`
# wrapper started. Stop the child first; stopping only the wrapper can orphan
# an emulator that ignores SIGTERM (Hatari does).
joeyChild() {
local tries
local child
for ((tries = 0; tries < 20; tries++)); do
child="$(pgrep -P "$1" | head -1)"
if [ -n "$child" ]; then
printf '%s\n' "$child"
return 0
fi
sleep 0.1
done
return 1
}
# joeyStop PID... -- stop emulator processes and make sure they are gone.
# Hatari, MAME and DOSBox Staging ignore or are slow to act on SIGTERM; a
# plain `kill` left them running (and burning CPU) after their script had
# finished, so escalate to SIGKILL. Only ever pass PIDs this script started.
joeyStop() {
local pid
local tick
for pid in "$@"; do
[ -n "$pid" ] || continue
kill "$pid" 2>/dev/null || continue
for tick in 1 2 3 4 5 6 7 8 9 10; do
joeyAlive "$pid" || break
sleep 0.2
done
kill -9 "$pid" 2>/dev/null || true
# Reap it when it is this shell's child (a no-op otherwise), so no
# zombie outlives the script where nothing else would reap it.
wait "$pid" 2>/dev/null || true
done
}