def _find_suggestions(self):
		if not self.shortest_cycle:
			return None, None

		suggestions = []
		final_solutions = {}

		for pos, pkg in enumerate(self.shortest_cycle):
			parent = self.shortest_cycle[pos-1]
			priorities = self.graph.nodes[parent][0][pkg]
			parent_atoms = self.all_parent_atoms.get(pkg)

			if priorities[-1].buildtime:
				dep = parent.metadata["DEPEND"]
			elif priorities[-1].runtime:
				dep = parent.metadata["RDEPEND"]

			for ppkg, atom in parent_atoms:
				if ppkg == parent:
					changed_parent = ppkg
					parent_atom = atom.unevaluated_atom
					break

			try:
				affecting_use = extract_affecting_use(dep, parent_atom,
					eapi=parent.metadata["EAPI"])
			except InvalidDependString:
				if not parent.installed:
					raise
				affecting_use = set()

			# Make sure we don't want to change a flag that is 
			#	a) in use.mask or use.force
			#	b) changed by autounmask
			
			usemask, useforce = self._get_use_mask_and_force(parent)
			autounmask_changes = self._get_autounmask_changes(parent)
			untouchable_flags = frozenset(chain(usemask, useforce, autounmask_changes))

			affecting_use.difference_update(untouchable_flags)

			#If any of the flags we're going to touch is in REQUIRED_USE, add all
			#other flags in REQUIRED_USE to affecting_use, to not lose any solution.
			required_use_flags = get_required_use_flags(
				parent.metadata.get("REQUIRED_USE", ""))

			if affecting_use.intersection(required_use_flags):
				# TODO: Find out exactly which REQUIRED_USE flags are
				# entangled with affecting_use. We have to limit the
				# number of flags since the number of loops is
				# exponentially related (see bug #374397).
				total_flags = set()
				total_flags.update(affecting_use, required_use_flags)
				total_flags.difference_update(untouchable_flags)
				if len(total_flags) <= 10:
					affecting_use = total_flags

			affecting_use = tuple(affecting_use)

			if not affecting_use:
				continue

			#We iterate over all possible settings of these use flags and gather
			#a set of possible changes
			#TODO: Use the information encoded in REQUIRED_USE
			solutions = set()
			for use_state in product(("disabled", "enabled"),
				repeat=len(affecting_use)):
				current_use = set(self.depgraph._pkg_use_enabled(parent))
				for flag, state in zip(affecting_use, use_state):
					if state == "enabled":
						current_use.add(flag)
					else:
						current_use.discard(flag)
				try:
					reduced_dep = use_reduce(dep,
						uselist=current_use, flat=True)
				except InvalidDependString:
					if not parent.installed:
						raise
					reduced_dep = None

				if reduced_dep is not None and \
					parent_atom not in reduced_dep:
					#We found an assignment that removes the atom from 'dep'.
					#Make sure it doesn't conflict with REQUIRED_USE.
					required_use = parent.metadata.get("REQUIRED_USE", "")

					if check_required_use(required_use, current_use, parent.iuse.is_valid_flag):
						use = self.depgraph._pkg_use_enabled(parent)
						solution = set()
						for flag, state in zip(affecting_use, use_state):
							if state == "enabled" and \
								flag not in use:
								solution.add((flag, True))
							elif state == "disabled" and \
								flag in use:
								solution.add((flag, False))
						solutions.add(frozenset(solution))

			for solution in solutions:
				ignore_solution = False
				for other_solution in solutions:
					if solution is other_solution:
						continue
					if solution.issuperset(other_solution):
						ignore_solution = True
				if ignore_solution:
					continue

				#Check if a USE change conflicts with use requirements of the parents.
				#If a requiremnet is hard, ignore the suggestion.
				#If the requirment is conditional, warn the user that other changes might be needed.
				followup_change = False
				parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(changed_parent)
				for ppkg, atom in parent_parent_atoms:

					atom = atom.unevaluated_atom
					if not atom.use:
						continue

					for flag, state in solution:
						if flag in atom.use.enabled or flag in atom.use.disabled:
							ignore_solution = True
							break
						elif atom.use.conditional:
							for flags in atom.use.conditional.values():
								if flag in flags:
									followup_change = True
									break

					if ignore_solution:
						break

				if ignore_solution:
					continue

				changes = []
				for flag, state in solution:
					if state:
						changes.append(colorize("red", "+"+flag))
					else:
						changes.append(colorize("blue", "-"+flag))
				msg = "- %s (Change USE: %s)\n" \
					% (parent.cpv, " ".join(changes))
				if followup_change:
					msg += " (This change might require USE changes on parent packages.)"
				suggestions.append(msg)
				final_solutions.setdefault(pkg, set()).add(solution)

		return final_solutions, suggestions
