mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-13 13:33:52 -06:00
fix: say when Windows does not answer under WSL
This commit is contained in:
1 parent
f693cd3ec6
commit
2571acc820
6 files changed
+87
-15
No files matched your search
+1
-1
@@ -16,7 +16,7 @@
|
||||
# and Invoke-WebRequest throws on a cut transfer before the hash is read:
|
||||
# the site's build rewrites this line there (scripts/sitebootstrap.py).
|
||||
$mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
|
||||
$defaultInstallSha256 = "1096705acbaa531b7e05458cc58b393426b7e1577d0db62bd2cd29e1c8257ca3"
|
||||
$defaultInstallSha256 = "450cee7625f098d5796452b02ab9c8bffdeaaef89180d2254bbe07ce354bc359"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+42
-3
@@ -1200,7 +1200,9 @@ def _parse_os_release() -> dict[str, str]:
|
||||
# Set under WSL: a Windows path read from a config or a rule names a drive
|
||||
# that Linux sees under /mnt (drvfs). C:\\RetroBat\\bios was used as a relative
|
||||
# path and nothing Windows-side was ever found from WSL.
|
||||
_WSL = {"active": False, "root": "/mnt"}
|
||||
# unasked: why Windows could not be asked for its folders, said once by
|
||||
# detection.
|
||||
_WSL = {"active": False, "root": "/mnt", "unasked": ""}
|
||||
_WINDOWS_VARIABLES = ("USERPROFILE", "APPDATA", "LOCALAPPDATA", "ProgramFiles", "ProgramFiles(x86)")
|
||||
|
||||
|
||||
@@ -1255,13 +1257,27 @@ def _enter_wsl() -> None:
|
||||
capture_output=True, text=True, timeout=10, stdin=subprocess.DEVNULL,
|
||||
cwd="/mnt/c" if Path("/mnt/c").is_dir() else None,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
except (OSError, subprocess.SubprocessError) as exc:
|
||||
# The others would wait as long: detection is told the Windows
|
||||
# side was not searched, never that nothing is there.
|
||||
_windows_unasked("cmd.exe", exc)
|
||||
return
|
||||
value = result.stdout.strip()
|
||||
if value and not value.startswith("%"):
|
||||
os.environ[variable] = str(_host_path(value))
|
||||
|
||||
|
||||
def _windows_unasked(tool: str, exc: Exception) -> None:
|
||||
"""Keep why a Windows program run from WSL gave no answer: an interop
|
||||
slow to start or turned off hid every Windows setup, and the run said
|
||||
none was installed."""
|
||||
if isinstance(exc, subprocess.TimeoutExpired):
|
||||
why = f"{tool} did not answer in {exc.timeout:g} s"
|
||||
else:
|
||||
why = f"{tool} could not run: {getattr(exc, 'strerror', None) or exc}"
|
||||
_WSL["unasked"] = _WSL["unasked"] or why
|
||||
|
||||
|
||||
def _expand_retroarch_path(value: str, app_dir: Path, home: Path | None = None) -> Path:
|
||||
"""Expand the notations RetroArch writes into its config values.
|
||||
|
||||
@@ -2521,6 +2537,9 @@ def refused_words(folder: Path, why: str, shown: str = "") -> str:
|
||||
"""The line said for a folder detection could not look in. shown is
|
||||
the folder as the screen writes it (~/.config/EmuDeck), the full path
|
||||
by default."""
|
||||
if _WSL["active"] and why == _WSL["unasked"] and folder == Path(_WSL["root"]):
|
||||
return (f"Could not look for setups on the Windows side: {why}. "
|
||||
"Run it again once Windows answers, or name the BIOS folder with --dest.")
|
||||
if folder in _DETECTION_UNREADABLE:
|
||||
return (f"Could not look for setups: {shown or folder} could not be read ({why}). "
|
||||
"A failing card or a link that loops gives this: check it, or name the "
|
||||
@@ -2555,6 +2574,22 @@ def _note_refused(path: Path, exc: OSError) -> None:
|
||||
print(f" {screen_text(words)}", file=sys.stderr)
|
||||
|
||||
|
||||
def _note_windows_unasked() -> None:
|
||||
"""Say once, as a folder detection could not look in, that Windows did
|
||||
not answer: its drives are then searched without its folders."""
|
||||
why = _WSL["unasked"]
|
||||
if (not why or not getattr(_SETUP_SEARCH, "active", False)
|
||||
or getattr(_SETUP_SEARCH, "quiet", False)):
|
||||
return
|
||||
refused = (Path(_WSL["root"]), why)
|
||||
if refused in _DETECTION_REFUSED:
|
||||
return
|
||||
_DETECTION_REFUSED.append(refused)
|
||||
words = refused_words(*refused)
|
||||
EVENTS.emit("unsearched", folder=str(refused[0]), reason=why, message=words)
|
||||
print(f" {screen_text(words)}", file=sys.stderr)
|
||||
|
||||
|
||||
def _detection_guarded(detect: Callable[..., list]) -> Callable[..., list]:
|
||||
"""Detection whose probes treat a folder this user cannot open as
|
||||
absent, and say so once.
|
||||
@@ -2694,7 +2729,9 @@ def _read_registry(key: str, value: str) -> str | None:
|
||||
capture_output=True, text=True, errors="replace", timeout=10,
|
||||
stdin=subprocess.DEVNULL, cwd="/mnt/c" if Path("/mnt/c").is_dir() else None,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
except (OSError, subprocess.SubprocessError) as exc:
|
||||
_windows_unasked("reg.exe", exc)
|
||||
_note_windows_unasked()
|
||||
return None
|
||||
match = re.search(
|
||||
rf"^\s*{re.escape(value)}\s+REG_(?:EXPAND_)?SZ\s+(.+?)\s*$", result.stdout, re.M
|
||||
@@ -2734,6 +2771,8 @@ def _detect_by_path(os_type: str, found: list[tuple[str, Path]]) -> None:
|
||||
def detect_platforms(os_type: str) -> list[tuple[str, Path]]:
|
||||
"""Detect installed emulator platforms and their BIOS directories."""
|
||||
found: list[tuple[str, Path]] = []
|
||||
if os_type == "wsl":
|
||||
_note_windows_unasked()
|
||||
|
||||
if os_type == "android":
|
||||
found.extend(_detect_android())
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins
|
||||
# curl or wget stop on a cut transfer before the hash is read: the site's
|
||||
# build rewrites this line there (scripts/sitebootstrap.py).
|
||||
MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
|
||||
DEFAULT_INSTALL_SHA256="1096705acbaa531b7e05458cc58b393426b7e1577d0db62bd2cd29e1c8257ca3"
|
||||
DEFAULT_INSTALL_SHA256="450cee7625f098d5796452b02ab9c8bffdeaaef89180d2254bbe07ce354bc359"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
{
|
||||
"dest": "roms/ports/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 1113525,
|
||||
"sha256": "1096705acbaa531b7e05458cc58b393426b7e1577d0db62bd2cd29e1c8257ca3",
|
||||
"size": 1115328,
|
||||
"sha256": "450cee7625f098d5796452b02ab9c8bffdeaaef89180d2254bbe07ce354bc359",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -71,8 +71,8 @@
|
||||
"muos": {
|
||||
"archive": {
|
||||
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
||||
"size": 1167152,
|
||||
"sha256": "eaed4292f1bcfdbe613e377b5e6b69933819b71c50b1b2d2f162603743d6b17a"
|
||||
"size": 1168955,
|
||||
"sha256": "1f52731fda2beb3ff9e7aaf7f3d9333853e66b99240b24e4f6b0026368755cb4"
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
@@ -92,8 +92,8 @@
|
||||
{
|
||||
"name": "RetroBIOS/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 1113525,
|
||||
"sha256": "1096705acbaa531b7e05458cc58b393426b7e1577d0db62bd2cd29e1c8257ca3",
|
||||
"size": 1115328,
|
||||
"sha256": "450cee7625f098d5796452b02ab9c8bffdeaaef89180d2254bbe07ce354bc359",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -166,8 +166,8 @@
|
||||
{
|
||||
"dest": ".config/modules/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 1113525,
|
||||
"sha256": "1096705acbaa531b7e05458cc58b393426b7e1577d0db62bd2cd29e1c8257ca3",
|
||||
"size": 1115328,
|
||||
"sha256": "450cee7625f098d5796452b02ab9c8bffdeaaef89180d2254bbe07ce354bc359",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4511,6 +4511,37 @@ class WindowsSeenFromWsl(unittest.TestCase):
|
||||
self.assertEqual(os.environ["ProgramFiles"], "")
|
||||
|
||||
|
||||
def test_windows_not_answering_is_said_not_taken_for_nothing(self):
|
||||
"""cmd.exe timing out on the first variable ended the import without
|
||||
a word, and detection said no setup was installed."""
|
||||
calls: list = []
|
||||
|
||||
def slow(argv, **kwargs):
|
||||
calls.append(argv)
|
||||
raise subprocess.TimeoutExpired(argv, kwargs.get("timeout", 10))
|
||||
|
||||
cleared = {name: "" for name in install._WINDOWS_VARIABLES}
|
||||
home = Path(_tempdir(self))
|
||||
err = io.StringIO()
|
||||
with unittest.mock.patch.dict(os.environ, {**cleared, "HOME": str(home)}), \
|
||||
unittest.mock.patch.object(install.subprocess, "run", slow), \
|
||||
unittest.mock.patch.object(Path, "home", return_value=home), \
|
||||
unittest.mock.patch.object(install, "_DETECTION_REFUSED", []), \
|
||||
unittest.mock.patch.object(install.EVENTS, "emit") as emit, \
|
||||
contextlib.redirect_stderr(err):
|
||||
install._enter_wsl()
|
||||
self.assertEqual(len(calls), 1)
|
||||
install.detect_platforms("wsl")
|
||||
install.detect_platforms("wsl")
|
||||
said = err.getvalue()
|
||||
self.assertEqual(said.count("on the Windows side"), 1, said)
|
||||
self.assertIn("cmd.exe did not answer in 10 s", said)
|
||||
kinds = [c.args[0] for c in emit.call_args_list]
|
||||
self.assertEqual(kinds.count("unsearched"), 1)
|
||||
self.assertEqual(emit.call_args_list[kinds.index("unsearched")].kwargs["folder"],
|
||||
str(self.root))
|
||||
|
||||
|
||||
class SteamRetroArchOnMacIsPortable(unittest.TestCase):
|
||||
"""platform_darwin.m makes the Steam build portable (HAVE_STEAM) with no
|
||||
portable.txt: a default system_directory is Application Support's, and
|
||||
|
||||
+4
-2
@@ -1160,7 +1160,9 @@ Under WSL both sides are searched. The Windows one is reached through its
|
||||
`/mnt/<drive>` mount: a `C:\` path from a rule or a config becomes
|
||||
`/mnt/c/...`, and the Windows variables the rules use (`USERPROFILE`,
|
||||
`APPDATA`, `LOCALAPPDATA`, `ProgramFiles`) are read through `cmd.exe` when
|
||||
WSL does not pass them. A forced `--platform retrobat` falls back to
|
||||
WSL does not pass them. When `cmd.exe` or `reg.exe` gives no answer within
|
||||
10 seconds or cannot run (interop turned off), the run says the Windows side
|
||||
was not searched, never that nothing is installed there. A forced `--platform retrobat` falls back to
|
||||
`/mnt/c/RetroBat/bios`. With nothing found, the folder question offers both
|
||||
usual folders of a setup that has one on each side: RetroArch's in this Linux
|
||||
(`~/.config/retroarch/system`, under Enter) and the Windows one
|
||||
@@ -1797,7 +1799,7 @@ other schemas under `api/v1/schemas/`.
|
||||
| Kind | Keys | When |
|
||||
|------|------|------|
|
||||
| `start` | `schema`, `installer`, `mode` | First line. `schema` is 1, `installer` the identity `--version` prints, `mode` one of `install`, `check`, `dry-run`, `list` |
|
||||
| `unsearched` | `folder`, `reason`, `message` | Before `detect`, once for each folder the search for setups could not look in, this user being refused it or a read failing there (a failing card, a link that loops). What it would hold counts as absent, and the other places are still searched: `detect` then lists what they hold, and an empty `platforms` after an `unsearched` line means nothing was found where the installer could look. `reason` says why in words (`Permission denied`), `message` says it in a sentence |
|
||||
| `unsearched` | `folder`, `reason`, `message` | Before `detect`, once for each folder the search for setups could not look in, this user being refused it or a read failing there (a failing card, a link that loops), or under WSL the Windows side when Windows did not answer (`folder` is then the drive mount, `/mnt`). What it would hold counts as absent, and the other places are still searched: `detect` then lists what they hold, and an empty `platforms` after an `unsearched` line means nothing was found where the installer could look. `reason` says why in words (`Permission denied`), `message` says it in a sentence |
|
||||
| `detect` | `os`, `os_name`, `platforms` | Once the platforms are known. `os_name` comes from `/etc/os-release`. `platforms` lists `{platform, name, bios_dir}`, and is empty when nothing supported was found |
|
||||
| `error` | `stage`, `reason`, `platform`, `message` | The file list could not be read (`stage` is `manifest`): `reason` is `network`, `not_found` or `invalid`, `clock` when the server's certificate was refused and the system clock reads a date before the installer was written, `no_ca` when the machine holds no list of trusted certificates (`SSL_CERT_FILE` names one), `no_ssl` when this Python was built without its `ssl` module and cannot use HTTPS, or `offline` when `--offline` forbids the request. A folder named with `--from` cannot be read (`stage` is `from`, `reason` `not_found`, `not_a_folder` for a file that is no ZIP, `unreadable`, or `system` for the root of the system, `/proc`, `/sys` or `/dev`). The BIOS folder sits under a folder this user cannot open (`stage` is `dest`, `reason` `unreadable`), or is a file or lies under one (`reason` `not_a_folder`), and the run ends with code 1 before anything is written. A platform, target, system, core or emulator named on the command line is unknown or cannot be used here (`stage` is `option`, `reason` `unknown`, `platform` the platform it was checked against or the name typed), refused before any download with code 2. The BIOS folder cannot be created (`stage` is `dest`, `reason` `not_writable`): a `summary` follows with what the run installed before it. Two systems are detected on one machine and no `--dest` names the folder (`stage` is `detect`, `reason` `conflict`). The platform named with `--platform` is found nowhere and no folder can be assumed for it: a card system with no card named, a portable emulator, Batocera or Recalbox named on a computer (their folder is the `bios` folder of their SHARE drive), or no usual folder on this system or one set aside behind a folder this user cannot open (`stage` is `detect`, `reason` `not_found`). So is a platform named by a file list or a pack, in a run without questions where it is found nowhere here. A `--dest` refused as the card of the system named carries the same `stage` and `reason`: another system's card, a folder inside a card, a drive with none of the files its card always has, or a folder that does not exist. `message` says it in words, and is the only thing that tells these cases apart |
|
||||
| `scanned` | `folder`, `files`, `truncated`, `other_drives` | Each folder named with `--from`, once read and before `detect`. `files` counts the files found, `truncated` is true when the search stopped at 200,000 of them, and `other_drives` lists the folders below it on another drive, which were not entered |
|
||||
|
||||
Reference in new issue
Block a user