--autounmask: Support license changes
authorSebastian Luther <SebastianLuther@gmx.de>
Fri, 17 Sep 2010 12:50:19 +0000 (14:50 +0200)
committerZac Medico <zmedico@gentoo.org>
Fri, 17 Sep 2010 20:58:38 +0000 (13:58 -0700)
pym/_emerge/depgraph.py
pym/portage/package/ebuild/getmaskingstatus.py
pym/portage/tests/resolver/ResolverPlayground.py
pym/portage/tests/resolver/test_autounmask.py

index 6a7eb04b7646de1a6dc5a6ce4e8106f7d6f7de95..fe375b7bbf35ac19d77693845f646a279bd58126 100644 (file)
@@ -122,7 +122,7 @@ class _frozen_depgraph_config(object):
 class _dynamic_depgraph_config(object):
 
        def __init__(self, depgraph, myparams, allow_backtracking,
-               runtime_pkg_mask, needed_unstable_keywords, needed_use_config_changes):
+               runtime_pkg_mask, needed_unstable_keywords, needed_use_config_changes, needed_license_changes):
                self.myparams = myparams.copy()
                self._vdb_loaded = False
                self._allow_backtracking = allow_backtracking
@@ -206,6 +206,11 @@ class _dynamic_depgraph_config(object):
                else:
                        self._needed_unstable_keywords = needed_unstable_keywords.copy()
 
+               if needed_license_changes is None:
+                       self._needed_license_changes = {}
+               else:
+                       self._needed_license_changes = needed_license_changes.copy()
+
                if needed_use_config_changes is None:
                        self._needed_use_config_changes = {}
                else:
@@ -295,13 +300,14 @@ class depgraph(object):
        
        def __init__(self, settings, trees, myopts, myparams, spinner,
                frozen_config=None, runtime_pkg_mask=None, needed_unstable_keywords=None, \
-                       needed_use_config_changes=None, allow_backtracking=False):
+                       needed_use_config_changes=None, needed_license_changes=None, allow_backtracking=False):
                if frozen_config is None:
                        frozen_config = _frozen_depgraph_config(settings, trees,
                        myopts, spinner)
                self._frozen_config = frozen_config
                self._dynamic_config = _dynamic_depgraph_config(self, myparams,
-                       allow_backtracking, runtime_pkg_mask, needed_unstable_keywords, needed_use_config_changes)
+                       allow_backtracking, runtime_pkg_mask, needed_unstable_keywords, \
+                       needed_use_config_changes, needed_license_changes)
 
                self._select_atoms = self._select_atoms_highest_available
                self._select_package = self._select_pkg_highest_available
@@ -1900,7 +1906,9 @@ class depgraph(object):
                if set(self._dynamic_config.digraph).intersection( \
                        self._dynamic_config._needed_unstable_keywords) or \
                        set(self._dynamic_config.digraph).intersection( \
-                       self._dynamic_config._needed_use_config_changes) :
+                       self._dynamic_config._needed_use_config_changes) or \
+                       set(self._dynamic_config.digraph).intersection( \
+                       self._dynamic_config._needed_license_changes) :
                        #We failed if the user needs to change the configuration
                        return False, myfavorites
 
@@ -2519,23 +2527,21 @@ class depgraph(object):
                                not self._want_installed_pkg(pkg):
                                pkg = None
 
-                       for allow_unstable_keywords in False, True:
+                       for only_use_changes in True, False:
                                if pkg is not None:
                                        break
 
                                pkg, existing = \
                                        self._wrapped_select_pkg_highest_available_imp(
                                                root, atom, onlydeps=onlydeps,
-                                               allow_use_changes=True, allow_unstable_keywords=allow_unstable_keywords)
+                                               allow_use_changes=True,
+                                               allow_unstable_keywords=(not only_use_changes),
+                                               allow_license_changes=(not only_use_changes))
 
                                if pkg is not None and \
                                        pkg.installed and \
                                        not self._want_installed_pkg(pkg):
                                        pkg = None
-
-                               if pkg is not None and \
-                                       not pkg.visible and allow_unstable_keywords:
-                                       self._dynamic_config._needed_unstable_keywords.add(pkg)
                        
                        if self._dynamic_config._need_restart:
                                return None, None