Пример #2
0
    def _find_suggestions(self):
        if not self.shortest_cycle:
            return None, None

        suggestions = []
        final_solutions = {}

        for pos, pkg in enumerate(self.shortest_cycle):
            parent = self.shortest_cycle[pos - 1]
            priorities = self.graph.nodes[parent][0][pkg]
            parent_atoms = self.all_parent_atoms.get(pkg)

            if priorities[-1].buildtime:
                dep = parent.metadata["DEPEND"]
            elif priorities[-1].runtime:
                dep = parent.metadata["RDEPEND"]

            for ppkg, atom in parent_atoms:
                if ppkg == parent:
                    changed_parent = ppkg
                    parent_atom = atom.unevaluated_atom
                    break

            try:
                affecting_use = extract_affecting_use(
                    dep, parent_atom, eapi=parent.metadata["EAPI"])
            except InvalidDependString:
                if not parent.installed:
                    raise
                affecting_use = set()

            # Make sure we don't want to change a flag that is
            #	a) in use.mask or use.force
            #	b) changed by autounmask

            usemask, useforce = self._get_use_mask_and_force(parent)
            autounmask_changes = self._get_autounmask_changes(parent)
            untouchable_flags = frozenset(
                chain(usemask, useforce, autounmask_changes))

            affecting_use.difference_update(untouchable_flags)

            #If any of the flags we're going to touch is in REQUIRED_USE, add all
            #other flags in REQUIRED_USE to affecting_use, to not lose any solution.
            required_use_flags = get_required_use_flags(
                parent.metadata.get("REQUIRED_USE", ""))

            if affecting_use.intersection(required_use_flags):
                # TODO: Find out exactly which REQUIRED_USE flags are
                # entangled with affecting_use. We have to limit the
                # number of flags since the number of loops is
                # exponentially related (see bug #374397).
                total_flags = set()
                total_flags.update(affecting_use, required_use_flags)
                total_flags.difference_update(untouchable_flags)
                if len(total_flags) <= 10:
                    affecting_use = total_flags

            affecting_use = tuple(affecting_use)

            if not affecting_use:
                continue

            #We iterate over all possible settings of these use flags and gather
            #a set of possible changes
            #TODO: Use the information encoded in REQUIRED_USE
            solutions = set()
            for use_state in product(("disabled", "enabled"),
                                     repeat=len(affecting_use)):
                current_use = set(self.depgraph._pkg_use_enabled(parent))
                for flag, state in zip(affecting_use, use_state):
                    if state == "enabled":
                        current_use.add(flag)
                    else:
                        current_use.discard(flag)
                try:
                    reduced_dep = use_reduce(dep,
                                             uselist=current_use,
                                             flat=True)
                except InvalidDependString:
                    if not parent.installed:
                        raise
                    reduced_dep = None

                if reduced_dep is not None and \
                 parent_atom not in reduced_dep:
                    #We found an assignment that removes the atom from 'dep'.
                    #Make sure it doesn't conflict with REQUIRED_USE.
                    required_use = parent.metadata.get("REQUIRED_USE", "")

                    if check_required_use(required_use, current_use,
                                          parent.iuse.is_valid_flag):
                        use = self.depgraph._pkg_use_enabled(parent)
                        solution = set()
                        for flag, state in zip(affecting_use, use_state):
                            if state == "enabled" and \
                             flag not in use:
                                solution.add((flag, True))
                            elif state == "disabled" and \
                             flag in use:
                                solution.add((flag, False))
                        solutions.add(frozenset(solution))

            for solution in solutions:
                ignore_solution = False
                for other_solution in solutions:
                    if solution is other_solution:
                        continue
                    if solution.issuperset(other_solution):
                        ignore_solution = True
                if ignore_solution:
                    continue

                #Check if a USE change conflicts with use requirements of the parents.
                #If a requiremnet is hard, ignore the suggestion.
                #If the requirment is conditional, warn the user that other changes might be needed.
                followup_change = False
                parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(
                    changed_parent)
                for ppkg, atom in parent_parent_atoms:

                    atom = atom.unevaluated_atom
                    if not atom.use:
                        continue

                    for flag, state in solution:
                        if flag in atom.use.enabled or flag in atom.use.disabled:
                            ignore_solution = True
                            break
                        elif atom.use.conditional:
                            for flags in atom.use.conditional.values():
                                if flag in flags:
                                    followup_change = True
                                    break

                    if ignore_solution:
                        break

                if ignore_solution:
                    continue

                changes = []
                for flag, state in solution:
                    if state:
                        changes.append(colorize("red", "+" + flag))
                    else:
                        changes.append(colorize("blue", "-" + flag))
                msg = "- %s (Change USE: %s)\n" \
                 % (parent.cpv, " ".join(changes))
                if followup_change:
                    msg += " (This change might require USE changes on parent packages.)"
                suggestions.append(msg)
                final_solutions.setdefault(pkg, set()).add(solution)

        return final_solutions, suggestions
	def testExtractAffectingUSE(self):
		test_cases = (
			("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "A", ("a",)),
			("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "B", ("b",)),
			("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "C", ("c",)),
			("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "D", ("d",)),
			
			("a? ( b? ( AB ) )", "AB", ("a", "b")),
			("a? ( b? ( c? ( ABC ) ) )", "ABC", ("a", "b", "c")),

			("a? ( A b? ( c? ( ABC ) AB ) )", "A", ("a",)),
			("a? ( A b? ( c? ( ABC ) AB ) )", "AB", ("a", "b")),
			("a? ( A b? ( c? ( ABC ) AB ) )", "ABC", ("a", "b", "c")),
			("a? ( A b? ( c? ( ABC ) AB ) ) X", "X", []),
			("X a? ( A b? ( c? ( ABC ) AB ) )", "X", []),

			("ab? ( || ( A B ) )", "A", ("ab",)),
			("!ab? ( || ( A B ) )", "B", ("ab",)),
			("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "A", ("ab",)),
			("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "B", ("ab", "b")),
			("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "C", ("ab", "b")),

			("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "A", ("ab",)),
			("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "B", ("ab", "b")),
			("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )", "C", ("ab", "b")),

			("a? ( A )", "B", []),

			("a? ( || ( A B ) )", "B", ["a"]),

			# test USE dep defaults for bug #363073
			("a? ( >=dev-lang/php-5.2[pcre(+)] )", ">=dev-lang/php-5.2[pcre(+)]", ["a"]),
		)

		test_cases_xfail = (
			("? ( A )", "A"),
			("!? ( A )", "A"),
			("( A", "A"),
			("A )", "A"),
			
			("||( A B )", "A"),
			("|| (A B )", "A"),
			("|| ( A B)", "A"),
			("|| ( A B", "A"),
			("|| A B )", "A"),
			("|| A B", "A"),
			("|| ( A B ) )", "A"),
			("|| || B C", "A"),
			("|| ( A B || )", "A"),
			("a? A", "A"),
			("( || ( || || ( A ) foo? ( B ) ) )", "A"),
			("( || ( || bar? ( A ) foo? ( B ) ) )", "A"),
		)

		for dep, atom, expected in test_cases:
			expected = set(expected)
			result = extract_affecting_use(dep, atom, eapi="0")
			fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
				" ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
			self.assertEqual(result, expected, fail_msg)

		for dep, atom in test_cases_xfail:
			fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
				" ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
			self.assertRaisesMsg(fail_msg, \
				InvalidDependString, extract_affecting_use, dep, atom, eapi="0")
    def testExtractAffectingUSE(self):
        test_cases = (
            ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "A", ("a", )),
            ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "B", ("b", )),
            ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "C", ("c", )),
            ("a? ( A ) !b? ( B ) !c? ( C ) d? ( D )", "D", ("d", )),
            ("a? ( b? ( AB ) )", "AB", ("a", "b")),
            ("a? ( b? ( c? ( ABC ) ) )", "ABC", ("a", "b", "c")),
            ("a? ( A b? ( c? ( ABC ) AB ) )", "A", ("a", )),
            ("a? ( A b? ( c? ( ABC ) AB ) )", "AB", ("a", "b")),
            ("a? ( A b? ( c? ( ABC ) AB ) )", "ABC", ("a", "b", "c")),
            ("a? ( A b? ( c? ( ABC ) AB ) ) X", "X", []),
            ("X a? ( A b? ( c? ( ABC ) AB ) )", "X", []),
            ("ab? ( || ( A B ) )", "A", ("ab", )),
            ("!ab? ( || ( A B ) )", "B", ("ab", )),
            ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "A", ("ab", )),
            ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "B", ("ab", "b")),
            ("ab? ( || ( A || ( b? ( || ( B C ) ) ) ) )", "C", ("ab", "b")),
            ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )",
             "A", ("ab", )),
            ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )",
             "B", ("ab", "b")),
            ("( ab? ( || ( ( A ) || ( b? ( ( ( || ( B ( C ) ) ) ) ) ) ) ) )",
             "C", ("ab", "b")),
            ("a? ( A )", "B", []),
            ("a? ( || ( A B ) )", "B", ["a"]),

            # test USE dep defaults for bug #363073
            ("a? ( >=dev-lang/php-5.2[pcre(+)] )",
             ">=dev-lang/php-5.2[pcre(+)]", ["a"]),
        )

        test_cases_xfail = (
            ("? ( A )", "A"),
            ("!? ( A )", "A"),
            ("( A", "A"),
            ("A )", "A"),
            ("||( A B )", "A"),
            ("|| (A B )", "A"),
            ("|| ( A B)", "A"),
            ("|| ( A B", "A"),
            ("|| A B )", "A"),
            ("|| A B", "A"),
            ("|| ( A B ) )", "A"),
            ("|| || B C", "A"),
            ("|| ( A B || )", "A"),
            ("a? A", "A"),
            ("( || ( || || ( A ) foo? ( B ) ) )", "A"),
            ("( || ( || bar? ( A ) foo? ( B ) ) )", "A"),
        )

        for dep, atom, expected in test_cases:
            expected = set(expected)
            result = extract_affecting_use(dep, atom, eapi="0")
            fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
             " ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
            self.assertEqual(result, expected, fail_msg)

        for dep, atom in test_cases_xfail:
            fail_msg = "dep: " + dep + ", atom: " + atom + ", got: " + \
             " ".join(sorted(result)) + ", expected: " + " ".join(sorted(expected))
            self.assertRaisesMsg(fail_msg, \
             InvalidDependString, extract_affecting_use, dep, atom, eapi="0")
