mirror of
https://github.com/lutris/lutris
synced 2024-11-02 11:48:38 +00:00
First cut at version compatibility restrictions
This code looks for a '.lutris_compat.json' file at the root of a component's version's directory. This contains, in JSON, the minimum Lutris version required. If Lutris finds this to be too late, it will try earlier versions. The code is messy and this could download a lot of versions, since it tries them one at a time. But it's a start.
This commit is contained in:
parent
567c9a99b5
commit
14ae02536e
5 changed files with 74 additions and 29 deletions
|
@ -16,7 +16,7 @@ from lutris.util.display import DISPLAY_MANAGER, get_default_dpi
|
|||
from lutris.util.graphics import vkquery
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.steam.config import get_steam_dir
|
||||
from lutris.util.strings import parse_version, split_arguments
|
||||
from lutris.util.strings import split_arguments
|
||||
from lutris.util.wine.d3d_extras import D3DExtrasManager
|
||||
from lutris.util.wine.dgvoodoo2 import dgvoodoo2Manager
|
||||
from lutris.util.wine.dxvk import REQUIRED_VULKAN_API_VERSION, DXVKManager
|
||||
|
@ -28,7 +28,7 @@ from lutris.util.wine.wine import (
|
|||
POL_PATH, WINE_DIR, WINE_PATHS, detect_arch, display_vulkan_error, esync_display_limit_warning,
|
||||
esync_display_version_warning, fsync_display_support_warning, fsync_display_version_warning, get_default_version,
|
||||
get_overrides_env, get_proton_paths, get_real_executable, get_wine_version, get_wine_versions, is_esync_limit_set,
|
||||
is_fsync_supported, is_gstreamer_build, is_version_esync, is_version_fsync
|
||||
is_fsync_supported, is_gstreamer_build, is_version_esync, is_version_fsync, parse_wine_version
|
||||
)
|
||||
|
||||
DEFAULT_WINE_PREFIX = "~/.wine"
|
||||
|
@ -729,9 +729,9 @@ class wine(Runner):
|
|||
|
||||
wine_versions = get_wine_versions()
|
||||
if min_version:
|
||||
min_version_list, _, _ = parse_version(min_version)
|
||||
min_version_list, _, _ = parse_wine_version(min_version)
|
||||
for wine_version in wine_versions:
|
||||
version_list, _, _ = parse_version(wine_version)
|
||||
version_list, _, _ = parse_wine_version(wine_version)
|
||||
if version_list > min_version_list:
|
||||
return True
|
||||
logger.warning("Wine %s or higher not found", min_version)
|
||||
|
|
|
@ -69,8 +69,6 @@ def parse_version(version):
|
|||
Returns:
|
||||
tuple: (version number as list, prefix, suffix)
|
||||
"""
|
||||
version = version.replace("Proton7-", "Proton-7.")
|
||||
version = version.replace("Proton8-", "Proton-8.")
|
||||
version_match = re.search(r"(\d[\d\.]+\d)", version)
|
||||
if not version_match:
|
||||
return [], "", ""
|
||||
|
@ -80,19 +78,6 @@ def parse_version(version):
|
|||
return [int(p) for p in version_number.split(".")], suffix, prefix
|
||||
|
||||
|
||||
def version_sort(versions, reverse=False):
|
||||
|
||||
def version_key(version):
|
||||
version_list, prefix, suffix = parse_version(version)
|
||||
# Normalize the length of sub-versions
|
||||
sort_key = version_list + [0] * (10 - len(version_list))
|
||||
sort_key.append(prefix)
|
||||
sort_key.append(suffix)
|
||||
return sort_key
|
||||
|
||||
return sorted(versions, key=version_key, reverse=reverse)
|
||||
|
||||
|
||||
def unpack_dependencies(string):
|
||||
"""Parse a string to allow for complex dependencies
|
||||
Works in a similar fashion as Debian dependencies, separate dependencies
|
||||
|
|
|
@ -4,10 +4,12 @@ import os
|
|||
import shutil
|
||||
from gettext import gettext as _
|
||||
|
||||
from lutris import settings
|
||||
from lutris.util import system
|
||||
from lutris.util.extract import extract_archive
|
||||
from lutris.util.http import download_file
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.strings import parse_version
|
||||
from lutris.util.wine.prefix import WinePrefixManager
|
||||
|
||||
|
||||
|
@ -50,10 +52,35 @@ class DLLManager:
|
|||
recommended_versions = [v for v in versions if self.is_recommended_version(v)]
|
||||
return recommended_versions[0] if recommended_versions else versions[0]
|
||||
|
||||
def get_recommended_versions(self):
|
||||
"""Returns a list of version numbers that are recommended, based on the versions JSON file;
|
||||
merely having a directory does not count, but we do return only recommended versions. This
|
||||
means that a version can be recommended until it is downloaded, and then if it has a
|
||||
'.lutris_compat.json' file it may cease to be recommended. The DLL download code retries
|
||||
with an earlier version if this happens.
|
||||
|
||||
This list is in the usual highest-version-first order, and we try the downloads in that order.
|
||||
"""
|
||||
versions = self.load_versions()
|
||||
return [v for v in versions if self.is_recommended_version(v)]
|
||||
|
||||
def is_recommended_version(self, version):
|
||||
"""True if the version given should be usable as the default; false if it
|
||||
should not be the default, but may be selected by the user. If only
|
||||
non-recommended versions exist, we'll still default to one of them, however."""
|
||||
non-recommended versions exist, we'll still default to one of them, however.
|
||||
|
||||
By default, we check for a '.lutris_compat.json' file; if this Lutris is
|
||||
too old, we'll reject the version."""
|
||||
path = os.path.join(self.base_dir, version, ".lutris_compat.json")
|
||||
if os.path.isfile(path):
|
||||
with open(path, "r", encoding='utf-8') as json_file:
|
||||
js = json.load(json_file)
|
||||
if "min_lutris_version" in js:
|
||||
min_lutris_version = parse_version(js["min_lutris_version"])
|
||||
current_lutris_version = parse_version(settings.VERSION)
|
||||
if current_lutris_version < min_lutris_version:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
|
@ -271,8 +298,19 @@ class DLLManager:
|
|||
def upgrade(self):
|
||||
self.fetch_versions()
|
||||
if not self.is_available():
|
||||
if self.version:
|
||||
logger.info("Downloading %s %s...", self.component, self.version)
|
||||
versions = self.get_recommended_versions()
|
||||
|
||||
while versions:
|
||||
# Try to download the latest recommended version.
|
||||
version = versions[0]
|
||||
logger.info("Downloading %s %s...", self.component, version)
|
||||
self.download()
|
||||
else:
|
||||
logger.warning("Unable to download %s because version information was not available.", self.component)
|
||||
|
||||
# If the version is still recommended, we're done,
|
||||
# if not we'll try again with the next one.
|
||||
new_versions = self.get_recommended_versions()
|
||||
if version in new_versions:
|
||||
return
|
||||
versions = new_versions
|
||||
|
||||
logger.warning("Unable to download %s because version information was not available.", self.component)
|
||||
|
|
|
@ -10,7 +10,7 @@ from lutris.gui.dialogs import DontShowAgainDialog, ErrorDialog
|
|||
from lutris.runners.steam import steam
|
||||
from lutris.util import linux, system
|
||||
from lutris.util.log import logger
|
||||
from lutris.util.strings import version_sort
|
||||
from lutris.util.strings import parse_version
|
||||
from lutris.util.wine import fsync
|
||||
|
||||
WINE_DIR = os.path.join(settings.RUNNER_DIR, "wine")
|
||||
|
@ -236,6 +236,27 @@ def get_wine_version_exe(version):
|
|||
return os.path.join(WINE_DIR, "{}/bin/wine".format(version))
|
||||
|
||||
|
||||
def parse_wine_version(version):
|
||||
"""This is a specialized parse_version() that adjusts some odd
|
||||
Wine versions for correct parsing."""
|
||||
version = version.replace("Proton7-", "Proton-7.")
|
||||
version = version.replace("Proton8-", "Proton-8.")
|
||||
return parse_version(version)
|
||||
|
||||
|
||||
def version_sort(versions, reverse=False):
|
||||
|
||||
def version_key(version):
|
||||
version_list, prefix, suffix = parse_wine_version(version)
|
||||
# Normalize the length of sub-versions
|
||||
sort_key = version_list + [0] * (10 - len(version_list))
|
||||
sort_key.append(prefix)
|
||||
sort_key.append(suffix)
|
||||
return sort_key
|
||||
|
||||
return sorted(versions, key=version_key, reverse=reverse)
|
||||
|
||||
|
||||
def is_version_installed(version):
|
||||
return os.path.isfile(get_wine_version_exe(version))
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import os
|
|||
from collections import OrderedDict
|
||||
from unittest import TestCase
|
||||
|
||||
from lutris.util.wine import wine
|
||||
from lutris.util import fileio, strings, system
|
||||
from lutris.util.steam import vdfutils
|
||||
|
||||
|
@ -96,11 +97,11 @@ class TestStringUtils(TestCase):
|
|||
|
||||
class TestVersionSort(TestCase):
|
||||
def test_parse_version(self):
|
||||
self.assertEqual(strings.parse_version("3.6-staging"), ([3, 6], '-staging', ''))
|
||||
self.assertEqual(wine.parse_wine_version("3.6-staging"), ([3, 6], '-staging', ''))
|
||||
|
||||
def test_versions_are_correctly_sorted(self):
|
||||
versions = ['1.8', '1.7.4', '1.9.1', '1.9.10', '1.9.4']
|
||||
versions = strings.version_sort(versions)
|
||||
versions = wine.version_sort(versions)
|
||||
self.assertEqual(versions[0], '1.7.4')
|
||||
self.assertEqual(versions[1], '1.8')
|
||||
self.assertEqual(versions[2], '1.9.1')
|
||||
|
@ -114,7 +115,7 @@ class TestVersionSort(TestCase):
|
|||
'1.9.10-staging', '1.9.10',
|
||||
'1.9.4', 'staging-1.9.4'
|
||||
]
|
||||
versions = strings.version_sort(versions)
|
||||
versions = wine.version_sort(versions)
|
||||
self.assertEqual(versions[0], '1.7.4')
|
||||
self.assertEqual(versions[1], '1.8')
|
||||
self.assertEqual(versions[2], '1.8-staging')
|
||||
|
@ -126,7 +127,7 @@ class TestVersionSort(TestCase):
|
|||
|
||||
def test_versions_can_be_reversed(self):
|
||||
versions = ['1.9', '1.6', '1.7', '1.8']
|
||||
versions = strings.version_sort(versions, reverse=True)
|
||||
versions = wine.version_sort(versions, reverse=True)
|
||||
self.assertEqual(versions[0], '1.9')
|
||||
self.assertEqual(versions[3], '1.6')
|
||||
|
||||
|
|
Loading…
Reference in a new issue