Skip to content

Commit

Permalink
UnstatedIuse: check for unstated IUSE in "?" dependencies
Browse files Browse the repository at this point in the history
Check for cases where a dependency uses conditional use dependency
with unknown USE flag. PMS states:

  It is an error for an ebuild to use a conditional use dependency when
  that ebuild does not have the flag in IUSE_EFFECTIVE.

Bug: https://bugs.gentoo.org/921841
Requested-by: Sam James <[email protected]>
Signed-off-by: Arthur Zamarin <[email protected]>
  • Loading branch information
arthurzam committed Jan 13, 2024
1 parent 4776408 commit f2d4e37
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 15 deletions.
33 changes: 21 additions & 12 deletions src/pkgcheck/checks/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from functools import partial
from operator import attrgetter

from pkgcore.ebuild import atom as atom_mod, restricts
from pkgcore.ebuild.atom import atom as atom_cls
from pkgcore.ebuild import restricts
from pkgcore.ebuild.atom import atom as atom_cls, transitive_use_atom
from pkgcore.ebuild.eapi import get_eapi
from pkgcore.ebuild.misc import sort_keywords
from pkgcore.fetch import fetchable, unknown_mirror
Expand Down Expand Up @@ -871,7 +871,7 @@ class DependencyCheck(Check):

required_addons = (addons.UseAddon,)
known_results = frozenset(
[
{
BadDependency,
MissingPackageRevision,
MissingUseDepDefault,
Expand All @@ -883,7 +883,7 @@ class DependencyCheck(Check):
InvalidBdepend,
InvalidIdepend,
MisplacedWeakBlocker,
]
}
)

def __init__(self, *args, use_addon):
Expand Down Expand Up @@ -929,12 +929,9 @@ def feed(self, pkg):
)
yield from unstated

unknowns_useflags = set()
for node in nodes:
if isinstance(node, boolean.OrRestriction):
in_or_restriction = True
else:
in_or_restriction = False

in_or_restriction = isinstance(node, boolean.OrRestriction)
for atom in iflatten_instance(node, (atom_cls,)):
# Skip reporting blockers on deprecated packages; the primary
# purpose of deprecations is to get rid of dependencies
Expand All @@ -959,6 +956,15 @@ def feed(self, pkg):
if atom.op == "=" and not atom.revision:
yield MissingPackageRevision(attr, str(atom), pkg=pkg)

if isinstance(atom, transitive_use_atom) and atom.use is not None:
for useflag in atom.use:
if useflag[-1] == "?":
useflag = useflag[:-1].removeprefix("!")
if useflag[-1] == ")":
useflag = useflag[:-3]
if useflag not in pkg.iuse_stripped:
unknowns_useflags.add(useflag)

if atom.blocks:
if atom.match(pkg):
yield BadDependency(attr, atom, "package blocks itself", pkg=pkg)
Expand All @@ -969,6 +975,9 @@ def feed(self, pkg):
elif not atom.blocks_strongly:
weak_blocks[attr].add(atom)

if unknowns_useflags:
yield UnstatedIuse(attr, sorted(unknowns_useflags), pkg=pkg)

for attr in ("depend", "bdepend"):
weak_blocks[attr].difference_update(weak_blocks["rdepend"])
weak_blocks["idepend"].difference_update(weak_blocks["rdepend"], weak_blocks["depend"])
Expand Down Expand Up @@ -1597,7 +1606,7 @@ class InvalidProperties(results.MetadataError, results.VersionResult):
class _RestrictPropertiesCheck(Check):
"""Generic check for RESTRICT and PROPERTIES."""

_attr = None
_attr: str = None
_unknown_result_cls = None
required_addons = (addons.UseAddon,)

Expand All @@ -1606,9 +1615,9 @@ def __init__(self, *args, use_addon):
self.filter = use_addon.get_filter(self._attr)

# pull allowed values from a repo and its masters
allowed = []
allowed = set()
for repo in self.options.target_repo.trees:
allowed.extend(getattr(repo.config, f"{self._attr}_allowed"))
allowed.update(getattr(repo.config, f"{self._attr}_allowed"))
self.allowed = frozenset(allowed)