Пример #5
0
	def _find_suggestions(self):
		if not self.shortest_cycle:
			return None, None

		suggestions = []
		final_solutions = {}

		for pos, pkg in enumerate(self.shortest_cycle):
			parent = self.shortest_cycle[pos-1]
			priorities = self.graph.nodes[parent][0][pkg]
			parent_atoms = self.all_parent_atoms.get(pkg)

			if priorities[-1].buildtime:
				dep = parent.metadata["DEPEND"]
			elif priorities[-1].runtime:
				dep = parent.metadata["RDEPEND"]

			for ppkg, atom in parent_atoms:
				if ppkg == parent:
					changed_parent = ppkg
					parent_atom = atom.unevaluated_atom
					break

			affecting_use = extract_affecting_use(dep, parent_atom)

			# Make sure we don't want to change a flag that is 
			#	a) in use.mask or use.force
			#	b) changed by autounmask
			
			usemask, useforce = self._get_use_mask_and_force(parent)
			autounmask_changes = self._get_autounmask_changes(parent)
			untouchable_flags = frozenset(chain(usemask, useforce, autounmask_changes))

			affecting_use.difference_update(untouchable_flags)

			#If any of the flags we're going to touch is in REQUIRED_USE, add all
			#other flags in REQUIRED_USE to affecting_use, to not lose any solution.
			required_use_flags = get_required_use_flags(parent.metadata["REQUIRED_USE"])

			if affecting_use.intersection(required_use_flags):
				affecting_use.update(required_use_flags)
				affecting_use.difference_update(untouchable_flags)

			affecting_use = tuple(affecting_use)

			if not affecting_use:
				continue

			#We iterate over all possible settings of these use flags and gather
			#a set of possible changes
			#TODO: Use the information encoded in REQUIRED_USE
			use_state = []
			for flag in affecting_use:
				use_state.append("disabled")

			def _next_use_state(state, id=None):
				if id is None:
					id = len(state)-1

				if id == 0 and state[0] == "enabled":
					return False

				if state[id] == "disabled":
					state[id] = "enabled"
					for i in range(id+1,len(state)):
						state[i] = "disabled"
					return True
				else:
					return _next_use_state(state, id-1)

			solutions = set()
			while(True):
				current_use = set(self.depgraph._pkg_use_enabled(parent))
				for flag, state in zip(affecting_use, use_state):
					if state == "enabled":
						current_use.add(flag)
					else:
						current_use.discard(flag)
				reduced_dep = use_reduce(dep,
					uselist=current_use, flat=True)

				if parent_atom not in reduced_dep:
					#We found an assignment that removes the atom from 'dep'.
					#Make sure it doesn't conflict with REQUIRED_USE.
					required_use = parent.metadata["REQUIRED_USE"]

					if check_required_use(required_use, current_use, parent.iuse.is_valid_flag):
						use = self.depgraph._pkg_use_enabled(parent)
						solution = set()
						for flag, state in zip(affecting_use, use_state):
							if state == "enabled" and \
								flag not in use:
								solution.add((flag, True))
							elif state == "disabled" and \
								flag in use:
								solution.add((flag, False))
						solutions.add(frozenset(solution))

				if not _next_use_state(use_state):
					break

			for solution in solutions:
				ignore_solution = False
				for other_solution in solutions:
					if solution is other_solution:
						continue
					if solution.issuperset(other_solution):
						ignore_solution = True
				if ignore_solution:
					continue

				#Check if a USE change conflicts with use requirements of the parents.
				#If a requiremnet is hard, ignore the suggestion.
				#If the requirment is conditional, warn the user that other changes might be needed.
				followup_change = False
				parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(changed_parent)
				for ppkg, atom in parent_parent_atoms:

					atom = atom.unevaluated_atom
					if not atom.use:
						continue

					for flag, state in solution:
						if flag in atom.use.enabled or flag in atom.use.disabled:
							ignore_solution = True
							break
						elif atom.use.conditional:
							for flags in atom.use.conditional.values():
								if flag in flags:
									followup_change = True
									break

					if ignore_solution:
						break

				if ignore_solution:
					continue

				changes = []
				for flag, state in solution:
					if state:
						changes.append(colorize("red", "+"+flag))
					else:
						changes.append(colorize("blue", "-"+flag))
				msg = "- %s (Change USE: %s)\n" \
					% (parent.cpv, " ".join(changes))
				if followup_change:
					msg += " (This change might require USE changes on parent packages.)"
				suggestions.append(msg)
				final_solutions.setdefault(pkg, set()).add(solution)

		return final_solutions, suggestions
