Finish the portability sweep: one firmware resolver, no author paths

The probe scripts, run.sh, trace_run.sh, webui.py and two emulator tests each named the same hardcoded firmware from a source tree that is not in this repository. They now resolve QEMU and the firmware the way tools/uvk5_testenv.py does -- environment, then PATH, then whatever the checkout has -- and skip with a reason when there is nothing.

webui.py's --qemu and --elf lost their author defaults too: a bare qemu-system-arm through PATH, and no firmware until one is uploaded, which the page already reports.
This commit is contained in:
mckero committed 2026-10-01 15:15:31 +08:00
1 parent ae8b48c85a
commit 542515d5ad
21 files changed
+133 -37

No files matched your search

+2 -1
View File
@@ -114,7 +114,8 @@ keypresses silently stop working. Run the test after touching that code;
uvk5_stream.py the MJPEG-style frame pump behind /stream uvk5_stream.py the MJPEG-style frame pump behind /stream
uvk5_supervisor.py starts, stops and recovers the emulator process uvk5_supervisor.py starts, stops and recovers the emulator process
test_kill_emulator.sh cleanup never kills an unrelated process test_kill_emulator.sh cleanup never kills an unrelated process
(plus ad-hoc probe scripts -- scan_trace.sh, gpio_watch.py and friends -- uvk5_elf.sh where the probe scripts find a firmware (env, then the checkout)
(plus ad-hoc probe scripts -- scan_trace.sh, gpio_watch.py and friends --
kept because they are quick to reach for, not because they are polished) kept because they are quick to reach for, not because they are polished)
harness/, stubs/, shim/, tests/ host build of the CW timing chain (stage A) harness/, stubs/, shim/, tests/ host build of the CW timing chain (stage A)
+1
View File
@@ -102,6 +102,7 @@ VFO 重算了状态。在真机上你只看到"什么都没发生",在这里
uvk5_stream.py /stream 背后的抓帧泵 uvk5_stream.py /stream 背后的抓帧泵
uvk5_supervisor.py 启动、停止、以及故障后恢复模拟器进程 uvk5_supervisor.py 启动、停止、以及故障后恢复模拟器进程
test_kill_emulator.sh 清理逻辑绝不会杀掉无关进程 test_kill_emulator.sh 清理逻辑绝不会杀掉无关进程
uvk5_elf.sh 探针脚本从哪里找固件(环境变量,然后本仓库)
(另有一批临时探针脚本 —— scan_trace.sh、gpio_watch.py 等 —— (另有一批临时探针脚本 —— scan_trace.sh、gpio_watch.py 等 ——
留着是因为随手就能用,不是因为它们打磨过) 留着是因为随手就能用,不是因为它们打磨过)
harness/, stubs/, shim/, tests/ CW 时序链的宿主机构建(阶段 A) harness/, stubs/, shim/, tests/ CW 时序链的宿主机构建(阶段 A)
+3 -1
View File
@@ -6,7 +6,9 @@
# probe only read IDR, which cannot tell those two apart. # probe only read IDR, which cannot tell those two apart.
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
BASE=0x50000400 BASE=0x50000400
IDR=$((BASE + 0x10)) IDR=$((BASE + 0x10))
+3 -1
View File
@@ -5,7 +5,9 @@
# both twice gives the rate and an estimate of how long the delay will take. # both twice gives the rate and an estimate of how long the delay will take.
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
GAP="${1:-4}" GAP="${1:-4}"
SCRIPT=$(mktemp --suffix=.gdb) SCRIPT=$(mktemp --suffix=.gdb)
trap 'rm -f "$SCRIPT"' EXIT trap 'rm -f "$SCRIPT"' EXIT
+3 -1
View File
@@ -5,7 +5,9 @@
# IDR 0x10, ODR 0x14. # IDR 0x10, ODR 0x14.
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
BASE=0x50000400 BASE=0x50000400
SCRIPT=$(mktemp --suffix=.gdb) SCRIPT=$(mktemp --suffix=.gdb)
trap 'rm -f "$SCRIPT"' EXIT trap 'rm -f "$SCRIPT"' EXIT
+3 -1
View File
@@ -6,7 +6,9 @@
# the debounce in app.c accept it? # the debounce in app.c accept it?
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
python3 - "$KEY" <<'PY' python3 - "$KEY" <<'PY'
+3 -1
View File
@@ -9,7 +9,9 @@
# BSRR: low half sets a pin, high half resets it (py32f071xB.h). # BSRR: low half sets a pin, high half resets it (py32f071xB.h).
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
BASE=0x50000400 BASE=0x50000400
BSRR=$((BASE + 0x18)) BSRR=$((BASE + 0x18))
+1 -1
View File
@@ -6,7 +6,7 @@
# in the matrix wiring or the debounce. # in the matrix wiring or the debounce.
set -uo pipefail set -uo pipefail
TOOLS="$HOME/uvk5-port/sim/tools" TOOLS="$(cd "$(dirname "$0")" && pwd)"
KEY="${1:-MENU}" KEY="${1:-MENU}"
python3 - "$KEY" <<'PY' python3 - "$KEY" <<'PY'
+3 -1
View File
@@ -6,7 +6,9 @@
# (different PC, or r1 reset for a new call). # (different PC, or r1 reset for a new call).
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
SAMPLES="${1:-6}" SAMPLES="${1:-6}"
GAP="${2:-2}" GAP="${2:-2}"
SCRIPT=$(mktemp --suffix=.gdb) SCRIPT=$(mktemp --suffix=.gdb)
+3 -1
View File
@@ -6,7 +6,9 @@
# this only observes; the firmware does the driving. # this only observes; the firmware does the driving.
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
STEPS="${2:-400}" STEPS="${2:-400}"
+7 -3
View File
@@ -7,9 +7,13 @@
# Usage: run.sh [firmware.elf] # Usage: run.sh [firmware.elf]
set -euo pipefail set -euo pipefail
QEMU="$HOME/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm" QEMU="${QEMU:-$(command -v qemu-system-arm || true)}"
ELF="${1:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" [ -n "$QEMU" ] || { echo "SKIP no qemu-system-arm; set QEMU or put it on PATH" >&2; exit 0; }
FLASH="$HOME/uvk5-port/sim/assets/flash.img" ELF="${1:-}"
# shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
FLASH="${UVK5_FLASH_IMAGE:-$(cd "$(dirname "$0")/.." && pwd)/assets/flash.img}"
QMP=/tmp/uvk5-qmp.sock QMP=/tmp/uvk5-qmp.sock
# Never `pkill -f 'M uv-k5-v3'` here: see tools/lib_kill_emulator.sh for why that # Never `pkill -f 'M uv-k5-v3'` here: see tools/lib_kill_emulator.sh for why that
+3 -1
View File
@@ -6,7 +6,9 @@
# the firmware looks, versus the rows going low but the debounce rejecting them. # the firmware looks, versus the rows going low but the debounce rejecting them.
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
SAMPLES="${2:-10}" SAMPLES="${2:-10}"
+3 -1
View File
@@ -7,7 +7,9 @@
# go low". # go low".
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
KEY="${1:-MENU}" KEY="${1:-MENU}"
HITS="${2:-12}" HITS="${2:-12}"
+3 -2
View File
@@ -46,8 +46,9 @@ kill "$decoy" "$decoy2" 2>/dev/null || true
wait 2>/dev/null || true wait 2>/dev/null || true
# And a real emulator must actually be terminated. # And a real emulator must actually be terminated.
QEMU="$HOME/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm" QEMU="${QEMU:-$(command -v qemu-system-arm || true)}"
ELF="$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf" . "$HERE/uvk5_elf.sh"
ELF=$(uvk5_find_elf || true)
IMG=$(mktemp /tmp/kill-test-XXXX.img) IMG=$(mktemp /tmp/kill-test-XXXX.img)
gzip -dc "$HERE/../assets/pristine/flash-pristine.img.gz" > "$IMG" gzip -dc "$HERE/../assets/pristine/flash-pristine.img.gz" > "$IMG"
+13 -6
View File
@@ -18,18 +18,25 @@ import time
HERE = os.path.dirname(os.path.abspath(__file__)) HERE = os.path.dirname(os.path.abspath(__file__))
SIM = os.path.dirname(HERE) SIM = os.path.dirname(HERE)
QEMU = os.path.expanduser("~/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm") sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
ELF = os.path.expanduser("~/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf") import uvk5_testenv
QEMU = uvk5_testenv.qemu()
ELF = uvk5_testenv.firmware()
FLASH = os.path.join(SIM, "assets", "flash.img") FLASH = os.path.join(SIM, "assets", "flash.img")
LOG = "/tmp/uvk5-serial-test.log" LOG = "/tmp/uvk5-serial-test.log"
QMP = "/tmp/uvk5-serial-test.sock" QMP = "/tmp/uvk5-serial-test.sock"
def main(): def main():
for path, what in ((QEMU, "QEMU binary"), (ELF, "firmware ELF"), missing = uvk5_testenv.missing([
(FLASH, "flash image")): (QEMU, "QEMU binary", "set QEMU=/path/to/qemu-system-arm, or put it on PATH"),
if not os.path.exists(path): (ELF, "firmware", "run tools/fetch_firmware.py, or set ELF=..."),
sys.exit(f"missing {what}: {path}") (FLASH, "flash image", "run tools/make_flash.py"),
])
if missing:
print("SKIP: %s" % missing)
return 0
if os.path.exists(QMP): if os.path.exists(QMP):
os.unlink(QMP) os.unlink(QMP)
+5 -2
View File
@@ -25,8 +25,11 @@ HERE = os.path.dirname(os.path.abspath(__file__))
SIM = os.path.dirname(HERE) SIM = os.path.dirname(HERE)
sys.path.insert(0, HERE) sys.path.insert(0, HERE)
QEMU = os.path.expanduser("~/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm") sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
ELF = os.path.expanduser("~/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf") import uvk5_testenv
QEMU = uvk5_testenv.qemu()
ELF = uvk5_testenv.firmware()
FLASH = os.path.join(SIM, "assets", "flash.img") FLASH = os.path.join(SIM, "assets", "flash.img")
QMP = "/tmp/uvk5-webui-e2e.sock" QMP = "/tmp/uvk5-webui-e2e.sock"
HTTP_PORT = 8099 HTTP_PORT = 8099
+8 -4
View File
@@ -6,9 +6,13 @@
# - does the GPIO input callback run? (TRACE gpio... set_input) # - does the GPIO input callback run? (TRACE gpio... set_input)
set -uo pipefail set -uo pipefail
QEMU="$HOME/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm" QEMU="${QEMU:-$(command -v qemu-system-arm || true)}"
ELF="${1:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" [ -n "$QEMU" ] || { echo "SKIP no qemu-system-arm; set QEMU or put it on PATH" >&2; exit 0; }
FLASH="$HOME/uvk5-port/sim/assets/flash.img" ELF="${1:-}"
# shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
FLASH="${UVK5_FLASH_IMAGE:-$(cd "$(dirname "$0")/.." && pwd)/assets/flash.img}"
LOG=/tmp/uvk5-trace.log LOG=/tmp/uvk5-trace.log
QMP=/tmp/uvk5-qmp.sock QMP=/tmp/uvk5-qmp.sock
@@ -26,7 +30,7 @@ sleep 1
-kernel "$ELF" -gdb tcp::1234 >"$LOG" 2>&1 & -kernel "$ELF" -gdb tcp::1234 >"$LOG" 2>&1 &
sleep 12 sleep 12
python3 "$HOME/uvk5-port/sim/tools/key.py" MENU >/dev/null 2>&1 || true python3 "$(dirname "$0")/key.py" MENU >/dev/null 2>&1 || true
sleep 2 sleep 2
echo "== keypad row drives ==" echo "== keypad row drives =="
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Where the firmware is, for the ad-hoc probe scripts.
#
# Every one of them used to name the same hardcoded path -- one developer's CW build --
# so none of them ran on a machine that did not have that file. The order here matches
# tools/uvk5_testenv.py, so the scripts and the tests agree about what is available.
#
# ELF, UVK5_FIRMWARE, UVK5_MULTIBOOT_IMAGE the environment wins
# assets/firmware/*, work/* then whatever the checkout has
#
# Returns non-zero when there is nothing, so a caller can skip with a reason rather
# than run against a path that does not exist.
uvk5_find_elf() {
local var value
for var in ELF UVK5_FIRMWARE UVK5_MULTIBOOT_IMAGE; do
eval "value=\${$var:-}"
if [ -n "$value" ] && [ -e "$value" ]; then
printf '%s' "$value"
return 0
fi
done
local root cand
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
for cand in "$root"/assets/firmware/*.elf "$root"/assets/firmware/*.bin \
"$root"/work/*.elf "$root"/work/*.bin; do
if [ -e "$cand" ]; then
printf '%s' "$cand"
return 0
fi
done
return 1
}
+2 -4
View File
@@ -1,10 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Where the emulator tests find a QEMU and a firmware, and what to say when they cannot. """Where the emulator tests find a QEMU and a firmware, and what to say when they cannot.
The emulator tests used to name one developer's build directory: The emulator tests used to name one developer's build directory -- a QEMU under their
home, and one firmware build from a source tree outside this repository --
QEMU = os.path.expanduser("~/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm")
ELF = os.path.expanduser("~/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf")
which is fine on the machine they were written on and useless anywhere else -- and a which is fine on the machine they were written on and useless anywhere else -- and a
missing file used to be a hard failure, so a checkout without that build could never missing file used to be a hard failure, so a checkout without that build could never
+29 -4
View File
@@ -1164,6 +1164,29 @@ pollFirmware();
</body></html>""" </body></html>"""
def _default_qemu():
"""A QEMU to spawn: the environment, then PATH. Never one developer's build dir."""
try:
import uvk5_testenv
return str(uvk5_testenv.qemu() or "qemu-system-arm")
except Exception:
return "qemu-system-arm"
def _default_firmware():
"""Whatever firmware this checkout has, or nothing.
Nothing is the normal case now: a firmware can be uploaded from the page, and the page
says when one is not loaded. tools/uvk5_testenv.py looks in assets/firmware and work.
"""
try:
import uvk5_testenv
firmware = uvk5_testenv.firmware()
return str(firmware) if firmware else None
except Exception:
return None
def main() -> int: def main() -> int:
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument("--qmp", default="/tmp/uvk5-qmp.sock") ap.add_argument("--qmp", default="/tmp/uvk5-qmp.sock")
@@ -1178,12 +1201,14 @@ def main() -> int:
help="attach to an emulator started elsewhere (run.sh) " help="attach to an emulator started elsewhere (run.sh) "
"instead of managing one. Off is then refused, since " "instead of managing one. Off is then refused, since "
"this server did not start that process.") "this server did not start that process.")
ap.add_argument("--qemu", default=os.path.expanduser( # A path or a bare name: Popen resolves a bare name through PATH. The machine is
"~/qemu-build/qemu-7.2+dfsg/build/qemu-system-arm")) # built from this checkout, not from one developer's build directory.
ap.add_argument("--qemu", default=_default_qemu())
# Named --elf for history; any .bin or .elf works, and its shape is read out # Named --elf for history; any .bin or .elf works, and its shape is read out
# of the file rather than assumed. More can be uploaded from the page. # of the file rather than assumed. More can be uploaded from the page.
ap.add_argument("--elf", "--firmware", dest="elf", default=os.path.expanduser( # Nothing local by default: upload a firmware from the page, which says so when
"~/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf")) # none is loaded. tools/uvk5_testenv.py finds one in assets/firmware or work.
ap.add_argument("--elf", "--firmware", dest="elf", default=_default_firmware())
ap.add_argument("--flash", default=os.path.join( ap.add_argument("--flash", default=os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"assets", "flash.img")) "assets", "flash.img"))
+3 -1
View File
@@ -4,7 +4,9 @@
# Usage: where.sh [samples] # Usage: where.sh [samples]
set -uo pipefail set -uo pipefail
ELF="${ELF:-$HOME/uvk5-port/uvk5-sat/build/CW/nr7y.cw.elf}" # shellcheck source=tools/uvk5_elf.sh
. "$(dirname "$0")/uvk5_elf.sh"
ELF=$(uvk5_find_elf) || { echo "SKIP no firmware found; set ELF or put one in assets/firmware" >&2; exit 0; }
SAMPLES="${1:-1}" SAMPLES="${1:-1}"
SCRIPT=$(mktemp --suffix=.gdb) SCRIPT=$(mktemp --suffix=.gdb)
trap 'rm -f "$SCRIPT"' EXIT trap 'rm -f "$SCRIPT"' EXIT