@@ -2547,26 +2553,59 @@ class depgraph(object):
 
                return pkg, existing
 
-       def _pkg_visibility_check(self, pkg, allow_unstable_keywords=False):
-               if pkg.visible:
-                       return True
+       def _pkg_visibility_check(self, pkg, allow_unstable_keywords=False, allow_license_changes=False):
 
-               if pkg in self._dynamic_config._needed_unstable_keywords:
+               if pkg.visible:
                        return True
 
-               if not allow_unstable_keywords:
-                       return False
-
                pkgsettings = self._frozen_config.pkgsettings[pkg.root]
                root_config = self._frozen_config.roots[pkg.root]
                mreasons = _get_masking_status(pkg, pkgsettings, root_config, use=self._pkg_use_enabled(pkg))
-               if len(mreasons) == 1 and \
-                       mreasons[0].unmask_hint and \
-                       mreasons[0].unmask_hint.key == 'unstable keyword':
+
+               masked_by_unstable_keywords = False
+               missing_licenses = None
+               masked_by_something_else = False
+
+               for reason in mreasons:
+                       hint = reason.unmask_hint
+
+                       if hint is None:
+                               masked_by_something_else = True
+                       elif hint.key == "unstable keyword":
+                               masked_by_unstable_keywords = True
+                       elif hint.key == "license":
+                               missing_licenses = hint.value
+                       else:
+                               masked_by_something_else = True
+
+               if masked_by_something_else:
+                       return False
+
+               if pkg in self._dynamic_config._needed_unstable_keywords:
+                       #If the package is already keyworded, remove the mask.
+                       masked_by_unstable_keywords = False
+
+               if missing_licenses:
+                       #If the needed licenses are already unmasked, remove the mask.
+                       missing_licenses.difference_update(self._dynamic_config._needed_license_changes.get(pkg, set()))
+
+               if not (masked_by_unstable_keywords or missing_licenses):
+                       #Package has already been unmasked.
                        return True
-               else:
+
+               if (masked_by_unstable_keywords and not allow_unstable_keywords) or \
+                       (missing_licenses and not allow_license_changes):
+                       #We are not allowed to do the needed changes.
                        return False
 