Пример #6
0
    def _find_suggestions(self):
        if not self.shortest_cycle:
            return None, None

        suggestions = []
        final_solutions = {}

        for pos, pkg in enumerate(self.shortest_cycle):
            parent = self.shortest_cycle[pos - 1]
            priorities = self.graph.nodes[parent][0][pkg]
            parent_atoms = self.all_parent_atoms.get(pkg)

            if priorities[-1].buildtime:
                dep = parent.metadata["DEPEND"]
            elif priorities[-1].runtime:
                dep = parent.metadata["RDEPEND"]

            for ppkg, atom in parent_atoms:
                if ppkg == parent:
                    changed_parent = ppkg
                    parent_atom = atom.unevaluated_atom
                    break

            affecting_use = extract_affecting_use(dep, parent_atom)

            # Make sure we don't want to change a flag that is
            #	a) in use.mask or use.force
            #	b) changed by autounmask

            usemask, useforce = self._get_use_mask_and_force(parent)
            autounmask_changes = self._get_autounmask_changes(parent)
            untouchable_flags = frozenset(
                chain(usemask, useforce, autounmask_changes))

            affecting_use.difference_update(untouchable_flags)

            #If any of the flags we're going to touch is in REQUIRED_USE, add all
            #other flags in REQUIRED_USE to affecting_use, to not lose any solution.
            required_use_flags = get_required_use_flags(
                parent.metadata["REQUIRED_USE"])

            if affecting_use.intersection(required_use_flags):
                affecting_use.update(required_use_flags)
                affecting_use.difference_update(untouchable_flags)

            affecting_use = tuple(affecting_use)

            if not affecting_use:
                continue

            #We iterate over all possible settings of these use flags and gather
            #a set of possible changes
            #TODO: Use the information encoded in REQUIRED_USE
            use_state = []
            for flag in affecting_use:
                use_state.append("disabled")

            def _next_use_state(state, id=None):
                if id is None:
                    id = len(state) - 1

                if id == 0 and state[0] == "enabled":
                    return False

                if state[id] == "disabled":
                    state[id] = "enabled"
                    for i in range(id + 1, len(state)):
                        state[i] = "disabled"
                    return True
                else:
                    return _next_use_state(state, id - 1)

            solutions = set()
            while (True):
                current_use = set(self.depgraph._pkg_use_enabled(parent))
                for flag, state in zip(affecting_use, use_state):
                    if state == "enabled":
                        current_use.add(flag)
                    else:
                        current_use.discard(flag)
                reduced_dep = use_reduce(dep, uselist=current_use, flat=True)

                if parent_atom not in reduced_dep:
                    #We found an assignment that removes the atom from 'dep'.
                    #Make sure it doesn't conflict with REQUIRED_USE.
                    required_use = parent.metadata["REQUIRED_USE"]

                    if check_required_use(required_use, current_use,
                                          parent.iuse.is_valid_flag):
                        use = self.depgraph._pkg_use_enabled(parent)
                        solution = set()
                        for flag, state in zip(affecting_use, use_state):
                            if state == "enabled" and \
                             flag not in use:
                                solution.add((flag, True))
                            elif state == "disabled" and \
                             flag in use:
                                solution.add((flag, False))
                        solutions.add(frozenset(solution))

                if not _next_use_state(use_state):
                    break

            for solution in solutions:
                ignore_solution = False
                for other_solution in solutions:
                    if solution is other_solution:
                        continue
                    if solution.issuperset(other_solution):
                        ignore_solution = True
                if ignore_solution:
                    continue

                #Check if a USE change conflicts with use requirements of the parents.
                #If a requiremnet is hard, ignore the suggestion.
                #If the requirment is conditional, warn the user that other changes might be needed.
                followup_change = False
                parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(
                    changed_parent)
                for ppkg, atom in parent_parent_atoms:

                    atom = atom.unevaluated_atom
                    if not atom.use:
                        continue

                    for flag, state in solution:
                        if flag in atom.use.enabled or flag in atom.use.disabled:
                            ignore_solution = True
                            break
                        elif atom.use.conditional:
                            for flags in atom.use.conditional.values():
                                if flag in flags:
                                    followup_change = True
                                    break

                    if ignore_solution:
                        break

                if ignore_solution:
                    continue

                changes = []
                for flag, state in solution:
                    if state:
                        changes.append(colorize("red", "+" + flag))
                    else:
                        changes.append(colorize("blue", "-" + flag))
                msg = "- %s (Change USE: %s)\n" \
                 % (parent.cpv, " ".join(changes))
                if followup_change:
                    msg += " (This change might require USE changes on parent packages.)"
                suggestions.append(msg)
                final_solutions.setdefault(pkg, set()).add(solution)

        return final_solutions, suggestions