pmb.parse: Respect provider_priority if multiple providers exist (MR 1945)

Alpine APKBUILDs have the concept of "provider priorities" that affect
the choice of the provider to install when multiple packages provide
a virtual package.

One use case for this is to allow installation of different firmware
versions. bq-paella can run unsigned firmware, therefore you have the
choice between using the original firmware from the manufacturer, or
a slightly newer version from Qualcomm for the Dragonboard 410c.

We add provides="firmware-qcom-msm8916-wcnss" (the "virtual package")
to both firmware-qcom-db410c-wcnss and firmware-bq-picmt-wcnss.
At this point, attempting to install "firmware-qcom-msm8916-wcnss"
would still fail with apk. (Because it does not know which provider
to install.)

To pick a default we can set e.g. provider_priority=100 for
firmware-qcom-db410c-wcnss (the slightly newer version).
In that case, firmware-qcom-db410c-wcnss should be installed by default.

However, the user can choose to do "apk add firmware-bq-picmt-wcnss"
to override the default choice in case of problems. In that case,
the conflicting firmware-qcom-db410c-wcnss will be automatically removed.

At the moment, pmbootstrap does not respect the "provider_priority" at all.
In the above case, it would always install "firmware-bq-picmt-wcnss"
during "pmbootstrap install" since that has the shortest name.

Extend the pmbootstrap code to pick a provider with the highest priority
(if any of the providers has a priority set).
This commit is contained in:
Minecrell 2020-06-08 22:10:12 +02:00 committed by Alexey Min
parent 6d013b4472
commit 3ebb994206
No known key found for this signature in database
GPG Key ID: EBF5ECFFFEE34DED
4 changed files with 78 additions and 2 deletions

View File

@ -42,6 +42,7 @@ def parse_next_block(args, path, lines, start):
"o": "origin",
"P": "pkgname",
"p": "provides",
"k": "provider_priority",
"t": "timestamp",
"V": "version",
}
@ -321,6 +322,32 @@ def providers(args, package, arch=None, must_exist=True, indexes=None):
return ret
def provider_highest_priority(providers, pkgname):
"""
Get the provider(s) with the highest provider_priority and log a message.
:param providers: returned dict from providers(), must not be empty
:param pkgname: the package name we are interested in (for the log message)
"""
max_priority = 0
priority_providers = collections.OrderedDict()
for provider_name, provider in providers.items():
priority = int(provider.get("provider_priority", -1))
if priority > max_priority:
priority_providers.clear()
max_priority = priority
if priority == max_priority:
priority_providers[provider_name] = provider
if priority_providers:
logging.debug(f"{pkgname}: picked provider(s) with higest priority {max_priority}: " +
", ".join(priority_providers.keys()))
return priority_providers
# None of the providers seems to have a provider_priority defined
return providers
def provider_shortest(providers, pkgname):
"""
Get the provider with the shortest pkgname and log a message. In most cases

View File

@ -73,7 +73,12 @@ def package_provider(args, pkgname, pkgnames_install, suffix="native"):
" the '" + suffix + "' chroot already")
return provider
# 5. Pick the provider
# 5. Pick the provider(s) with the highest priority
providers = pmb.parse.apkindex.provider_highest_priority(providers, pkgname)
if len(providers) == 1:
return list(providers.values())[0]
# 6. Pick the shortest provider. (Note: Normally apk would fail here!)
return pmb.parse.apkindex.provider_shortest(providers, pkgname)

View File

@ -300,6 +300,43 @@ def test_providers_highest_version(args, monkeypatch):
assert providers["test"]["version"] == "3"
def test_provider_highest_priority(args, monkeypatch):
# Verify that it picks the provider with highest priority
func = pmb.parse.apkindex.provider_highest_priority
provider_none_a = {"pkgname": "a", "provides": ["test"]}
provider_none_b = {"pkgname": "b", "provides": ["test"]}
provider_low_c = {"pkgname": "c", "provides": ["test"],
"provider_priority": 42}
provider_low_d = {"pkgname": "d", "provides": ["test"],
"provider_priority": 42}
provider_high = {"pkgname": "e", "provides": ["test"],
"provider_priority": 1337}
# No provider has a priority
providers = {"a": provider_none_a}
assert func(providers, "test") == providers
providers = {"a": provider_none_a, "b": provider_none_b}
assert func(providers, "test") == providers
# One provider has a priority, another one does not
providers = {"a": provider_none_a, "e": provider_high}
assert func(providers, "test") == {"e": provider_high}
# One provider has a priority, another one has a higher priority
providers = {"c": provider_low_c, "e": provider_high}
assert func(providers, "test") == {"e": provider_high}
# One provider has a priority, another one has the same priority
providers = {"c": provider_low_c, "d": provider_low_d}
assert func(providers, "test") == providers
# + some package without priority at all should be filtered out
providers2 = providers.copy()
providers2["a"] = provider_none_a
assert func(providers2, "test") == providers
def test_package(args, monkeypatch):
# Override pmb.parse.apkindex.providers()
providers = collections.OrderedDict()

View File

@ -72,7 +72,14 @@ def test_package_provider(args, monkeypatch):
pkgnames_install = []
assert func(args, pkgname, pkgnames_install) == package
# 5. Pick the first one
# 5. Pick package with highest priority
package_with_priority = {"pkgname": "test-priority", "provides": ["test"],
"provider_priority": 100}
providers = {"test-two": package_two, "test-priority": package_with_priority}
assert func(args, pkgname, pkgnames_install) == package_with_priority
# 6. Pick the first one
providers = {"test_": package, "test-two": package_two}
installed = {}
assert func(args, pkgname, pkgnames_install) == package