build: add support for conflicting dependencies (MR 2146)
This adds support for the depends="!conflict ..." syntax for explicitly marking another package as conflicting with the current one. Fixes: #2085
This commit is contained in:
parent
8a14d366ef
commit
a9d1049a3c
|
@ -130,6 +130,9 @@ def build_depends(args, apkbuild, arch, strict):
|
||||||
if "no_depends" in args and args.no_depends:
|
if "no_depends" in args and args.no_depends:
|
||||||
pmb.helpers.repo.update(args, arch)
|
pmb.helpers.repo.update(args, arch)
|
||||||
for depend in depends:
|
for depend in depends:
|
||||||
|
# Ignore conflicting dependencies
|
||||||
|
if depend.startswith("!"):
|
||||||
|
continue
|
||||||
# Check if binary package is missing
|
# Check if binary package is missing
|
||||||
if not pmb.parse.apkindex.package(args, depend, arch, False):
|
if not pmb.parse.apkindex.package(args, depend, arch, False):
|
||||||
raise RuntimeError("Missing binary package for dependency '" +
|
raise RuntimeError("Missing binary package for dependency '" +
|
||||||
|
@ -147,6 +150,8 @@ def build_depends(args, apkbuild, arch, strict):
|
||||||
else:
|
else:
|
||||||
# Build the dependencies
|
# Build the dependencies
|
||||||
for depend in depends:
|
for depend in depends:
|
||||||
|
if depend.startswith("!"):
|
||||||
|
continue
|
||||||
if package(args, depend, arch, strict=strict):
|
if package(args, depend, arch, strict=strict):
|
||||||
depends_built += [depend]
|
depends_built += [depend]
|
||||||
logging.verbose(pkgname + ": build dependencies: done, built: " +
|
logging.verbose(pkgname + ": build dependencies: done, built: " +
|
||||||
|
|
|
@ -97,6 +97,10 @@ def install_is_necessary(args, build, arch, package, packages_installed):
|
||||||
:returns: True if the package needs to be installed/updated,
|
:returns: True if the package needs to be installed/updated,
|
||||||
False otherwise.
|
False otherwise.
|
||||||
"""
|
"""
|
||||||
|
# For packages to be removed we can do the test immediately
|
||||||
|
if package.startswith("!"):
|
||||||
|
return package[1:] in packages_installed
|
||||||
|
|
||||||
# User may have disabled buiding packages during "pmbootstrap install"
|
# User may have disabled buiding packages during "pmbootstrap install"
|
||||||
build_disabled = False
|
build_disabled = False
|
||||||
if args.action == "install" and not args.build_pkgs_on_install:
|
if args.action == "install" and not args.build_pkgs_on_install:
|
||||||
|
|
|
@ -81,8 +81,6 @@ def parse_next_block(path, lines, start):
|
||||||
values = ret[key].split(" ")
|
values = ret[key].split(" ")
|
||||||
ret[key] = []
|
ret[key] = []
|
||||||
for value in values:
|
for value in values:
|
||||||
if value.startswith("!"):
|
|
||||||
continue
|
|
||||||
for operator in [">", "=", "<", "~"]:
|
for operator in [">", "=", "<", "~"]:
|
||||||
if operator in value:
|
if operator in value:
|
||||||
value = value.split(operator)[0]
|
value = value.split(operator)[0]
|
||||||
|
|
|
@ -116,7 +116,8 @@ def recurse(args, pkgnames, suffix="native"):
|
||||||
has multiple providers, we look at the installed packages in
|
has multiple providers, we look at the installed packages in
|
||||||
the chroot to make a decision (see package_provider()).
|
the chroot to make a decision (see package_provider()).
|
||||||
:returns: list of pkgnames: consists of the initial pkgnames plus all
|
:returns: list of pkgnames: consists of the initial pkgnames plus all
|
||||||
depends
|
depends. Dependencies explicitly marked as conflicting are
|
||||||
|
prefixed with !.
|
||||||
"""
|
"""
|
||||||
logging.debug(f"({suffix}) calculate depends of {', '.join(pkgnames)} "
|
logging.debug(f"({suffix}) calculate depends of {', '.join(pkgnames)} "
|
||||||
"(pmbootstrap -v for details)")
|
"(pmbootstrap -v for details)")
|
||||||
|
@ -131,6 +132,10 @@ def recurse(args, pkgnames, suffix="native"):
|
||||||
if pkgname_depend in ret:
|
if pkgname_depend in ret:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Check if the dependency is explicitly marked as conflicting
|
||||||
|
is_conflict = pkgname_depend.startswith("!")
|
||||||
|
pkgname_depend = pkgname_depend.lstrip("!")
|
||||||
|
|
||||||
# Get depends and pkgname from aports
|
# Get depends and pkgname from aports
|
||||||
pkgnames_install = list(ret) + todo
|
pkgnames_install = list(ret) + todo
|
||||||
package = package_from_aports(args, pkgname_depend)
|
package = package_from_aports(args, pkgname_depend)
|
||||||
|
@ -147,18 +152,23 @@ def recurse(args, pkgnames, suffix="native"):
|
||||||
f"Required by '{source}'. See: "
|
f"Required by '{source}'. See: "
|
||||||
"https://postmarketos.org/depends")
|
"https://postmarketos.org/depends")
|
||||||
|
|
||||||
# Append to todo/ret (unless it is a duplicate)
|
# Determine pkgname
|
||||||
pkgname = package["pkgname"]
|
pkgname = package["pkgname"]
|
||||||
|
if is_conflict:
|
||||||
|
pkgname = f"!{pkgname}"
|
||||||
|
|
||||||
|
# Append to todo/ret (unless it is a duplicate)
|
||||||
if pkgname in ret:
|
if pkgname in ret:
|
||||||
logging.verbose(f"{pkgname}: already found")
|
logging.verbose(f"{pkgname}: already found")
|
||||||
else:
|
else:
|
||||||
depends = package["depends"]
|
if not is_conflict:
|
||||||
logging.verbose(f"{pkgname}: depends on: {','.join(depends)}")
|
depends = package["depends"]
|
||||||
if depends:
|
logging.verbose(f"{pkgname}: depends on: {','.join(depends)}")
|
||||||
todo += depends
|
if depends:
|
||||||
for dep in depends:
|
todo += depends
|
||||||
if dep not in required_by:
|
for dep in depends:
|
||||||
required_by[dep] = set()
|
if dep not in required_by:
|
||||||
required_by[dep].add(pkgname_depend)
|
required_by[dep] = set()
|
||||||
|
required_by[dep].add(pkgname_depend)
|
||||||
ret.append(pkgname)
|
ret.append(pkgname)
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Copyright 2021 Oliver Smith
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pmb_test # noqa
|
||||||
|
import pmb.chroot.apk
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def args(tmpdir, request):
|
||||||
|
import pmb.parse
|
||||||
|
sys.argv = ["pmbootstrap.py", "init"]
|
||||||
|
args = pmb.parse.arguments()
|
||||||
|
args.log = args.work + "/log_testsuite.txt"
|
||||||
|
pmb.helpers.logging.init(args)
|
||||||
|
request.addfinalizer(pmb.helpers.logging.logfd.close)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_is_necessary(args):
|
||||||
|
# osk-sdl not installed, nothing to do
|
||||||
|
ret = pmb.chroot.apk.install_is_necessary(args, False, "aarch64",
|
||||||
|
"!osk-sdl",
|
||||||
|
{"unl0kr": {"unl0kr": {}}})
|
||||||
|
assert not ret
|
||||||
|
|
||||||
|
# osk-sdl installed, (un)install necessary
|
||||||
|
ret = pmb.chroot.apk.install_is_necessary(args, False, "aarch64",
|
||||||
|
"!osk-sdl",
|
||||||
|
{"osk-sdl": {"osk-sdl": {}}})
|
||||||
|
assert ret
|
|
@ -140,16 +140,18 @@ def test_get_depends(monkeypatch):
|
||||||
def test_build_depends(args, monkeypatch):
|
def test_build_depends(args, monkeypatch):
|
||||||
# Shortcut and fake apkbuild
|
# Shortcut and fake apkbuild
|
||||||
func = pmb.build._package.build_depends
|
func = pmb.build._package.build_depends
|
||||||
apkbuild = {"pkgname": "test", "depends": ["a"], "makedepends": ["b"],
|
apkbuild = {"pkgname": "test", "depends": ["a", "!c"],
|
||||||
"checkdepends": [], "subpackages": {"d": None}, "options": []}
|
"makedepends": ["b"], "checkdepends": [],
|
||||||
|
"subpackages": {"d": None}, "options": []}
|
||||||
|
|
||||||
# No depends built (first makedepends + depends, then only makedepends)
|
# No depends built (first makedepends + depends, then only makedepends)
|
||||||
monkeypatch.setattr(pmb.build._package, "package", return_none)
|
monkeypatch.setattr(pmb.build._package, "package", return_none)
|
||||||
assert func(args, apkbuild, "armhf", True) == (["a", "b"], [])
|
assert func(args, apkbuild, "armhf", True) == (["!c", "a", "b"], [])
|
||||||
|
|
||||||
# All depends built (makedepends only)
|
# All depends built (makedepends only)
|
||||||
monkeypatch.setattr(pmb.build._package, "package", return_string)
|
monkeypatch.setattr(pmb.build._package, "package", return_string)
|
||||||
assert func(args, apkbuild, "armhf", False) == (["a", "b"], ["a", "b"])
|
assert func(args, apkbuild, "armhf", False) == (["!c", "a", "b"],
|
||||||
|
["a", "b"])
|
||||||
|
|
||||||
|
|
||||||
def test_build_depends_no_binary_error(args, monkeypatch):
|
def test_build_depends_no_binary_error(args, monkeypatch):
|
||||||
|
|
|
@ -115,6 +115,34 @@ def test_parse_next_block_virtual():
|
||||||
assert start == [31]
|
assert start == [31]
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_next_block_conflict():
|
||||||
|
"""
|
||||||
|
Test parsing a package that specifies a conflicting dependency from an
|
||||||
|
APKINDEX.
|
||||||
|
"""
|
||||||
|
# Read the file
|
||||||
|
func = pmb.parse.apkindex.parse_next_block
|
||||||
|
path = pmb.config.pmb_src + "/test/testdata/apkindex/conflict"
|
||||||
|
with open(path, "r", encoding="utf-8") as handle:
|
||||||
|
lines = handle.readlines()
|
||||||
|
|
||||||
|
# First block
|
||||||
|
start = [0]
|
||||||
|
block = {'arch': 'x86_64',
|
||||||
|
'depends': ['!conflict', 'so:libc.musl-x86_64.so.1'],
|
||||||
|
'origin': 'hello-world',
|
||||||
|
'pkgname': 'hello-world',
|
||||||
|
'provides': ['cmd:hello-world'],
|
||||||
|
'timestamp': '1500000000',
|
||||||
|
'version': '2-r0'}
|
||||||
|
assert func(path, lines, start) == block
|
||||||
|
assert start == [20]
|
||||||
|
|
||||||
|
# No more blocks
|
||||||
|
assert func(path, lines, start) is None
|
||||||
|
assert start == [20]
|
||||||
|
|
||||||
|
|
||||||
def test_parse_add_block(args):
|
def test_parse_add_block(args):
|
||||||
func = pmb.parse.apkindex.parse_add_block
|
func = pmb.parse.apkindex.parse_add_block
|
||||||
multiple_providers = False
|
multiple_providers = False
|
||||||
|
|
|
@ -146,7 +146,8 @@ def test_recurse(args, monkeypatch):
|
||||||
depends = {
|
depends = {
|
||||||
"test": ["libtest", "so:libtest.so.1"],
|
"test": ["libtest", "so:libtest.so.1"],
|
||||||
"libtest": ["libtest_depend"],
|
"libtest": ["libtest_depend"],
|
||||||
"libtest_depend": [],
|
"libtest_depend": ["!libtest_conflict"],
|
||||||
|
"libtest_conflict": [],
|
||||||
"so:libtest.so.1": ["libtest_depend"],
|
"so:libtest.so.1": ["libtest_depend"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,5 +159,6 @@ def test_recurse(args, monkeypatch):
|
||||||
# Run
|
# Run
|
||||||
func = pmb.parse.depends.recurse
|
func = pmb.parse.depends.recurse
|
||||||
pkgnames = ["test", "so:libtest.so.1"]
|
pkgnames = ["test", "so:libtest.so.1"]
|
||||||
result = ["test", "so:libtest.so.1", "libtest", "libtest_depend"]
|
result = ["test", "so:libtest.so.1", "libtest", "libtest_depend",
|
||||||
|
"!libtest_conflict"]
|
||||||
assert func(args, pkgnames) == result
|
assert func(args, pkgnames) == result
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
C:Q1XaZzCVZ9mvH8djPyEb5aUYhG3r4=
|
||||||
|
P:hello-world
|
||||||
|
V:2-r0
|
||||||
|
A:x86_64
|
||||||
|
S:2897
|
||||||
|
I:20480
|
||||||
|
T:hello world program to be built in the testsuite
|
||||||
|
U:https://en.wikipedia.org/wiki/%22Hello,_World!%22_program
|
||||||
|
L:MIT
|
||||||
|
o:hello-world
|
||||||
|
t:1500000000
|
||||||
|
c:
|
||||||
|
D:!conflict so:libc.musl-x86_64.so.1
|
||||||
|
p:cmd:hello-world
|
||||||
|
F:usr
|
||||||
|
F:usr/bin
|
||||||
|
R:hello-world
|
||||||
|
a:0:0:755
|
||||||
|
Z:Q1ZjTpsnMchSsSwEPB1cTjihYuJvo=
|
||||||
|
|
Loading…
Reference in New Issue