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.


Reply via email to