diff --git a/install.ps1 b/install.ps1 index 8b8b18bd7..d321dfe7b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -68,30 +68,35 @@ $installer = $temporary } - # The py launcher first, then python3 and python. The Microsoft Store - # places a python.exe that only points at the Store: it fails the - # version check like a missing one. - $findPython = { - $found = Get-Command py -ErrorAction SilentlyContinue - if ($found) { return ,@($found, @("-3")) } - foreach ($name in @("python3", "python")) { - $found = Get-Command $name -ErrorAction SilentlyContinue - if ($found) { return ,@($found, @()) } - } - return $null - } $usable = { param($candidate) if (-not $candidate) { return $false } $arguments = $candidate[1] - & $candidate[0].Source @arguments -c "import sys; raise SystemExit(sys.version_info < (3, 8))" | Out-Null + & $candidate[0].Source @arguments -c "import sys, ssl, unicodedata, zlib; raise SystemExit(sys.version_info < (3, 8))" 2>$null | Out-Null return ($LASTEXITCODE -eq 0) } + # The py launcher first, then python3 and python, the first that + # passes the check: a py launcher left with no usable runtime hid a + # Python 3.12 on PATH. The Microsoft Store places a python.exe that + # only points at the Store: it fails the check like a missing one. + $findPython = { + $candidates = @() + $found = Get-Command py -ErrorAction SilentlyContinue + if ($found) { $candidates += ,@($found, @("-3")) } + foreach ($name in @("python3", "python")) { + $found = Get-Command $name -ErrorAction SilentlyContinue + if ($found) { $candidates += ,@($found, @()) } + } + foreach ($candidate in $candidates) { + if (& $usable $candidate) { return ,$candidate } + } + return $null + } $needPython = "Python 3.8 or newer is required. Install it with " + "'winget install Python.Python.3.12' or from https://www.python.org/downloads/ " + "(tick 'Add python.exe to PATH'), then open a new PowerShell window and run this command again." $candidate = & $findPython - if (-not (& $usable $candidate)) { + if (-not $candidate) { # winget ships with Windows 10 and 11: at a console the bootstrap # offers to run the install the message names, then carries on. $winget = Get-Command winget -ErrorAction SilentlyContinue @@ -109,7 +114,7 @@ $candidate = & $findPython } } - if (-not (& $usable $candidate)) { throw $needPython } + if (-not $candidate) { throw $needPython } } $python = $candidate[0] $launcher = $candidate[1] diff --git a/install.sh b/install.sh index 408efd478..ebf1fc626 100755 --- a/install.sh +++ b/install.sh @@ -121,14 +121,29 @@ if [ -z "$INSTALLER" ] || [ ! -f "$INSTALLER" ]; then INSTALLER="$TEMP_INSTALLER" fi +# A Python can be built without ssl, unicodedata or zlib, and the installer +# needs all three: a python3 lacking ssl was chosen over a full python and +# every download then failed as if the network were down. find_python() { PYTHON="" + PYTHON_LACKING="" + PYTHON_OLD="" for command_name in python3 python; do - if command -v "$command_name" >/dev/null 2>&1 \ - && "$command_name" -c 'import sys; raise SystemExit(sys.version_info < (3, 8))' 2>/dev/null; then + command -v "$command_name" >/dev/null 2>&1 || continue + if ! "$command_name" -c 'import sys; raise SystemExit(sys.version_info < (3, 8))' \ + 2>/dev/null; then + found_version=$("$command_name" -c \ + 'import sys; print("%d.%d.%d" % tuple(sys.version_info[:3]))' 2>/dev/null) + if [ -n "$found_version" ]; then + PYTHON_OLD=${PYTHON_OLD:-"$command_name here is $found_version"} + fi + continue + fi + if "$command_name" -c 'import ssl, unicodedata, zlib' 2>/dev/null; then PYTHON=$command_name return 0 fi + PYTHON_LACKING=${PYTHON_LACKING:-$command_name} done return 1 } @@ -172,6 +187,20 @@ if ! find_python; then esac fi fi +if [ -z "$PYTHON" ] && [ -n "$PYTHON_LACKING" ]; then + echo "Error: $PYTHON_LACKING is built without a module the installer needs" >&2 + echo " (ssl, unicodedata or zlib). Run it with a full Python 3.8 or newer." >&2 + exit 1 +fi +if [ -z "$PYTHON" ] && [ -n "$PYTHON_OLD" ]; then + # Debian Buster ships 3.7: apt answers that python3 is already the newest + # version, and the same advice looped. + echo "Error: Python 3.8 or newer is required, and $PYTHON_OLD." >&2 + echo " Install a newer one: from this system's packages if they offer one, by" >&2 + echo " updating the system, or from https://www.python.org/downloads/." >&2 + echo " Then run this command again." >&2 + exit 1 +fi if [ -z "$PYTHON" ]; then echo "Error: Python 3.8 or newer is required." >&2 if [ -n "$install_command" ]; then @@ -183,6 +212,27 @@ if [ -z "$PYTHON" ]; then exit 1 fi +# Termux reaches shared storage, where RetroArch keeps its system folder, +# once termux-setup-storage has run: the installer told the user to run it +# and start again. At a terminal the bootstrap runs it here. It only asks +# the Termux app, which shows Android's permission dialog and creates +# ~/storage once allowed: the wait gives the user time to answer. +case "${TERMUX_VERSION:-}:${PREFIX:-}" in + ?*:*|*com.termux*) + if [ ! -d "$HOME/storage" ] && command -v termux-setup-storage >/dev/null 2>&1 \ + && (: /dev/null; then + echo "Android asks for the storage permission: allow it, so that the files reach" >/dev/tty + echo "the RetroArch folder." >/dev/tty + termux-setup-storage /dev/tty 2>&1 || true + waited=0 + while [ ! -d "$HOME/storage" ] && [ "$waited" -lt 60 ]; do + sleep 1 + waited=$((waited + 1)) + done + fi + ;; +esac + # Piped from curl, standard input is this script, not the terminal: the # installer would skip every question it can ask. Hand it the terminal when # there is one. diff --git a/tests/test_site_components.py b/tests/test_site_components.py index 2fe86df77..4bd277d60 100644 --- a/tests/test_site_components.py +++ b/tests/test_site_components.py @@ -1069,7 +1069,7 @@ class AChecksumLineIsAHash(unittest.TestCase): "deadbeef.bin", f"{md5} {sha1}", ] - out = _node(code + f"console.log(JSON.stringify({json.dumps(cases)}.map((c) => " + out = _node_typescript(code + f"console.log(JSON.stringify({json.dumps(cases)}.map((c) => " "[normalizeHash(c), isHash(normalizeHash(c))])));") self.assertEqual(json.loads(out), [ [md5, True], [sha1, True], [sha1, True], [sha1, True], [md5, True], diff --git a/wiki/reading-reports.md b/wiki/reading-reports.md index 066432d47..eda8e261f 100644 --- a/wiki/reading-reports.md +++ b/wiki/reading-reports.md @@ -107,7 +107,7 @@ RetroDECK: 2007/2008 OK, 1 untested [md5] UNTESTED (required): nintendo-ds/dsifirmware.bin: declared hash contradicted by the local file DISCREPANCY: nintendo-snes/sgb2.boot.rom: RetroDECK says OK but supersnes9x says size mismatch: got 524288, accepted [256] supersnes9x [size] - Core files: 1797 in pack, 1 required missing, 27 optional missing (--verbose lists them) + Core files: 1798 in pack, 1 required missing, 26 optional missing (--verbose lists them) Core files held but not packed: 154 (142 installed with its emulator, 8 read below a save folder per system and no system runs the core there, 4 a packed file occupies its directory) MISSING (required): FinalBurn Neo needs sfiii2a.zip (1 file) Game data: 5099 in pack, 38 missing (--verbose lists them)