+               if masked_by_unstable_keywords:
+                       self._dynamic_config._needed_unstable_keywords.add(pkg)
+
+               if missing_licenses:
+                       self._dynamic_config._needed_license_changes.setdefault(pkg, set()).update(missing_licenses)
+
+               return True
+
        def _pkg_use_enabled(self, pkg, target_use=None):
                """
                If target_use is None, returns pkg.use.enabled + changes in _needed_use_config_changes.
@@ -2646,7 +2685,7 @@ class depgraph(object):
                return new_use
 
        def _wrapped_select_pkg_highest_available_imp(self, root, atom, onlydeps=False, \
-               allow_use_changes=False, allow_unstable_keywords=False):
+               allow_use_changes=False, allow_unstable_keywords=False, allow_license_changes=False):
                root_config = self._frozen_config.roots[root]
                pkgsettings = self._frozen_config.pkgsettings[root]
                dbs = self._dynamic_config._filtered_trees[root]["dbs"]
@@ -2748,7 +2787,9 @@ class depgraph(object):
                                                # were installed can be automatically downgraded
                                                # to an unmasked version.
 
-                                               if not self._pkg_visibility_check(pkg, allow_unstable_keywords=allow_unstable_keywords):
+                                               if not self._pkg_visibility_check(pkg, \
+                                                       allow_unstable_keywords=allow_unstable_keywords,
+                                                       allow_license_changes=allow_license_changes):
                                                        continue
 
                                                # Enable upgrade or downgrade to a version
@@ -2783,7 +2824,8 @@ class depgraph(object):
                                                                                continue
                                                                        else:
                                                                                if not self._pkg_visibility_check(pkg_eb, \
-                                                                                       allow_unstable_keywords=allow_unstable_keywords):
+                                                                                       allow_unstable_keywords=allow_unstable_keywords,
+                                                                                       allow_license_changes=allow_license_changes):
                                                                                        continue
 
                                        # Calculation of USE for unbuilt ebuilds is relatively
@@ -3007,12 +3049,14 @@ class depgraph(object):
                        if avoid_update:
                                for pkg in matched_packages:
                                        if pkg.installed and self._pkg_visibility_check(pkg, \
-                                               allow_unstable_keywords=allow_unstable_keywords):
+                                               allow_unstable_keywords=allow_unstable_keywords,
+                                               allow_license_changes=allow_license_changes):
                                                return pkg, existing_node
 
                        bestmatch = portage.best(
                                [pkg.cpv for pkg in matched_packages \
-                                       if self._pkg_visibility_check(pkg, allow_unstable_keywords=allow_unstable_keywords)])
+                                       if self._pkg_visibility_check(pkg, allow_unstable_keywords=allow_unstable_keywords,
+                                               allow_license_changes=allow_license_changes)])
                        if not bestmatch:
                                # all are masked, so ignore visibility
                                bestmatch = portage.best(
@@ -5422,14 +5466,13 @@ class depgraph(object):
                                pkgsettings = self._frozen_config.pkgsettings[pkg.root]
                                mreasons = _get_masking_status(pkg, pkgsettings, pkg.root_config,
                                        use=self._pkg_use_enabled(pkg))
-                               if len(mreasons) == 1 and \
-                                       mreasons[0].unmask_hint and \
-                                       mreasons[0].unmask_hint.key == 'unstable keyword':
-                                       keyword = mreasons[0].unmask_hint.value
-                               else:
-                                       keyword = '~' + pkgsettings.get('ARCH', '*')
-                               unstable_keyword_msg.append(get_dep_chain(pkg))
-                               unstable_keyword_msg.append("=%s %s\n" % (pkg.cpv, keyword))
+                               for reason in mreasons:
+                                       if reason.unmask_hint and \
+                                               reason.unmask_hint.key == 'unstable keyword':
+                                               keyword = reason.unmask_hint.value
+
+                                               unstable_keyword_msg.append(get_dep_chain(pkg))
+                                               unstable_keyword_msg.append("=%s %s\n" % (pkg.cpv, keyword))
 
                use_changes_msg = []
                for pkg, needed_use_config_change in self._dynamic_config._needed_use_config_changes.items():
@@ -5445,6 +5488,13 @@ class depgraph(object):
                                use_changes_msg.append(get_dep_chain(pkg))
                                use_changes_msg.append("=%s %s\n" % (pkg.cpv, " ".join(adjustments)))
 
+               license_msg = []
+               for pkg, missing_licenses in self._dynamic_config._needed_license_changes.items():
+                       self._show_merge_list()
+                       if pkg in self._dynamic_config.digraph:
+                               license_msg.append(get_dep_chain(pkg))
+                               license_msg.append("=%s %s\n" % (pkg.cpv, " ".join(sorted(missing_licenses))))
+
                if unstable_keyword_msg:
                        writemsg_stdout("\nThe following " + colorize("BAD", "keyword changes") + \
                                " are necessary to proceed:\n", noiselevel=-1)
@@ -5455,6 +5505,11 @@ class depgraph(object):
                                " are necessary to proceed:\n", noiselevel=-1)
                        writemsg_stdout("".join(use_changes_msg), noiselevel=-1)
 
+               if license_msg:
+                       writemsg_stdout("\nThe following " + colorize("BAD", "license changes") + \
+                               " are necessary to proceed:\n", noiselevel=-1)
+                       writemsg_stdout("".join(license_msg), noiselevel=-1)
+
                # TODO: Add generic support for "set problem" handlers so that
                # the below warnings aren't special cases for world only.
 
@@ -5849,7 +5904,9 @@ class depgraph(object):
                        "runtime_pkg_mask":
                                self._dynamic_config._runtime_pkg_mask.copy(),
                        "needed_use_config_changes":
-                               self._dynamic_config._needed_use_config_changes.copy()
+                               self._dynamic_config._needed_use_config_changes.copy(),
+                       "needed_license_changes":
+                               self._dynamic_config._needed_license_changes.copy(),
                        }
                        
 
index 434b4b2eba6e8c77b63aaa932491240e8e5809cd..1eb6afed2475e1135d21b839f534a8028e2073bd 100644 (file)
@@ -156,7 +156,7 @@ def _getmaskingstatus(mycpv, settings, portdb):
                                if x in allowed_tokens]
                        msg = license_split[:]
                        msg.append("license(s)")
-                       rValue.append(_MaskReason("LICENSE", " ".join(msg)))
+                       rValue.append(_MaskReason("LICENSE", " ".join(msg), _UnmaskHint("license", set(missing_licenses))))
        except portage.exception.InvalidDependString as e:
                rValue.append(_MaskReason("invalid", "LICENSE: "+str(e)))
 
index 52198b6f1ce8a14904aa1e0dbccc75179be9cb11..f05bdf184f5a58ae052b380e1ea0a6e071f7af5f 100644 (file)
@@ -522,7 +522,7 @@ class ResolverPlaygroundTestCase(object):
 class ResolverPlaygroundResult(object):
 
        checks = (
-               "success", "mergelist", "use_changes", "unstable_keywords", "slot_collision_solutions",
+               "success", "mergelist", "use_changes", "license_changes", "unstable_keywords", "slot_collision_solutions",
                "circular_dependency_solutions",
                )
        optional_checks = (
@@ -535,6 +535,7 @@ class ResolverPlaygroundResult(object):
                self.favorites = favorites
                self.mergelist = None
                self.use_changes = None
+               self.license_changes = None
                self.unstable_keywords = None
                self.slot_collision_solutions = None
                self.circular_dependency_solutions = None
@@ -562,6 +563,11 @@ class ResolverPlaygroundResult(object):
                        for pkg in self.depgraph._dynamic_config._needed_unstable_keywords:
                                self.unstable_keywords.add(pkg.cpv)
 
+               if self.depgraph._dynamic_config._needed_license_changes:
+                       self.license_changes = {}
+                       for pkg, missing_licenses in self.depgraph._dynamic_config._needed_license_changes.items():
+                               self.license_changes[pkg.cpv] = missing_licenses
+
                if self.depgraph._dynamic_config._slot_conflict_handler is not None:
                        self.slot_collision_solutions  = []
                        handler = self.depgraph._dynamic_config._slot_conflict_handler
index df541120d80a9e22debbf5356e07867527e6405e..ce3ce38f0737f9b5dad9a5b3032971c2daad2812 100644 (file)
@@ -187,3 +187,42 @@ class AutounmaskTestCase(TestCase):
                                self.assertEqual(test_case.test_success, True, test_case.fail_msg)
                finally:
                        playground.cleanup()
+
+       def testAutounmaskForLicenses(self):
+
+               ebuilds = {
+                       "dev-libs/A-1": { "LICENSE": "TEST" },
+                       "dev-libs/B-1": { "LICENSE": "TEST", "IUSE": "foo", "KEYWORDS": "~x86"},
+                       "dev-libs/C-1": { "DEPEND": "dev-libs/B[foo]", "EAPI": 2 },
+                       }
+
+               test_cases = (
+                               ResolverPlaygroundTestCase(
+                                       ["=dev-libs/A-1"],
+                                       options = {"--autounmask": False},
+                                       success = False),
+                               ResolverPlaygroundTestCase(
+                                       ["=dev-libs/A-1"],
+                                       options = {"--autounmask": True},
+                                       success = False,
+                                       mergelist = ["dev-libs/A-1"],
+                                       license_changes = { "dev-libs/A-1": set(["TEST"]) }),
+
+                               #Test license+keyword+use change at once.
+                               ResolverPlaygroundTestCase(
+                                       ["=dev-libs/C-1"],
+                                       options = {"--autounmask": True},
+                                       success = False,
+                                       mergelist = ["dev-libs/B-1", "dev-libs/C-1"],
+                                       license_changes = { "dev-libs/B-1": set(["TEST"]) },
+                                       unstable_keywords = ["dev-libs/B-1"],
+                                       use_changes = { "dev-libs/B-1": { "foo": True } }),
+                       )
+
+               playground = ResolverPlayground(ebuilds=ebuilds)
+               try:
+                       for test_case in test_cases:
+                               playground.run_TestCase(test_case)
+                               self.assertEqual(test_case.test_success, True, test_case.fail_msg)
+               finally:
+                       playground.cleanup()