commit: ef73388d8c900c9e3332d22fcd305dd2b77c2cfe Author: Jethro Donaldson <devel <AT> jro <DOT> nz> AuthorDate: Tue Dec 2 11:06:25 2025 +0000 Commit: Sam James <sam <AT> gentoo <DOT> org> CommitDate: Mon Feb 16 06:54:15 2026 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=ef73388d
emerge: add --usepkg-include command line option Facilitate selection of specific binary packages during a build action, with all others to be satisfied by ebuilds. Has no effect unless -k is specified or implied. This is the logical inverse of existing argument --usepkg-exclude. Signed-off-by: Jethro Donaldson <devel <AT> jro.nz> Part-of: https://github.com/gentoo/portage/pull/1527 Signed-off-by: Sam James <sam <AT> gentoo.org> lib/_emerge/actions.py | 33 +- lib/_emerge/depgraph.py | 26 +- lib/_emerge/main.py | 6 + lib/portage/_sets/base.py | 4 + lib/portage/tests/resolver/ResolverPlayground.py | 8 +- .../tests/resolver/test_binpackage_selection.py | 636 +++++++++++++++++++++ .../tests/sets/base/test_internal_package_set.py | 12 + man/emerge.1 | 4 + 8 files changed, 720 insertions(+), 9 deletions(-) diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py index 0723d44076..e5bd06a746 100644 --- a/lib/_emerge/actions.py +++ b/lib/_emerge/actions.py @@ -81,7 +81,12 @@ from portage.binpkg import get_binpkg_format from _emerge.clear_caches import clear_caches from _emerge.create_depgraph_params import create_depgraph_params from _emerge.Dependency import Dependency -from _emerge.depgraph import backtrack_depgraph, depgraph, resume_depgraph +from _emerge.depgraph import ( + backtrack_depgraph, + depgraph, + resume_depgraph, + _wildcard_set, +) from _emerge.emergelog import emergelog from _emerge.is_valid_package_atom import is_valid_package_atom from _emerge.main import profile_check @@ -2798,6 +2803,32 @@ def adjust_config(myopts, settings): settings["PORTAGE_BINPKG_FORMAT"] = myopts["--pkg-format"] settings.backup_changes("PORTAGE_BINPKG_FORMAT") + binpkg_selection_config(myopts, settings) + + +def binpkg_selection_config(opts, settings): + atoms = " ".join(opts.pop("--usepkg-exclude", [])).split() + usepkg_exclude = _wildcard_set(atoms) + atoms = " ".join(opts.pop("--usepkg-include", [])).split() + usepkg_include = _wildcard_set(atoms) + + # warn if include/exclude lists overlap on command line + conflicted_atoms = usepkg_exclude.getAtoms().intersection(usepkg_include.getAtoms()) + if conflicted_atoms: + writemsg( + "\n!!! The following atoms appear in both the --usepkg-exclude " + "and --usepkg-include command line arguments:\n" + "\n %s\n" % ("\n ".join(conflicted_atoms)) + ) + for a in conflicted_atoms: + usepkg_exclude.remove(a) + usepkg_include.remove(a) + + if not usepkg_exclude.isEmpty(): + opts["--usepkg-exclude"] = list(usepkg_exclude) + if not usepkg_include.isEmpty(): + opts["--usepkg-include"] = list(usepkg_include) + def display_missing_pkg_set(root_config, set_name): msg = [] diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py index dee63760c9..0c9276de07 100644 --- a/lib/_emerge/depgraph.py +++ b/lib/_emerge/depgraph.py @@ -209,6 +209,8 @@ class _frozen_depgraph_config: self.reinstall_atoms = _wildcard_set(atoms) atoms = " ".join(myopts.get("--usepkg-exclude", [])).split() self.usepkg_exclude = _wildcard_set(atoms) + atoms = " ".join(myopts.get("--usepkg-include", [])).split() + self.usepkg_include = _wildcard_set(atoms) atoms = " ".join(myopts.get("--useoldpkg-atoms", [])).split() self.useoldpkg_atoms = _wildcard_set(atoms) atoms = " ".join(myopts.get("--rebuild-exclude", [])).split() @@ -7635,6 +7637,9 @@ class depgraph: ) reinstall_atoms = self._frozen_config.reinstall_atoms usepkg_exclude = self._frozen_config.usepkg_exclude + usepkg_include = self._frozen_config.usepkg_include + have_usepkg_exclude = not usepkg_exclude.isEmpty() + have_usepkg_include = not usepkg_include.isEmpty() useoldpkg_atoms = self._frozen_config.useoldpkg_atoms matched_oldpkg = [] # Behavior of the "selective" parameter depends on @@ -7705,14 +7710,21 @@ class depgraph: ): continue - if ( - built - and not installed - and usepkg_exclude.findAtomForPackage( - pkg, modified_use=self._pkg_use_enabled(pkg) + if built and not installed: + in_usepkg_exclude = ( + have_usepkg_exclude + and usepkg_exclude.findAtomForPackage( + pkg, modified_use=self._pkg_use_enabled(pkg) + ) ) - ): - break + in_usepkg_include = ( + not have_usepkg_include + or usepkg_include.findAtomForPackage( + pkg, modified_use=self._pkg_use_enabled(pkg) + ) + ) + if in_usepkg_exclude or not in_usepkg_include: + break # We can choose not to install a live package from using binary # cache by disabling it with option --usepkg-exclude-live in the diff --git a/lib/_emerge/main.py b/lib/_emerge/main.py index 55a7bcb690..ef532eefba 100644 --- a/lib/_emerge/main.py +++ b/lib/_emerge/main.py @@ -575,6 +575,11 @@ def parse_opts(tmpcmdline, silent=False): + "Emerge will ignore matching binary packages. ", "action": "append", }, + "--usepkg-include": { + "help": "A space separated list of package names or slot atoms. " + + "Emerge will ignore non-matching binary packages. ", + "action": "append", + }, "--onlydeps-with-ideps": { "help": "modify interpretation of dependencies to include IDEPEND", "choices": true_y_or_n, @@ -888,6 +893,7 @@ def parse_opts(tmpcmdline, silent=False): (myoptions.rebuild_exclude, "rebuild-exclude"), (myoptions.rebuild_ignore, "rebuild-ignore"), (myoptions.usepkg_exclude, "usepkg-exclude"), + (myoptions.usepkg_include, "usepkg-include"), (myoptions.useoldpkg_atoms, "useoldpkg-atoms"), ) bad_options = ( diff --git a/lib/portage/_sets/base.py b/lib/portage/_sets/base.py index e25727e6a9..4c9f4914ab 100644 --- a/lib/portage/_sets/base.py +++ b/lib/portage/_sets/base.py @@ -61,6 +61,10 @@ class PackageSet: self._load() return self._nonatoms.copy() + def isEmpty(self): + self._load() + return len(self._atoms) == 0 and len(self._nonatoms) == 0 + def _setAtoms(self, atoms): self._atoms.clear() self._nonatoms.clear() diff --git a/lib/portage/tests/resolver/ResolverPlayground.py b/lib/portage/tests/resolver/ResolverPlayground.py index 6b7653d93f..aa7fbea70a 100644 --- a/lib/portage/tests/resolver/ResolverPlayground.py +++ b/lib/portage/tests/resolver/ResolverPlayground.py @@ -28,7 +28,11 @@ from portage.exception import InvalidBinaryPackageFormat from portage.gpg import GPG import _emerge -from _emerge.actions import _calc_depclean, expand_set_arguments +from _emerge.actions import ( + _calc_depclean, + binpkg_selection_config, + expand_set_arguments, +) from _emerge.Blocker import Blocker from _emerge.create_depgraph_params import create_depgraph_params from _emerge.DependencyArg import DependencyArg @@ -737,6 +741,8 @@ class ResolverPlayground: if "--usepkgonly" in options: options["--usepkg"] = True + binpkg_selection_config(options, self.settings) + global_noiselimit = portage.util.noiselimit global_emergelog_disable = _emerge.emergelog._disable try: diff --git a/lib/portage/tests/resolver/test_binpackage_selection.py b/lib/portage/tests/resolver/test_binpackage_selection.py new file mode 100644 index 0000000000..eaecc03a49 --- /dev/null +++ b/lib/portage/tests/resolver/test_binpackage_selection.py @@ -0,0 +1,636 @@ +# Copyright 2026 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from portage.tests import TestCase +from portage.tests.resolver.ResolverPlayground import ( + ResolverPlayground, + ResolverPlaygroundTestCase, +) + + +# base class for unit tests of binary package selection options +class BinPkgSelectionTestCase(TestCase): + pkgs_no_deps = { + "app-misc/foo-1.0": {}, + "app-misc/bar-1.0": {}, + "app-misc/baz-1.0": {}, + } + + pkgs_with_deps_newer = { + "app-misc/foo-1.1": {"RDEPEND": "app-misc/bar"}, + "app-misc/bar-1.1": {"RDEPEND": "app-misc/baz"}, + "app-misc/baz-1.1": {}, + } + + pkgs_with_deps = { + "app-misc/foo-1.0": {"RDEPEND": "app-misc/bar"}, + "app-misc/bar-1.0": {"RDEPEND": "app-misc/baz"}, + "app-misc/baz-1.0": {}, + } + + pkgs_with_slots = { + "app-misc/foo-1.0": {"SLOT": "1"}, + "app-misc/foo-2.0": {"SLOT": "2"}, + "app-misc/bar-1.0": {"SLOT": "1"}, + "app-misc/bar-2.0": {"SLOT": "2"}, + "app-misc/baz-1.0": {"SLOT": "1"}, + "app-misc/baz-2.0": {"SLOT": "2"}, + } + + pkg_atoms = ["app-misc/foo", "app-misc/bar", "app-misc/baz"] + + # runs multiple test cases in the same playground + def runBinPkgSelectionTest(self, test_cases, **kwargs): + playground = ResolverPlayground(**kwargs) + + try: + for n, test_case in enumerate(test_cases): + with self.subTest(f"Test {n+1}/{len(test_cases)}"): + playground.run_TestCase(test_case) + self.assertEqual(test_case.test_success, True, test_case.fail_msg) + finally: + playground.cleanup() + + +# test --usepkg-exclude option +class UsePkgExcludeTestCase(BinPkgSelectionTestCase): + + def testUsePkgExcludeOpt(self): + binpkgs = self.pkgs_no_deps + ebuilds = self.pkgs_no_deps + installed = self.pkgs_no_deps + + test_cases = ( + # --usepkg-exclude to have no effect without --usepkg + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg-exclude": ["foo"]}, + mergelist=[ + "app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # --usepkg-exclude in conflict with --usepkg-include to have no effect + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={ + "--usepkg": True, + "--usepkg-exclude": ["foo"], + "--usepkg-include": ["foo"], + }, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # --usepkg-exclude with unmatched atom excludes no binaries + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={ + "--usepkg": True, + "--usepkg-exclude": ["dev-libs/foo"], + }, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-exclude with a single atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["foo"]}, + mergelist=[ + "app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-exclude with multiple atoms + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["foo", "bar"]}, + mergelist=[ + "app-misc/foo-1.0", + "app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-exclude with wildcard + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["app-misc/b*"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-exclude with a single atom + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["foo"]}, + mergelist=[ + "app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-exclude with multiple atoms + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["foo", "bar"]}, + mergelist=[ + "app-misc/foo-1.0", + "app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-exclude with wildcard + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["app-misc/b*"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # --usepkg-exclude may not intersect requested atoms with --usepkgonly + ResolverPlaygroundTestCase( + ["app-misc/foo", "app-misc/bar"], + success=True, + ignore_mergelist_order=True, + options={"--usepkgonly": True, "--usepkg-exclude": ["baz"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + ], + ), + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=False, + options={"--usepkgonly": True, "--usepkg-exclude": ["foo"]}, + ), + # conflicting --usepkg-include and --usepkg-exclude to not interfere + # with non-overlapping --usepkg-exclude + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={ + "--usepkg": True, + "--usepkg-exclude": ["foo", "bar"], + "--usepkg-include": ["foo"], + }, + mergelist=[ + "[binary]app-misc/foo-1.0", + "app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + ) + + self.runBinPkgSelectionTest( + test_cases, binpkgs=binpkgs, ebuilds=ebuilds, installed=installed + ) + + def testUsePkgExcludeDeps(self): + binpkgs = self.pkgs_with_deps + ebuilds = self.pkgs_with_deps + + test_cases = ( + # request foo --usepkg-exclude for a single dependency + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-exclude": ["baz"]}, + mergelist=[ + "app-misc/baz-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/foo-1.0", + ], + ), + # request foo and --usepkg-exclude for multiple dependencies + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-exclude": ["bar baz"]}, + mergelist=[ + "app-misc/baz-1.0", + "app-misc/bar-1.0", + "[binary]app-misc/foo-1.0", + ], + ), + # request foo and --usepkg-exclude with wildcard matching dependencies + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-exclude": ["app-misc/b*"]}, + mergelist=[ + "app-misc/baz-1.0", + "app-misc/bar-1.0", + "[binary]app-misc/foo-1.0", + ], + ), + ) + + self.runBinPkgSelectionTest(test_cases, binpkgs=binpkgs, ebuilds=ebuilds) + + def testUsePkgExcludeSlot(self): + ebuilds = self.pkgs_with_slots + binpkgs = self.pkgs_with_slots + + test_cases = ( + # request all packages and --usepkg-exclude with single slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["foo:2"]}, + mergelist=[ + "app-misc/foo-2.0", + "[binary]app-misc/bar-2.0", + "[binary]app-misc/baz-2.0", + ], + ), + # request all packages and --usepkg-exclude with wildcard slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["app-misc/b*:2"]}, + mergelist=[ + "[binary]app-misc/foo-2.0", + "app-misc/bar-2.0", + "app-misc/baz-2.0", + ], + ), + # request all packages and --usepkg-exclude with unmatched slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-exclude": ["app-misc/foo:1"]}, + mergelist=[ + "[binary]app-misc/foo-2.0", + "[binary]app-misc/bar-2.0", + "[binary]app-misc/baz-2.0", + ], + ), + ) + + self.runBinPkgSelectionTest(test_cases, binpkgs=binpkgs, ebuilds=ebuilds) + + def testUsePkgExcludeUpdate(self): + ebuilds = self.pkgs_with_deps | self.pkgs_with_deps_newer + binpkgs = self.pkgs_with_deps | self.pkgs_with_deps_newer + installed = self.pkgs_with_deps + world = ("app-misc/foo",) + + test_cases = ( + # world update and --usepkg-exclude with single atom + ResolverPlaygroundTestCase( + ["@world"], + success=True, + options={ + "--update": True, + "--deep": True, + "--usepkg": True, + "--usepkg-exclude": ["baz"], + }, + mergelist=[ + "app-misc/baz-1.1", + "[binary]app-misc/bar-1.1", + "[binary]app-misc/foo-1.1", + ], + ), + # world update and --usepkg-exclude with wildcard + ResolverPlaygroundTestCase( + ["@world"], + success=True, + options={ + "--update": True, + "--deep": True, + "--usepkg": True, + "--usepkg-exclude": ["app-misc/b*"], + }, + mergelist=[ + "app-misc/baz-1.1", + "app-misc/bar-1.1", + "[binary]app-misc/foo-1.1", + ], + ), + ) + + self.runBinPkgSelectionTest( + test_cases, + binpkgs=binpkgs, + ebuilds=ebuilds, + installed=installed, + world=world, + ) + + +# test --usepkg-include option +class UsePkgIncludeTestCase(BinPkgSelectionTestCase): + + def testUsePkgIncludeOpt(self): + binpkgs = self.pkgs_no_deps + ebuilds = self.pkgs_no_deps + installed = self.pkgs_no_deps + + test_cases = ( + # --usepkg-include to have no effect without --usepkg + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg-include": ["foo"]}, + mergelist=[ + "app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # --usepkg-include with unmatched atom excludes all binaries + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={ + "--usepkg": True, + "--usepkg-include": ["dev-libs/foo"], + }, + mergelist=[ + "app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-include with single atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["foo"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-include with multiple atoms + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["foo", "bar"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # request all packages and --usepkg-include with wildcard + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["app-misc/b*"]}, + mergelist=[ + "app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-include with single atom + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["foo"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-include with multiple atoms + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["app-misc/b*"]}, + mergelist=[ + "app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "[binary]app-misc/baz-1.0", + ], + ), + # request @installed set and --usepkg-include with wildcard + ResolverPlaygroundTestCase( + ["@installed"], + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["foo", "bar"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + # --usepkg-include must encompass all requested atoms with --usepkgonly + ResolverPlaygroundTestCase( + ["app-misc/foo", "app-misc/bar"], + success=True, + ignore_mergelist_order=True, + options={"--usepkgonly": True, "--usepkg-include": ["foo", "bar"]}, + mergelist=[ + "[binary]app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + ], + ), + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=False, + options={"--usepkgonly": True, "--usepkg-include": ["foo"]}, + ), + # conflicting --usepkg-include and --usepkg-exclude to not interfere + # with non-overlapping --usepkg-include + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={ + "--usepkg": True, + "--usepkg-exclude": ["foo"], + "--usepkg-include": ["foo", "bar"], + }, + mergelist=[ + "app-misc/foo-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/baz-1.0", + ], + ), + ) + + self.runBinPkgSelectionTest( + test_cases, binpkgs=binpkgs, ebuilds=ebuilds, installed=installed + ) + + def testUsePkgIncludeDeps(self): + binpkgs = self.pkgs_with_deps + ebuilds = self.pkgs_with_deps + + test_cases = ( + # request for --usepkg-include for a single dependency + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-include": ["bar"]}, + mergelist=[ + "app-misc/baz-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/foo-1.0", + ], + ), + # request for --usepkg-include for multiple dependencies + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-include": ["bar baz"]}, + mergelist=[ + "[binary]app-misc/baz-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/foo-1.0", + ], + ), + # request for --usepkg-include with wildcard matching dependencies + ResolverPlaygroundTestCase( + ["app-misc/foo"], + success=True, + options={"--usepkg": True, "--usepkg-include": ["app-misc/b*"]}, + mergelist=[ + "[binary]app-misc/baz-1.0", + "[binary]app-misc/bar-1.0", + "app-misc/foo-1.0", + ], + ), + ) + + self.runBinPkgSelectionTest(test_cases, binpkgs=binpkgs, ebuilds=ebuilds) + + def testUsePkgIncludeSlot(self): + ebuilds = self.pkgs_with_slots + binpkgs = self.pkgs_with_slots + + test_cases = ( + # request all packages and --usepkg-include with single slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["foo:2"]}, + mergelist=[ + "[binary]app-misc/foo-2.0", + "app-misc/bar-2.0", + "app-misc/baz-2.0", + ], + ), + # request all packages and --usepkg-include with wildcard slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["app-misc/b*:2"]}, + mergelist=[ + "app-misc/foo-2.0", + "[binary]app-misc/bar-2.0", + "[binary]app-misc/baz-2.0", + ], + ), + # request all packages and --usepkg-include with unmatched slot atom + ResolverPlaygroundTestCase( + self.pkg_atoms, + success=True, + ignore_mergelist_order=True, + options={"--usepkg": True, "--usepkg-include": ["app-misc/foo:1"]}, + mergelist=[ + "app-misc/foo-2.0", + "app-misc/bar-2.0", + "app-misc/baz-2.0", + ], + ), + ) + + self.runBinPkgSelectionTest(test_cases, binpkgs=binpkgs, ebuilds=ebuilds) + + def testUsePkgIncludeUpdate(self): + ebuilds = self.pkgs_with_deps | self.pkgs_with_deps_newer + binpkgs = self.pkgs_with_deps | self.pkgs_with_deps_newer + installed = self.pkgs_with_deps + world = ("app-misc/foo",) + + test_cases = ( + # world update and --usepkg-include with single atom + ResolverPlaygroundTestCase( + ["@world"], + success=True, + options={ + "--update": True, + "--deep": True, + "--usepkg": True, + "--usepkg-include": ["baz"], + }, + mergelist=[ + "[binary]app-misc/baz-1.1", + "app-misc/bar-1.1", + "app-misc/foo-1.1", + ], + ), + # world update and --usepkg-include with wildcard + ResolverPlaygroundTestCase( + ["@world"], + success=True, + options={ + "--update": True, + "--deep": True, + "--usepkg": True, + "--usepkg-include": ["app-misc/b*"], + }, + mergelist=[ + "[binary]app-misc/baz-1.1", + "[binary]app-misc/bar-1.1", + "app-misc/foo-1.1", + ], + ), + ) + + self.runBinPkgSelectionTest( + test_cases, + binpkgs=binpkgs, + ebuilds=ebuilds, + installed=installed, + world=world, + ) diff --git a/lib/portage/tests/sets/base/test_internal_package_set.py b/lib/portage/tests/sets/base/test_internal_package_set.py index 77934cab23..c767c2249b 100644 --- a/lib/portage/tests/sets/base/test_internal_package_set.py +++ b/lib/portage/tests/sets/base/test_internal_package_set.py @@ -14,13 +14,20 @@ class InternalPackageSetTestCase(TestCase): def testInternalPackageSet(self): i1_atoms = {"dev-libs/A", ">=dev-libs/A-1", "dev-libs/B"} i2_atoms = {"dev-libs/A", "dev-libs/*", "dev-libs/C"} + i3_sets = {"@world", "@installed", "@system"} i1 = InternalPackageSet(initial_atoms=i1_atoms) i2 = InternalPackageSet(initial_atoms=i2_atoms, allow_wildcard=True) + i3 = InternalPackageSet(initial_atoms=i3_sets) self.assertRaises(InvalidAtom, InternalPackageSet, initial_atoms=i2_atoms) + self.assertFalse(i1.isEmpty()) + self.assertFalse(i2.isEmpty()) + self.assertFalse(i3.isEmpty()) + self.assertEqual(i1.getAtoms(), i1_atoms) self.assertEqual(i2.getAtoms(), i2_atoms) + self.assertEqual(i3.getNonAtoms(), i3_sets) new_atom = Atom("*/*", allow_wildcard=True) self.assertRaises(InvalidAtom, i1.add, new_atom) @@ -60,3 +67,8 @@ class InternalPackageSetTestCase(TestCase): i2_atoms = set(replace_atoms) self.assertEqual(i2.getAtoms(), i2_atoms) + + i2.clear() + self.assertEqual(i2.getAtoms(), set()) + self.assertEqual(i2.getNonAtoms(), set()) + self.assertTrue(i2.isEmpty()) diff --git a/man/emerge.1 b/man/emerge.1 index c774c088aa..cc0a2c41fb 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -769,6 +769,10 @@ terminal device is determined to be a TTY. This flag disables it regardless. A space separated list of package names or slot atoms. Emerge will ignore matching binary packages. .TP +.BR "\-\-usepkg\-include " ATOMS +A space separated list of package names or slot names. Emerge will ignore +non\-matching binary packages. +.TP .BR "\-\-rebuild\-exclude " ATOMS A space separated list of package names or slot atoms. Emerge will not rebuild matching packages due to \fB\-\-rebuild\fR.