def feed(self, pkg):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
diff -Naur standalone/DependencyCheck/MissingUseDepDefault/MissingUseDepDefault-0.ebuild fixed/DependencyCheck/MissingUseDepDefault/MissingUseDepDefault-0.ebuild
--- standalone/DependencyCheck/MissingUseDepDefault/MissingUseDepDefault-0.ebuild 2019-12-02 21:50:34.617257001 -0700
+++ fixed/DependencyCheck/MissingUseDepDefault/MissingUseDepDefault-0.ebuild 2019-12-02 21:52:56.547749364 -0700
@@ -3,7 +3,7 @@
HOMEPAGE="https://github.com/pkgcore/pkgcheck"
@@ -4,7 +4,7 @@
SLOT="0"
LICENSE="BSD"
IUSE="foo"
-DEPEND="stub/stub1[foo]"
-RDEPEND="|| ( stub/stub2[used] stub/stub2[-foo] )"
-BDEPEND="stub/stub3[foo?]"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
{"__class__": "UnstatedIuse", "category": "DependencyCheck", "package": "UnstatedIuse", "version": "0", "attr": "depend", "flags": ["used"], "profile": null, "num_profiles": null}
{"__class__": "UnstatedIuse", "category": "DependencyCheck", "package": "UnstatedIuse", "version": "0", "attr": "rdepend", "flags": ["used"], "profile": "default/amd64", "num_profiles": null}
{"__class__": "UnstatedIuse", "category": "DependencyCheck", "package": "UnstatedIuse", "version": "0", "attr": "rdepend", "flags": ["used"], "profile": "default/x86", "num_profiles": null}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
{"__class__": "UnstatedIuse", "category": "DependencyCheck", "package": "UnstatedIuse", "version": "0", "attr": "depend", "flags": ["used"], "profile": null, "num_profiles": null}
{"__class__": "UnstatedIuse", "category": "DependencyCheck", "package": "UnstatedIuse", "version": "0", "attr": "rdepend", "flags": ["used"], "profile": "default/amd64", "num_profiles": 2}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ DESCRIPTION="Ebuild missing USE dependency default"
HOMEPAGE="https://github.com/pkgcore/pkgcheck"
SLOT="0"
LICENSE="BSD"
IUSE="foo"
DEPEND="stub/stub1[foo]"
RDEPEND="|| ( stub/stub2[used] stub/stub2[-foo] )"
BDEPEND="stub/stub3[foo?]"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
<use>
<flag name="foo">stub</flag>
</use>
</pkgmetadata>
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
EAPI=7

DESCRIPTION="Ebuild with unstated IUSE in depsets"
HOMEPAGE="https://github.com/pkgcore/pkgcheck"
SLOT="0"
LICENSE="BSD"
RDEPEND="used? ( stub/stub1 )"
DEPEND="stub/stub4[used?]"
2 changes: 1 addition & 1 deletion tests/checks/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ def test_depset_unstated_iuse(self, attr):
@pytest.mark.parametrize("attr", dep_attrs)
def test_depset_missing_usedep_default(self, attr):
chk = self.mk_check()
mk_pkg = partial(self.mk_pkg, attr)
mk_pkg = partial(self.mk_pkg, attr, iuse="foo bar baz blah")

# USE flag exists on all matching pkgs
self.assertNoReport(chk, mk_pkg(eapi="4", depset="dev-libs/foo[bar?]"))
Expand Down

2 comments on commit f2d4e37

@thesamesam
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ferringb not sure if you want to/could check if pkgcore needs something here too

@ferringb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thesamesam good call; if PMS mandates this, then the proper 'layer' is pkgcore. pkgcheck should just be a user of pkgcore (albeit an intrusively low level one).

Is you pinging me an indication you want me to try and do this pkgcore side, or is this something you'll poke at? Cause I'm fucking around w/ a k8s CSI atm ;)

Please sign in to comment.