mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-26 19:47:42 +00:00
Add darling-lol --set-prefix to keep the prefix on another drive
The prefix holds the installed clients and game (about 40 GB). The Nix
launcher always exported DPREFIX=${DPREFIX:-~/.darling}, so the prefix saved
in build/config.json was never used and the only way to move it was an
environment variable in every launch context.
- The launcher uses DPREFIX if set, then the saved prefix, then ~/.darling.
- darling-lol --set-prefix PATH runs scripts/nix-prefix.py in the namespace,
whose root owns the prefix files, so cp -a keeps their owners and xattrs.
It renames the prefix when possible; across filesystems it copies to
PATH.partial and removes the old prefix and its overlay work directory
only after the copy completes. A PATH that already holds a Darling prefix
is used as it is. It refuses non-empty directories that are not prefixes,
nested paths and paths with commas or colons (overlayfs separators), then
saves the path with configure.py --prefix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6044e838cc
commit
8c25542b31
5 files changed
+220
-2
No files matched your search
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""darling-lol --set-prefix PATH: choose where the guest prefix, and with it the
|
||||
installed games, is stored.
|
||||
|
||||
An existing prefix is moved to PATH. Across filesystems it is copied first and
|
||||
the old copy is removed only once the new one is complete. If PATH already holds
|
||||
a Darling prefix, the launcher switches to it and leaves both prefixes alone.
|
||||
"""
|
||||
import errno
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def has_files(path):
|
||||
return path.is_dir() and any(path.iterdir())
|
||||
|
||||
|
||||
def is_prefix(path):
|
||||
# Darling creates this mount point in every prefix it starts.
|
||||
return (path / 'Volumes/SystemRoot').is_dir()
|
||||
|
||||
|
||||
def move(old, new):
|
||||
new.parent.mkdir(parents=True, exist_ok=True)
|
||||
if new.is_dir():
|
||||
new.rmdir()
|
||||
try:
|
||||
os.rename(old, new)
|
||||
return
|
||||
except OSError as error:
|
||||
if error.errno != errno.EXDEV:
|
||||
raise
|
||||
partial = new.with_name(new.name + '.partial')
|
||||
if partial.exists():
|
||||
shutil.rmtree(partial)
|
||||
print(f'Copying {old} to {new}; this can take several minutes.', flush=True)
|
||||
try:
|
||||
subprocess.run(['cp', '-a', '--', str(old), str(partial)], check=True)
|
||||
except BaseException:
|
||||
shutil.rmtree(partial, ignore_errors=True)
|
||||
raise
|
||||
os.rename(partial, new)
|
||||
print(f'Removing {old}.', flush=True)
|
||||
shutil.rmtree(old)
|
||||
|
||||
|
||||
def main():
|
||||
old = Path(os.environ['DPREFIX']).resolve()
|
||||
new = Path(sys.argv[1]).expanduser()
|
||||
if not new.is_absolute():
|
||||
raise SystemExit('The prefix must be an absolute path.')
|
||||
new = new.resolve()
|
||||
# Darling passes the prefix to overlayfs, which splits its options on these.
|
||||
if any(character in str(new) for character in ',:\n'):
|
||||
raise SystemExit('The prefix path must not contain commas, colons or newlines.')
|
||||
if new != old:
|
||||
if new.is_relative_to(old) or old.is_relative_to(new):
|
||||
raise SystemExit(f'{new} and {old} must not contain each other.')
|
||||
if has_files(new):
|
||||
if not is_prefix(new):
|
||||
raise SystemExit(f'{new} is not empty and is not a Darling prefix; '
|
||||
'choose a new or empty directory.')
|
||||
print(f'Using the existing prefix in {new}; {old} is left as it is.')
|
||||
elif has_files(old):
|
||||
move(old, new)
|
||||
# Darling's overlay work directory; it creates a new one next to the prefix.
|
||||
shutil.rmtree(old.with_name(old.name + '.workdir'), ignore_errors=True)
|
||||
subprocess.run([sys.executable, str(ROOT / 'scripts/configure.py'), '--prefix', str(new)],
|
||||
cwd=ROOT, stdout=subprocess.DEVNULL, check=True)
|
||||
print(f'The prefix is now {new}. The next darling-lol launch sets up the runtime there if needed.')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user