Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Reaper #43

Merged
merged 10 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ulwgl-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ jobs:
- name: Lint ulwgl_*.py files with Ruff
run: |
pip install ruff
ruff --output-format github ulwgl_*.py
ruff --output-format github ./ULWGL/ulwgl_*.py
- name: Test with unittest
run: python3 ulwgl_test.py
run: python3 ./ULWGL/ulwgl_test.py
- name: Test with unittest for plugins
if: ${{ matrix.version == '3.11' || matrix.version == '3.12' }}
run: python3 ulwgl_test_plugins.py
run: python3 ./ULWGL/ulwgl_test_plugins.py
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "reaper"]
path = reaper
url = https://github.com/Plagman/reaper.git
2 changes: 1 addition & 1 deletion ULWGL/ulwgl-run
15 changes: 15 additions & 0 deletions ULWGL/ulwgl_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,18 @@ def enable_steam_game_drive(env: Dict[str, str]) -> Dict[str, str]:
env["STEAM_RUNTIME_LIBRARY_PATH"] = ":".join(list(paths))

return env


def enable_reaper(
env: Dict[str, str], command: List[str], entry_point: str
) -> List[str]:
"""Enable Reaper to monitor and keep track of descendent processes."""
command.extend(
[
Path(entry_point).parent.joinpath("reaper").as_posix(),
"ULWGL_ID=" + env["ULWGL_ID"],
"--",
]
)

