fix(setup): use consistent runtime paths and reject incomplete installs

This commit is contained in:
Nicolai Bruns committed 2026-10-17 22:09:29 +02:00
1 parent 057f0f06d1
commit 9adfaf010f
18 files changed
+439 -48

No files matched your search

+16 -1
View File
@@ -12,7 +12,22 @@ python3 scripts/configure.py --guest-user my-linux-user
python3 scripts/configure.py --sdk /usr/local/libexec/darling
```
The normal wrappers currently support the default `~/.darling` prefix and `/usr/local` base installation. The configurator exposes a custom prefix for developers, but several archived tools assume the default layout. Custom prefixes/install locations have not been tested end to end.
The runtime installation wrappers resolve `DPREFIX` first, otherwise the saved
`configure.py --prefix` setting. The configured guest account supplies the
default guest home. `RIOT_GUEST_HOME`, when set, is an absolute **host** path
inside the selected prefix, not a guest `/Users/...` path. Installers, runtime
validation and the Riot launcher's guest library paths use that selection.
Paths containing spaces are supported by isolated regression fixtures. Explicit
environment overrides win over saved configuration; clear stale values before
selecting a different installation.
A complete real installation using a custom prefix has not been verified.
Archived tools may still assume the default layout. Runtime validation fails on
missing expected binaries, unreadable/missing directories or checker errors;
an empty input set is not a successful validation.
Runtime installation requires the selected runtime to stop before overlay
replacement. Failed shutdown blocks replacement.
The build uses Unix Makefiles because the incremental runtime helpers read CMake `flags.make` and `link.txt`. Ninja is not a drop-in replacement for those helpers.
+6 -1
View File
@@ -17,7 +17,12 @@ build=ROOT/'build';build.mkdir(exist_ok=True)
folder=build/'runtime/tools';folder.mkdir(parents=True,exist_ok=True)
for source in (ROOT/'tools').iterdir():
if not source.is_file():continue
target=folder/source.name;target.write_text(render(source.read_text(),c));os.chmod(target,source.stat().st_mode & 0o777)
target=folder/source.name
# Unlink the directory entry, including dangling symlinks. Any other error
# must abort rather than following a leftover link or writing through it.
target.unlink(missing_ok=True)
target.write_text(render(source.read_text(),c))
os.chmod(target,source.stat().st_mode & 0o777)
link=folder.parent/'build'
if not link.exists():link.symlink_to(build,target_is_directory=True)
print('Configured',len(list(folder.iterdir())),'runtime sources for',c['guest_home'])
+5 -5
View File
@@ -4,6 +4,7 @@ set -eu
repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
cd "$repo"
python3 scripts/configure.py
. build/runtime/tools/runtime-paths.sh
if [ -d build/venv/bin ]; then PATH="$repo/build/venv/bin:$PATH"; export PATH; fi
# A server killed by the base install leaves a stale .init.pid that makes darling shell fail.
python3 build/runtime/tools/ensure-darling-runtime.py
@@ -11,8 +12,9 @@ python3 build/runtime/tools/ensure-darling-runtime.py
darling shell /usr/bin/true
# The prefix has no /private/tmp of its own, so the guest falls through to the root-owned
# installed layer and every write there (the Riot installer's temp files) fails with EACCES.
mkdir -p "$HOME/.darling/private/tmp"
chmod 1777 "$HOME/.darling/private/tmp"
prefix=$prefix_dir
mkdir -p "$prefix/private/tmp"
chmod 1777 "$prefix/private/tmp"
python3 build/runtime/tools/stop-darling-runtime.py
sh build/runtime/tools/install-riot-presentation-runtime.sh
sh build/runtime/tools/install-riot-gpu-runtime.sh
@@ -23,6 +25,4 @@ if [ -f build/runtime/tools/build-riot-libs-runtime.sh ]; then
fi
python3 build/runtime/tools/install-darling-system-profiler.py
# Every guest process loads these overlays; refuse a set the compiler broke (check-darwin-tlv-calls.py).
find "$HOME/.darling/Users/$(id -un)/RuntimeLibraries" "$HOME/.darling/Users/$(id -un)/RuntimeFrameworks" \
-type f ! -name '*.before-*' ! -name '*.rt-*' ! -name '*.llvm*' -size +4k -print0 |
xargs -0 python3 build/runtime/tools/check-darwin-tlv-calls.py
python3 build/runtime/tools/validate-riot-runtime.py
+222
View File
@@ -0,0 +1,222 @@
"""Filesystem/subprocess fixtures: never invoke a host runtime or compiler."""
import importlib.util
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
VALIDATION_INPUTS = (
'RuntimeFrameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib',
'libDarlingXShm.dylib',
*(f'RuntimeLibraries/{name}.dylib' for name in (
'libsystem_kernel', 'libsystem_c', 'libsystem_platform', 'libsystem_malloc',
'libsystem_pthread', 'libobjc.A', 'libc++abi', 'libc++.1',
'libGL', 'libindium', 'libiridium')),
*(f'RuntimeFrameworks/{name}.framework/Versions/{version}/{name}' for name, version in (
('AppKit', 'C'), ('QuartzCore', 'A'), ('CoreFoundation', 'A'),
('SystemConfiguration', 'A'), ('CFNetwork', 'A'), ('IOSurface', 'A'),
('OpenGL', 'A'), ('CoreVideo', 'A'), ('CoreAudio', 'A'), ('AudioToolbox', 'A'),
('CoreGraphics', 'A'), ('CoreText', 'A'), ('Metal', 'A'), ('CarbonCore', 'A'),
('LaunchServices', 'A'))),
'RuntimeFrameworks/AppKit.framework/Versions/C/Resources/Backends/X11.backend/Contents/MacOS/X11',
'RuntimeFrameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib',
'Library/Audio/Plug-Ins/Components/DarlingRiotAudio.component/Contents/MacOS/CoreAudio',
)
class SetupFixture(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory(prefix='setup fixture ', dir=os.environ.get('TMPDIR'))
self.addCleanup(self.tmp.cleanup)
self.root = Path(self.tmp.name) / 'project space'
self.root.mkdir()
for name in ('scripts', 'tools'):
shutil.copytree(ROOT / name, self.root / name)
self.home = Path(self.tmp.name) / 'host home'
self.home.mkdir()
self.prefix = Path(self.tmp.name) / 'custom prefix'
self.guest = self.prefix / 'Users/Guest Account'
self.bin = Path(self.tmp.name) / 'bin'
self.bin.mkdir()
self.env = dict(os.environ, HOME=str(self.home), TMPDIR=self.tmp.name,
DPREFIX=str(self.prefix), PYTHONDONTWRITEBYTECODE='1',
PATH=str(self.bin) + ':' + os.environ['PATH'])
self.config = dict(project_root=str(self.root), host_home=str(self.home),
prefix=str(self.prefix), guest_user='Guest Account',
guest_home='/Users/Guest Account', sdk=str(self.root / 'sdk'), jobs=1)
(self.root / 'build').mkdir()
(self.root / 'build/config.json').write_text(json.dumps(self.config))
self.log = Path(self.tmp.name) / 'events'
self.env['FIXTURE_LOG'] = str(self.log)
def run_script(self, path, *args):
return subprocess.run([sys.executable if path.endswith('.py') else 'sh',
str(self.root / path), *args], env=self.env,
text=True, capture_output=True, timeout=20)
def configure(self):
result = self.run_script('scripts/configure.py')
self.assertEqual(result.returncode, 0, result.stderr)
def stub(self, name, body):
path = self.bin / name
path.write_text('#!/bin/sh\nset -eu\n' + body + '\n')
path.chmod(0o755)
def runtime_stubs(self):
# Nested Python is a strict allowlist: no accidental live helper execution.
self.stub('python3', '''printf 'python %s\\n' "$*" >> "$FIXTURE_LOG"
case "$1" in
-|*runtime_paths.py) exec REAL_PYTHON "$@";;
*stop-darling-runtime.py) [ "${STOP_FAIL:-0}" = 0 ] || exit 9; printf stopped\\n >> "$FIXTURE_LOG";;
*build-*.py|*ensure-darling-runtime.py|*install-darling-system-profiler.py|*install-riot-*.py) :;;
*) exit 90;;
esac'''.replace('REAL_PYTHON', sys.executable))
self.stub('darling', '''printf 'darling %s\\n' "$*" >> "$FIXTURE_LOG"
if [ "$1" = shutdown ]; then sleep "${SHUTDOWN_DELAY:-0}"; exit "${SHUTDOWN_FAIL:-0}"; fi''')
self.stub('clang', '''last=; for arg do if [ "$last" = -o ]; then printf binary > "$arg"; fi; last=$arg; done''')
self.stub('sudo', 'exit 1')
self.stub('make', ':')
self.stub('cc', 'exec clang "$@"')
self.stub('pkg-config', ':')
self.stub('install', '''case "$(cat "$FIXTURE_LOG")" in *stopped*) :;; *) exit 88;; esac
exec REAL_INSTALL "$@"'''.replace('REAL_INSTALL', shutil.which('install')))
def test_configure_replaces_readonly_and_repeats(self):
self.configure()
generated = self.root / 'build/runtime/tools/run-riot-client.sh'
generated.chmod(0o444)
self.configure()
self.configure()
self.assertTrue(generated.stat().st_mode & 0o200)
def test_configure_unlinks_dangling_symlink_without_writing_victim(self):
self.configure()
target = self.root / 'build/runtime/tools/run-riot-client.sh'
target.unlink()
victim = self.home / 'must not be created'
target.symlink_to(victim)
self.configure()
self.assertFalse(victim.exists(), 'configure followed the dangling generated-file symlink')
self.assertFalse(target.is_symlink())
def test_presentation_creates_prerequisites_and_repeat_install(self):
self.configure()
self.runtime_stubs()
self.presentation_inputs()
for _ in range(2):
result = self.run_script('build/runtime/tools/install-riot-presentation-runtime.sh')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertTrue((self.guest / 'libDarlingXShm.dylib').is_file())
self.assertTrue((self.guest / 'RuntimeLibraries/libsystem_kernel.dylib').is_file())
self.assertIn('stopped', self.log.read_text())
def presentation_inputs(self):
for name in ('external/cocotron/AppKit/AppKit', 'external/cocotron/AppKit/X11',
'external/cocotron/QuartzCore/QuartzCore',
'external/corefoundation/CoreFoundation',
'external/configd/SystemConfiguration.fproj/SystemConfiguration',
'external/cfnetwork/src/CFNetwork',
'external/libplatform/libsystem_platform.dylib',
'external/xnu/darling/src/libsystem_kernel/libsystem_kernel.dylib'):
path = self.root / 'build/darling-src/build/src' / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text('library')
for framework, version in (('AppKit', 'C'), ('QuartzCore', 'A'),
('CoreFoundation', 'A'), ('SystemConfiguration', 'A'),
('CFNetwork', 'A')):
path = self.root / 'sdk/System/Library/Frameworks' / (framework + '.framework')
binary = path / 'Versions' / version / framework
binary.parent.mkdir(parents=True)
binary.write_text('sdk binary')
binary.chmod(0o555)
(path / 'Versions/Current').symlink_to(version)
(path / framework).symlink_to('Versions/Current/' + framework)
if framework == 'AppKit':
backend = binary.parent / 'Resources/Backends/X11.backend/Contents/MacOS/X11'
backend.parent.mkdir(parents=True)
backend.write_text('sdk backend')
p = self.root / 'build/libcxx-install/lib'
p.mkdir(parents=True)
(p / 'libc++.1.0.dylib').write_text('library')
def prepare_validation(self):
self.configure()
for relative in VALIDATION_INPUTS:
path = self.guest / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(b'\xcf\xfa\xed\xfe' + b'\0' * 5000)
self.stub('llvm-objdump', '''printf '%s\\n' "$*" >> "$FIXTURE_LOG"
exit "${CHECK_FAIL:-0}"''')
def test_launcher_selects_paths_before_prefix_helpers(self):
source = (ROOT / 'tools/run-riot-client.sh').read_text()
self.assertLess(source.index('runtime-paths.sh'), source.index('ensure-darling-runtime.py'))
def test_paths_reject_symlink_escape(self):
self.configure()
outside = self.home / 'outside'
outside.mkdir()
self.prefix.mkdir(parents=True, exist_ok=True)
(self.prefix / 'escape').symlink_to(outside, target_is_directory=True)
self.env['RIOT_GUEST_HOME'] = str(self.prefix / 'escape/new-user')
result = self.run_script('build/runtime/tools/validate-riot-runtime.py')
self.assertNotEqual(result.returncode, 0)
self.assertIn('inside DPREFIX', result.stderr)
result = self.run_script('build/runtime/tools/runtime-paths.sh')
self.assertNotEqual(result.returncode, 0)
def test_validation_missing_empty_and_checker_failure(self):
self.prepare_validation()
self.env['CHECK_FAIL'] = '1'
self.assertNotEqual(self.run_script('build/runtime/tools/validate-riot-runtime.py').returncode, 0)
self.env.pop('CHECK_FAIL')
vdsp = self.guest / 'RuntimeFrameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib'
vdsp.unlink(missing_ok=True)
self.assertNotEqual(self.run_script('build/runtime/tools/validate-riot-runtime.py').returncode, 0)
shutil.rmtree(self.guest)
self.assertNotEqual(self.run_script('build/runtime/tools/validate-riot-runtime.py').returncode, 0)
def test_validation_uses_host_guest_override(self):
self.prepare_validation()
override = self.prefix / 'Users/Override Guest'
self.guest.rename(override)
self.env['RIOT_GUEST_HOME'] = str(override)
result = self.run_script('build/runtime/tools/validate-riot-runtime.py')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn(str(override), self.log.read_text())
def test_failed_stop_with_all_compile_outputs_available(self):
self.configure()
self.runtime_stubs()
self.presentation_inputs()
self.guest.mkdir(parents=True)
self.env['RIOT_GUEST_HOME'] = str(self.guest)
self.env.update(STOP_FAIL='1', SHUTDOWN_FAIL='1')
result = self.run_script('build/runtime/tools/install-riot-presentation-runtime.sh')
self.assertNotEqual(result.returncode, 0)
self.assertIn('stop-darling-runtime.py', self.log.read_text())
self.assertFalse((self.guest / 'libDarlingXShm.dylib').exists())
if __name__ == '__main__':
unittest.main()
+3
View File
@@ -51,6 +51,9 @@ def bad_accesses(path):
return found
if len(sys.argv) < 2:
sys.exit('No runtime binaries supplied for validation')
status = 0
for binary in sys.argv[1:]:
problems = bad_accesses(binary)
+4 -2
View File
@@ -1,11 +1,13 @@
#!/usr/bin/env python3
"""Install the host-backed system_profiler into the Darling prefix (no sudo)."""
import os
import json
from pathlib import Path
import shutil
from runtime_paths import runtime_paths
root = Path(__file__).resolve().parent
prefix = Path(os.environ.get("DPREFIX", str(Path.home() / ".darling")))
prefix, _ = runtime_paths(json.loads((root.parent / "build/config.json").read_text()))
target = prefix / "usr/sbin/system_profiler"
target.parent.mkdir(parents=True, exist_ok=True)
staged = target.with_name(target.name + ".new")
+1 -1
View File
@@ -3,7 +3,7 @@
# Manually exit the game and both clients, then shut down Darling before use.
set -eu
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
guest_home=${RIOT_GUEST_HOME:-$HOME/.darling@GUEST_HOME@}
. "$project_dir/tools/runtime-paths.sh"
framework="$guest_home/RuntimeFrameworks/AppKit.framework/Versions/C"
backup_dir=${RIOT_APPKIT_BACKUP_DIR:-$project_dir/build/appkit-runtime-backups}
# Preflight both artifacts and destinations before replacing either binary.
+2 -2
View File
@@ -3,8 +3,7 @@
# Exit the game and both clients, then run darling shutdown before this script.
set -eu
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
prefix_dir=${DPREFIX:-@DPREFIX@}
guest_home=${RIOT_GUEST_HOME:-$prefix_dir@GUEST_HOME@}
. "$project_dir/tools/runtime-paths.sh"
artifact=${RIOT_CARBONCORE_ARTIFACT:-$project_dir/build/riot-CarbonCore}
backup_dir=${RIOT_CARBONCORE_BACKUP_DIR:-$project_dir/build/carboncore-runtime-backups}
sdk_framework="@DARLING_SDK@/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework"
@@ -12,6 +11,7 @@ framework="$guest_home/RuntimeFrameworks/CarbonCore.framework"
prefix_binary="$prefix_dir/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore"
[ -f "$artifact" ] || { echo "Build CarbonCore first: $artifact" >&2; exit 1; }
[ -f "$sdk_framework/Versions/A/CarbonCore" ] || { echo "Missing SDK CarbonCore framework" >&2; exit 1; }
python3 "$project_dir/tools/stop-darling-runtime.py"
mkdir -p "$guest_home/RuntimeFrameworks" "$backup_dir"
if [ ! -d "$framework" ]; then
cp -a "$sdk_framework" "$framework"
+5 -4
View File
@@ -17,7 +17,9 @@ import shutil
import stat
import tempfile
ROOT = Path(__file__).resolve().parent.parent
from runtime_paths import runtime_paths
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_ARTIFACT = ROOT / "build/riot-Foundation"
@@ -110,9 +112,8 @@ def main():
parser.exit(1, "The library to install does not exist: " + str(args.binary) + "; build it first: "
"python3 build/runtime/tools/build-riot-foundation-runtime.py\n")
config = json.loads((ROOT / "build/config.json").read_text())
prefix = Path(config["prefix"])
guest_home = config["guest_home"].lstrip("/")
framework = prefix / guest_home / "RuntimeFrameworks/Foundation.framework"
_, guest_home = runtime_paths(config)
framework = guest_home / "RuntimeFrameworks/Foundation.framework"
sdk_framework = Path(config["sdk"]) / "System/Library/Frameworks/Foundation.framework"
if not sdk_framework.exists():
parser.exit(1, "The SDK framework does not exist: " + str(sdk_framework) + "\n")
+14 -13
View File
@@ -2,8 +2,9 @@
# Build/install the paired GPU, NV12 and display-clock runtime overrides.
set -eu
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
guest_home=${RIOT_GUEST_HOME:-$HOME/.darling@GUEST_HOME@}
. "$project_dir/tools/runtime-paths.sh"
cd "$project_dir"
mkdir -p build
python3 tools/build-m58f333-iosurface.py
# Rebuild the native wrapper with floating-point-safe symbol resolvers.
make -C build/darling-src/build/src/native GL -j4
@@ -31,6 +32,7 @@ python3 tools/build-riot-server-runtime.py
build/riot-host-message-check
cc -shared -fPIC -O2 -Wall -Wextra -Wl,-soname,"$project_dir/build/libdarling-xshm.so" tools/m58f299-xshm.c -o build/libdarling-xshm.so.next -pthread $(pkg-config --cflags --libs x11 x11-xcb xcb-shm xext)
python3 tools/stop-darling-runtime.py
mkdir -p "$guest_home/RuntimeFrameworks"
# Host fileports own SCM_RIGHTS descriptors and must die with their last sender.
if sudo -n true 2>/dev/null; then
sudo make -C "$project_dir/build/darling-src/build/src/external/darlingserver" install -j12
@@ -70,7 +72,7 @@ mkdir -p "$guest_home/RuntimeLibraries"
gl_wrapper="$guest_home/RuntimeLibraries/libGL.dylib"
if [ ! -f "$gl_wrapper.before-float-resolver" ]; then
if [ -f "$gl_wrapper" ]; then cp -p "$gl_wrapper" "$gl_wrapper.before-float-resolver"
else cp -p @DARLING_SDK@/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib "$gl_wrapper.before-float-resolver"; fi
else cp -p "@DARLING_SDK@"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib "$gl_wrapper.before-float-resolver"; fi
fi
install -m755 build/darling-src/build/src/native/libGL.dylib "$gl_wrapper.next"
mv "$gl_wrapper.next" "$gl_wrapper"
@@ -78,7 +80,7 @@ install -m755 build/darling-src/build/src/native/libGL.dylib "$guest_home/Runtim
mv "$guest_home/RuntimeFrameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib.next" "$guest_home/RuntimeFrameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
launchservices="$guest_home/RuntimeFrameworks/LaunchServices.framework"
if [ ! -d "$launchservices" ]; then
cp -a @DARLING_SDK@/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework "$launchservices"
cp -a "@DARLING_SDK@"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework "$launchservices"
fi
launchservices_binary="$launchservices/Versions/A/LaunchServices"
if [ ! -f "$launchservices_binary.before-null-error" ]; then cp -p "$launchservices_binary" "$launchservices_binary.before-null-error"; fi
@@ -86,12 +88,11 @@ install -m755 build/darling-src/build/src/frameworks/CoreServices/src/LaunchServ
mv "$launchservices_binary.next" "$launchservices_binary"
# Electron opens this subframework by its absolute path, bypassing the
# framework search override. Replace the same binary in the stopped prefix.
prefix_dir="${DPREFIX:-$HOME/.darling}"
launchservices_prefix="$prefix_dir/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices"
mkdir -p "$(dirname "$launchservices_prefix")"
if [ ! -f "$launchservices_prefix.before-null-error" ]; then
if [ -f "$launchservices_prefix" ]; then cp -p "$launchservices_prefix" "$launchservices_prefix.before-null-error"
else cp -p @DARLING_SDK@/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices "$launchservices_prefix.before-null-error"; fi
else cp -p "@DARLING_SDK@"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices "$launchservices_prefix.before-null-error"; fi
fi
install -m755 build/darling-src/build/src/frameworks/CoreServices/src/LaunchServices/LaunchServices "$launchservices_prefix.next"
mv "$launchservices_prefix.next" "$launchservices_prefix"
@@ -100,7 +101,7 @@ vdsp_prefix="$prefix_dir/System/Library/Frameworks/Accelerate.framework/Versions
mkdir -p "$(dirname "$vdsp_prefix")"
if [ ! -f "$vdsp_prefix.before-vdsp" ]; then
if [ -f "$vdsp_prefix" ]; then cp -p "$vdsp_prefix" "$vdsp_prefix.before-vdsp"
else cp -p @DARLING_SDK@/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib "$vdsp_prefix.before-vdsp"; fi
else cp -p "@DARLING_SDK@"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib "$vdsp_prefix.before-vdsp"; fi
fi
install -m755 build/darling-src/build/src/frameworks/Accelerate/vecLib/vDSP/libvDSP.dylib "$vdsp_prefix.next"
mv "$vdsp_prefix.next" "$vdsp_prefix"
@@ -108,7 +109,7 @@ mv "$vdsp_prefix.next" "$vdsp_prefix"
component="$guest_home/Library/Audio/Plug-Ins/Components/DarlingRiotAudio.component"
if [ ! -d "$component" ]; then
mkdir -p "$(dirname "$component")"
cp -a @DARLING_SDK@/System/Library/Components/CoreAudio.component "$component"
cp -a "@DARLING_SDK@"/System/Library/Components/CoreAudio.component "$component"
fi
component_binary="$component/Contents/MacOS/CoreAudio"
if [ ! -f "$component_binary.before-gpu-direct" ]; then cp -p "$component_binary" "$component_binary.before-gpu-direct"; fi
@@ -139,7 +140,7 @@ mv "$kernel.next" "$kernel"
allocator="$guest_home/RuntimeLibraries/libsystem_malloc.dylib"
if [ ! -f "$allocator.before-gpu-direct" ]; then
if [ -f "$allocator" ]; then cp -p "$allocator" "$allocator.before-gpu-direct"
else cp -p @DARLING_SDK@/usr/lib/system/libsystem_malloc.dylib "$allocator.before-gpu-direct"; fi
else cp -p "@DARLING_SDK@"/usr/lib/system/libsystem_malloc.dylib "$allocator.before-gpu-direct"; fi
fi
install -m755 build/riot-libsystem_malloc.dylib "$allocator.next"
mv "$allocator.next" "$allocator"
@@ -149,7 +150,7 @@ mv build/libdarling-xshm.so.next build/libdarling-xshm.so
objc_library="$guest_home/RuntimeLibraries/libobjc.A.dylib"
if [ ! -f "$objc_library.before-m761" ]; then
if [ -f "$objc_library" ]; then cp -p "$objc_library" "$objc_library.before-m761"
else cp -p @DARLING_SDK@/usr/lib/libobjc.A.dylib "$objc_library.before-m761"; fi
else cp -p "@DARLING_SDK@"/usr/lib/libobjc.A.dylib "$objc_library.before-m761"; fi
fi
install -m755 build/riot-libobjc.A.dylib "$objc_library.next"
mv "$objc_library.next" "$objc_library"
@@ -157,7 +158,7 @@ mv "$objc_library.next" "$objc_library"
cxxabi_library="$guest_home/RuntimeLibraries/libc++abi.dylib"
if [ ! -f "$cxxabi_library.before-m765" ]; then
if [ -f "$cxxabi_library" ]; then cp -p "$cxxabi_library" "$cxxabi_library.before-m765"
else cp -p @DARLING_SDK@/usr/lib/libc++abi.dylib "$cxxabi_library.before-m765"; fi
else cp -p "@DARLING_SDK@"/usr/lib/libc++abi.dylib "$cxxabi_library.before-m765"; fi
fi
install -m755 build/riot-libc++abi.dylib "$cxxabi_library.next"
mv "$cxxabi_library.next" "$cxxabi_library"
@@ -165,7 +166,7 @@ mv "$cxxabi_library.next" "$cxxabi_library"
indium_library="$guest_home/RuntimeLibraries/libindium.dylib"
if [ ! -f "$indium_library.before-m766" ]; then
if [ -f "$indium_library" ]; then cp -p "$indium_library" "$indium_library.before-m766"
else cp -p @DARLING_SDK@/usr/lib/darling/libindium.dylib "$indium_library.before-m766"; fi
else cp -p "@DARLING_SDK@"/usr/lib/darling/libindium.dylib "$indium_library.before-m766"; fi
fi
install -m755 build/riot-libindium.dylib "$indium_library.next"
mv "$indium_library.next" "$indium_library"
@@ -174,7 +175,7 @@ mv "$indium_library.next" "$indium_library"
iridium_library="$guest_home/RuntimeLibraries/libiridium.dylib"
if [ ! -f "$iridium_library.before-m767" ]; then
if [ -f "$iridium_library" ]; then cp -p "$iridium_library" "$iridium_library.before-m767"
else cp -p @DARLING_SDK@/usr/lib/darling/libiridium.dylib "$iridium_library.before-m767"; fi
else cp -p "@DARLING_SDK@"/usr/lib/darling/libiridium.dylib "$iridium_library.before-m767"; fi
fi
install -m755 build/riot-libiridium.dylib "$iridium_library.next"
mv "$iridium_library.next" "$iridium_library"
@@ -182,7 +183,7 @@ mv "$iridium_library.next" "$iridium_library"
pthread_library="$guest_home/RuntimeLibraries/libsystem_pthread.dylib"
if [ ! -f "$pthread_library.before-gpu-direct" ]; then
if [ -f "$pthread_library" ]; then cp -p "$pthread_library" "$pthread_library.before-gpu-direct"
else cp -p @DARLING_SDK@/usr/lib/system/libsystem_pthread.dylib "$pthread_library.before-gpu-direct"; fi
else cp -p "@DARLING_SDK@"/usr/lib/system/libsystem_pthread.dylib "$pthread_library.before-gpu-direct"; fi
fi
install -m755 build/riot-libsystem_pthread.dylib "$pthread_library.next"
mv "$pthread_library.next" "$pthread_library"
+11 -4
View File
@@ -23,7 +23,9 @@ import shutil
import stat
import tempfile
ROOT = Path(__file__).resolve().parent.parent
from runtime_paths import runtime_paths
ROOT = Path(__file__).resolve().parents[1]
# (built artifact, file name in the guest's RuntimeLibraries directory). The
# artifacts are the ones tools/build-riot-libs-runtime.sh builds.
@@ -91,6 +93,7 @@ def install(libraries, backup_folder):
else:
shutil.copy2(destination, backup)
metadata = destination.stat() if destination.is_file() else None
destination.parent.mkdir(parents=True, exist_ok=True)
descriptor, temporary = tempfile.mkstemp(prefix="." + destination.name + "-staged-",
dir=destination.parent)
staged.append((Path(temporary), destination))
@@ -186,9 +189,13 @@ def main(argv=None):
parser.exit(1, str(error) + "\n")
return
config = json.loads((ROOT / "build/config.json").read_text())
prefix = arguments.prefix or config["prefix"]
guest_home = arguments.guest_home or config["guest_home"]
libraries = destinations(prefix, guest_home)
prefix, host_guest_home = runtime_paths(config)
prefix = arguments.prefix or prefix
if arguments.prefix and not os.environ.get("RIOT_GUEST_HOME"):
host_guest_home = prefix / config["guest_home"].lstrip("/")
if arguments.guest_home:
host_guest_home = prefix / arguments.guest_home.lstrip("/")
libraries = destinations(host_guest_home, "")
if arguments.only:
wanted = set(arguments.only.split(","))
unknown = wanted - {name for _, name in LIBRARIES}
+6 -3
View File
@@ -10,7 +10,9 @@ import shutil
import stat
import tempfile
ROOT = Path(__file__).resolve().parent.parent
from runtime_paths import runtime_paths
ROOT = Path(__file__).resolve().parents[1]
# The optimized relink from tools/build-riot-kqueue-runtime.py is the library the
# client runtime is supposed to run; the plain CMake build (no -O flag) is not.
DEFAULT_ARTIFACT = ROOT / "build/riot-libsystem_kernel.dylib"
@@ -84,10 +86,11 @@ def prefix_destinations(config, binary, artifact):
install the artifact itself leaves it untouched. That is what keeps the
optimized relink from being silently replaced by the plain CMake build.
"""
prefix, guest_home = runtime_paths(config)
destinations = [
Path(config["prefix"]) / config["guest_home"].lstrip("/") / "RuntimeLibraries/libsystem_kernel.dylib",
guest_home / "RuntimeLibraries/libsystem_kernel.dylib",
# Keep the per-prefix bootstrap copy consistent; leave the system SDK alone.
Path(config["prefix"]) / "usr/lib/system/libsystem_kernel.dylib",
prefix / "usr/lib/system/libsystem_kernel.dylib",
]
if not artifact.exists():
return destinations, "the prepared artifact " + str(artifact) + " does not exist yet"
+8 -6
View File
@@ -4,7 +4,8 @@ set -eu
project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
source_dir="$project_dir/build/darling-src"
build_dir="$source_dir/build"
guest_home="${RIOT_GUEST_HOME:-$HOME/.darling@GUEST_HOME@}"
. "$project_dir/tools/runtime-paths.sh"
mkdir -p "$project_dir/build"
make -C "$build_dir/src/external/xnu/darling/src/libsystem_kernel" system_kernel -j8
make -C "$build_dir/src/external/libplatform" platform -j8
make -C "$build_dir/src/external/corefoundation" CoreFoundation_x86_64 -j8
@@ -17,14 +18,16 @@ cc -shared -fPIC -O3 -march=native -mtune=generic -Wall -Wextra \
"$project_dir/tools/m58f299-xshm.c" -o "$project_dir/build/libdarling-xshm.next.so" \
-pthread $(pkg-config --cflags --libs x11 x11-xcb xcb-shm xext)
clang -target x86_64-apple-macosx11.0 -fuse-ld=lld -dynamiclib -nostdlib -O3 -march=native -mtune=generic \
-isysroot @DARLING_SDK@ \
-isysroot "@DARLING_SDK@" \
-I"$source_dir/src/startup/mldr/elfcalls" \
"-DDARLING_XSHM_HOST_LIBRARY=\"$project_dir/build/libdarling-xshm.so\"" \
-Wl,-undefined,dynamic_lookup -Wl,-install_name,@GUEST_HOME@/libDarlingXShm.dylib \
"$project_dir/tools/m58f299-native-wrapper.c" -o "$guest_home/libDarlingXShm.next.dylib"
-Wl,-undefined,dynamic_lookup "-Wl,-install_name,@GUEST_HOME@/libDarlingXShm.dylib" \
"$project_dir/tools/m58f299-native-wrapper.c" -o "$project_dir/build/libDarlingXShm.next.dylib"
# Stop before changing the overlay's upper directory, even with atomic writes.
darling shutdown || true
python3 "$project_dir/tools/stop-darling-runtime.py"
mkdir -p "$guest_home"
mv "$project_dir/build/libdarling-xshm.next.so" "$project_dir/build/libdarling-xshm.so"
install -m755 "$project_dir/build/libDarlingXShm.next.dylib" "$guest_home/libDarlingXShm.next.dylib"
mv "$guest_home/libDarlingXShm.next.dylib" "$guest_home/libDarlingXShm.dylib"
mkdir -p "$guest_home/RuntimeFrameworks"
for framework in AppKit QuartzCore CoreFoundation SystemConfiguration CFNetwork; do
@@ -52,7 +55,6 @@ atomic_install "$build_dir/src/external/xnu/darling/src/libsystem_kernel/libsyst
# Bootstrap services can start before the app's DYLD_LIBRARY_PATH is applied.
# The prefix overlay is user-owned; install the same corrected kernel there.
prefix_dir="${DPREFIX:-$HOME/.darling}"
mkdir -p "$prefix_dir/usr/lib/system"
atomic_install "$build_dir/src/external/xnu/darling/src/libsystem_kernel/libsystem_kernel.dylib" "$prefix_dir/usr/lib/system/libsystem_kernel.dylib"
+5 -1
View File
@@ -39,6 +39,10 @@ with open({str(log)!r}, "a") as output:
prefix = folder / "prefix"
(prefix / "Applications/Riot Client.app").mkdir(parents=True)
env["DPREFIX"] = str(prefix)
env.pop("RIOT_GUEST_HOME", None)
(folder / "runtime-paths.sh").write_text(
(ROOT / "tools/runtime-paths.sh").read_text()
.replace("@DPREFIX@", str(prefix)).replace("@GUEST_HOME@", "/Users/offline-check"))
env["DYLD_INSERT_LIBRARIES"] = "/Users/offline-check/zzlogdir.dylib:/tmp/inherited.dylib"
env["RCLOL_APP_ARGS"] = "--inherited-shim-argument"
env["RCLOL_APP_ARG2"] = "--inherited-shim-argument-2"
@@ -60,7 +64,7 @@ with open({str(log)!r}, "a") as output:
records = [json.loads(line) for line in log.read_text().splitlines()]
maintenance = [r for r in records if r["command"] == "python3"]
assert [Path(r["args"][0]).name for r in maintenance] == [
"ensure-darling-runtime.py", "ensure-darling-service-accounts.py", "ensure-league-runtime-layout.py"
"runtime_paths.py", "ensure-darling-runtime.py", "ensure-darling-service-accounts.py", "ensure-league-runtime-layout.py"
], "unexpected automatic helper"
launches = [r for r in records if r["command"] == "darling"]
assert len(launches) == 1, "unexpected launch count"
+11 -5
View File
@@ -8,6 +8,7 @@ if [ "${RCLOL_CORE_DUMPS:-0}" != 1 ]; then
ulimit -c 0
fi
runner_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
. "$runner_dir/runtime-paths.sh"
python3 "$runner_dir/ensure-darling-runtime.py"
python3 "$runner_dir/ensure-darling-service-accounts.py"
python3 "$runner_dir/ensure-league-runtime-layout.py"
@@ -21,7 +22,12 @@ for option in RCLOL_GPU_DIRECT_PRESENT RCLOL_GPU_DIRECT_ASYNC RCLOL_GPU_DIRECT_V
eval 'value=${'"$option"'-1}'
case "$value" in 0|'') unset "$option";; *) export "$option=$value";; esac
done
prefix=${DPREFIX:-$HOME/.darling}
prefix=$prefix_dir
# RIOT_GUEST_HOME is host-side; DYLD paths must be relative to the guest root.
case "$guest_home" in
"$prefix"/*) guest_path=/${guest_home#"$prefix"/} ;;
*) echo "RIOT_GUEST_HOME must be inside DPREFIX" >&2; exit 1 ;;
esac
# The first-time installer puts the client in /Users/Shared/Riot Games and then
# hands off through LaunchServices, which does not start it under Darling. Link
# the installed client where the normal launch expects it.
@@ -85,9 +91,9 @@ exec env -u ELECTRON_RUN_AS_NODE -u DYLD_INSERT_LIBRARIES EGL_PLATFORM=x11 \
LANG="${RCLOL_GUEST_LOCALE:-en_US.UTF-8}" LC_ALL="${RCLOL_GUEST_LOCALE:-en_US.UTF-8}" \
mesa_glthread="${RCLOL_MESA_GLTHREAD-${mesa_glthread-false}}" \
XMODIFIERS="${RCLOL_XMODIFIERS:-@im=none}" DISPLAY="${DISPLAY:-:1}" \
DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH:-@GUEST_HOME@/RuntimeLibraries}" \
DYLD_FRAMEWORK_PATH="${DYLD_FRAMEWORK_PATH:-@GUEST_HOME@/RuntimeFrameworks}" \
DARLING_XSHM_LIBRARY="${DARLING_XSHM_LIBRARY:-@GUEST_HOME@/libDarlingXShm.dylib}" \
DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH:-$guest_path/RuntimeLibraries}" \
DYLD_FRAMEWORK_PATH="${DYLD_FRAMEWORK_PATH:-$guest_path/RuntimeFrameworks}" \
DARLING_XSHM_LIBRARY="${DARLING_XSHM_LIBRARY:-$guest_path/libDarlingXShm.dylib}" \
darling "$A" --region="${RCLOL_REGION:-EUW}" --locale="${RCLOL_LOCALE:-en_GB}" \
"$@" \
--log-dir="@GUEST_HOME@/Library/Logs/Riot Games/Riot Client"
--log-dir="$guest_path/Library/Logs/Riot Games/Riot Client"
+18
View File
@@ -0,0 +1,18 @@
# Sourced by configured runtime installers; RIOT_GUEST_HOME is a host path.
# Keep these defaults aligned with runtime_paths.py.
prefix_dir=${DPREFIX:-"@DPREFIX@"}
guest_home=${RIOT_GUEST_HOME:-"$prefix_dir@GUEST_HOME@"}
case "$prefix_dir" in
/*) : ;;
*) echo "DPREFIX must be an absolute host path" >&2; exit 1 ;;
esac
prefix_dir=${prefix_dir%/}
case "$guest_home" in
"$prefix_dir"/*) : ;;
*) echo "RIOT_GUEST_HOME must be inside DPREFIX" >&2; exit 1 ;;
esac
case "$guest_home/" in
*/../*|*/./*) echo "RIOT_GUEST_HOME must not contain dot components" >&2; exit 1 ;;
esac
export DPREFIX="$prefix_dir" RIOT_GUEST_HOME="$guest_home"
python3 "@PROJECT_ROOT@/build/runtime/tools/runtime_paths.py" "$prefix_dir" || exit 1
+24
View File
@@ -0,0 +1,24 @@
"""Shared host-side destinations for configured runtime installers."""
import os
from pathlib import Path
def runtime_paths(config):
prefix = Path(os.environ.get("DPREFIX") or config["prefix"])
guest_home = Path(os.environ.get("RIOT_GUEST_HOME") or
str(prefix / config["guest_home"].lstrip("/")))
if not prefix.is_absolute() or not guest_home.is_absolute():
raise ValueError("Runtime paths must be absolute host paths")
if ".." in guest_home.parts or not guest_home.is_relative_to(prefix) or guest_home == prefix:
raise ValueError("RIOT_GUEST_HOME must be inside DPREFIX")
if not guest_home.resolve().is_relative_to(prefix.resolve()) or guest_home.resolve() == prefix.resolve():
raise ValueError("RIOT_GUEST_HOME must resolve inside DPREFIX")
return prefix, guest_home
if __name__ == '__main__':
import sys
try:
runtime_paths({'prefix': sys.argv[1], 'guest_home': '/'},)
except (ValueError, OSError) as error:
raise SystemExit(str(error))
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""Validate installed overlays, refusing missing, empty or unreadable inputs."""
import json
import os
from pathlib import Path
import subprocess
import sys
from runtime_paths import runtime_paths
ROOT = Path(__file__).resolve().parents[1]
REQUIRED = (
'RuntimeFrameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib',
'libDarlingXShm.dylib',
*(f'RuntimeLibraries/{name}' for name in (
'libsystem_kernel.dylib', 'libsystem_c.dylib', 'libsystem_platform.dylib',
'libsystem_malloc.dylib', 'libsystem_pthread.dylib',
'libobjc.A.dylib', 'libc++abi.dylib', 'libc++.1.dylib',
'libGL.dylib', 'libindium.dylib', 'libiridium.dylib')),
*(f'RuntimeFrameworks/{name}.framework/Versions/{version}/{name}'
for name, version in (
('AppKit', 'C'), ('QuartzCore', 'A'), ('CoreFoundation', 'A'),
('SystemConfiguration', 'A'), ('CFNetwork', 'A'), ('IOSurface', 'A'),
('OpenGL', 'A'), ('CoreVideo', 'A'), ('CoreAudio', 'A'),
('AudioToolbox', 'A'), ('CoreGraphics', 'A'), ('CoreText', 'A'),
('Metal', 'A'), ('CarbonCore', 'A'), ('LaunchServices', 'A'))),
'RuntimeFrameworks/AppKit.framework/Versions/C/Resources/Backends/X11.backend/Contents/MacOS/X11',
'RuntimeFrameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib',
'Library/Audio/Plug-Ins/Components/DarlingRiotAudio.component/Contents/MacOS/CoreAudio',
)
MACHO = (b'\xcf\xfa\xed\xfe', b'\xca\xfe\xba\xbe')
def inputs(guest_home):
def enumeration_error(error):
raise error
def is_macho(path):
with path.open('rb') as source:
return source.read(4) in MACHO
required = [guest_home / relative for relative in REQUIRED]
for path in required:
if not is_macho(path):
raise ValueError('Expected installed Mach-O runtime binary: ' + str(path))
binaries = set(required)
for name in ('RuntimeLibraries', 'RuntimeFrameworks'):
folder = guest_home / name
if not folder.is_dir():
raise ValueError('Missing runtime directory: ' + str(folder))
found = []
for directory, _, files in os.walk(folder, onerror=enumeration_error):
for name in files:
if any(marker in name for marker in ('.before-', '.rt-', '.llvm')):
continue
path = Path(directory) / name
if is_macho(path):
found.append(path)
if not found:
raise ValueError('No Mach-O runtime binaries found in ' + str(folder))
binaries.update(found)
return sorted(binaries)
def main():
try:
_, guest_home = runtime_paths(json.loads((ROOT / 'build/config.json').read_text()))
binaries = inputs(guest_home)
subprocess.run([sys.executable, str(ROOT / 'tools/check-darwin-tlv-calls.py'),
*map(str, binaries)], check=True)
except (OSError, ValueError, subprocess.CalledProcessError) as error:
print('Runtime validation failed: ' + str(error), file=sys.stderr)
return 1
return 0
if __name__ == '__main__':
raise SystemExit(main())