return command
5 changes: 3 additions & 2 deletions ULWGL/ulwgl_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
from pathlib import Path
from typing import Dict, Any, List, Set, Union, Tuple
from ulwgl_plugins import enable_steam_game_drive, set_env_toml
from ulwgl_plugins import enable_steam_game_drive, set_env_toml, enable_reaper
from re import match
from subprocess import run
from ulwgl_dl_util import get_ulwgl_proton
Expand Down Expand Up @@ -274,6 +274,8 @@ def build_command(
err: str = "The following file was not found in PROTONPATH: proton"
raise FileNotFoundError(err)

enable_reaper(env, command, entry_point)

command.extend([entry_point, "--verb", verb, "--"])
command.extend(
[
Expand Down Expand Up @@ -355,7 +357,6 @@ def main() -> int: # noqa: D103
try:
sys.exit(main())
except KeyboardInterrupt:
# Until Reaper is part of the command sequence, spawned process may still be alive afterwards
log.warning(msg("Keyboard Interrupt", Level.WARNING))
sys.exit(1)
except Exception as e: # noqa: BLE001
Expand Down
15 changes: 12 additions & 3 deletions ULWGL/ulwgl_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ def setUp(self):
"../../../ulwgl-run"
)

# Mock Reaper
Path(self.test_user_share, "reaper").touch()

# Mock the proton file in the dir
self.test_proton_dir.joinpath("proton").touch(exist_ok=True)

Expand Down Expand Up @@ -736,7 +739,7 @@ def test_copy_tree(self):
result = ulwgl_util.copyfile_tree(test_dir, self.test_local_share)

# Confirm the state of the dest dir
self.assertFalse(result, "Expected False after calling copyfile_tree")
self.assertTrue(result, "Expected False after calling copyfile_tree")
self.assertTrue(
any(self.test_local_share.iterdir()),
"Expected destination dir to not be empty",
Expand Down Expand Up @@ -1427,11 +1430,17 @@ def test_build_command(self):
test_command = ulwgl_run.build_command(self.env, test_command)
self.assertIsInstance(test_command, list, "Expected a List from build_command")
self.assertEqual(
len(test_command), 7, "Expected 7 elements in the list from build_command"
len(test_command), 10, "Expected 10 elements in the list from build_command"
)
# DEBUG [ulwgl_run.main:352]:['/home/celsius/.local/share/ULWGL/reaper', 'ULWGL_ID=0', '--', '/home/celsius/.local/share/ULWGL/ULWGL', '--verb', 'waitforexitandrun', '--', '/home/celsius/Proton/GE-Proton8-30/proton', 'waitforexitandrun', '/home/celsius/Games/aiyoku-no-eustia/Aiyoku no Eustia/BGI.exe']
# Verify contents
entry_point, opt1, verb, opt2, proton, verb2, exe = [*test_command]
reaper, id, opt0, entry_point, opt1, verb, opt2, proton, verb2, exe = [
*test_command
]
# The entry point dest could change. Just check if there's a value
self.assertTrue(reaper, "Expected reaper")
self.assertTrue(id, "Expected a tag for reaper")
self.assertTrue(opt0, "Expected --")
self.assertTrue(entry_point, "Expected an entry point")
self.assertEqual(opt1, "--verb", "Expected --verb")
self.assertEqual(verb, self.test_verb, "Expected a verb")
Expand Down
7 changes: 6 additions & 1 deletion ULWGL/ulwgl_test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,13 @@ def test_build_command_toml(self):
)

# Verify contents of the command
entry_point, opt1, verb, opt2, proton, verb2, exe = [*test_command]
reaper, id, opt0, entry_point, opt1, verb, opt2, proton, verb2, exe = [
*test_command
]
# The entry point dest could change. Just check if there's a value
self.assertTrue(reaper, "Expected reaper")
self.assertTrue(id, "Expected a tag for reaper")
self.assertTrue(opt0, "Expected --")
self.assertTrue(entry_point, "Expected an entry point")
self.assertEqual(opt1, "--verb", "Expected --verb")
self.assertEqual(verb, self.test_verb, "Expected a verb")
Expand Down
35 changes: 30 additions & 5 deletions ULWGL/ulwgl_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def setup_ulwgl(root: Path, local: Path) -> None:
if not local.exists():
return _install_ulwgl(ulwgl_path, local, steam_compat, json)

return _update_ulwgl(ulwgl_path, local, steam_compat, json, _get_json(local, CONFIG))
return _update_ulwgl(
ulwgl_path, local, steam_compat, json, _get_json(local, CONFIG)
)


def _install_ulwgl(
Expand Down Expand Up @@ -119,8 +121,8 @@ def _install_ulwgl(
copyfile_tree(root.joinpath("pressure-vessel"), local.joinpath("pressure-vessel"))

# Reaper
# print(f"Copying reaper -> {local}", file=stderr)
# copytree(root.joinpath("reaper").as_posix(), local.joinpath("reaper"), symlinks=True)
print(f"Copying reaper -> {local}", file=stderr)
cp(root.joinpath("reaper"), local.joinpath("reaper"))

# Runtime platform
print(f"Copying runtime -> {local} ...", file=stderr)
Expand Down Expand Up @@ -182,7 +184,25 @@ def _update_ulwgl(
# Be lazy and just trust the integrity of local
for key, val in json_root["ulwgl"]["versions"].items():
if key == "reaper":
pass
reaper: str = json_local["ulwgl"]["versions"]["reaper"]

# Directory is absent
if not local.joinpath("reaper").is_file():
print(
f"Reaper not found\nCopying {key} -> {local} ...",
file=stderr,
)

cp(root.joinpath("reaper"), local.joinpath("reaper"))

# Update
if val != reaper:
print(f"Updating {key} to {reaper} ...", file=stderr)

local.joinpath("reaper").unlink(missing_ok=True)
cp(root.joinpath("reaper"), local.joinpath("reaper"))

json_local["ulwgl"]["versions"]["reaper"] = val
elif key == "pressure_vessel":
# Pressure Vessel
pv: str = json_local["ulwgl"]["versions"]["pressure_vessel"]
Expand Down Expand Up @@ -231,6 +251,10 @@ def _update_ulwgl(
for file in local.glob("run*"):
file.unlink(missing_ok=True)
cp(root.joinpath(file.name), local.joinpath(file.name))

# Reaper
# We copy it as it will ideally be built within the runtime platform
cp(root.joinpath("reaper"), local.joinpath("reaper"))
elif local.joinpath(runtime).is_dir() and val != runtime:
# Update
print(f"Updating {key} to {val} ...", file=stderr)
Expand Down Expand Up @@ -354,6 +378,7 @@ def copyfile_reflink(src: Path, dst: Path) -> None:

dst.chmod(src.stat().st_mode)


def copyfile_tree(src: Path, dest: Path) -> bool:
"""Copy the directory tree from a source to a destination, overwriting existing files."""
for file in src.iterdir():
Expand All @@ -362,5 +387,5 @@ def copyfile_tree(src: Path, dest: Path) -> bool:
dest_subdir.mkdir(parents=True, exist_ok=True)
copyfile_tree(file, dest_subdir)
else:
shutil.copy2(file, dest / file.name)
copyfile_reflink(file, dest / file.name)
return True