def testCheckRequiredUse(self):
		test_cases = (
			("a b c", ["a", "b", "c"]),

			("|| ( a b c )", ["a", "b", "c"]),
			("^^ ( a b c )", ["a", "b", "c"]),

			("|| ( a b ^^ ( d e f ) )", ["a", "b", "d", "e", "f"]),
			("^^ ( a b || ( d e f ) )", ["a", "b", "d", "e", "f"]),

			("( ^^ ( a ( b ) ( || ( ( d e ) ( f ) ) ) ) )", ["a", "b", "d", "e", "f"]),

			("a? ( ^^ ( b c ) )", ["a", "b", "c"]),
			("a? ( ^^ ( !b !d? ( c ) ) )", ["a", "b", "c", "d"]),
		)

		test_cases_xfail = (
			("^^ ( || ( a b ) ^^ ( b c )"),
			("^^( || ( a b ) ^^ ( b c ) )"),
			("^^ || ( a b ) ^^ ( b c )"),
			("^^ ( ( || ) ( a b ) ^^ ( b c ) )"),
			("^^ ( || ( a b ) ) ^^ ( b c ) )"),
		)

		for required_use, expected in test_cases:
			result = get_required_use_flags(required_use)
			expected = set(expected)
			self.assertEqual(result, expected, \
				"REQUIRED_USE: '%s', expected: '%s', got: '%s'" % (required_use, expected, result))

		for required_use in test_cases_xfail:
			self.assertRaisesMsg("REQUIRED_USE: '%s'" % (required_use,), \
				InvalidDependString, get_required_use_flags, required_use)
	def testCheckRequiredUse(self):
		test_cases = (
			("a b c", ["a", "b", "c"]),

			("|| ( a b c )", ["a", "b", "c"]),
			("^^ ( a b c )", ["a", "b", "c"]),
			("?? ( a b c )", ["a", "b", "c"]),
			("?? ( )", []),

			("|| ( a b ^^ ( d e f ) )", ["a", "b", "d", "e", "f"]),
			("^^ ( a b || ( d e f ) )", ["a", "b", "d", "e", "f"]),

			("( ^^ ( a ( b ) ( || ( ( d e ) ( f ) ) ) ) )", ["a", "b", "d", "e", "f"]),

			("a? ( ^^ ( b c ) )", ["a", "b", "c"]),
			("a? ( ^^ ( !b !d? ( c ) ) )", ["a", "b", "c", "d"]),
		)

		test_cases_xfail = (
			("^^ ( || ( a b ) ^^ ( b c )"),
			("^^( || ( a b ) ^^ ( b c ) )"),
			("^^ || ( a b ) ^^ ( b c )"),
			("^^ ( ( || ) ( a b ) ^^ ( b c ) )"),
			("^^ ( || ( a b ) ) ^^ ( b c ) )"),
		)

		for required_use, expected in test_cases:
			result = get_required_use_flags(required_use)
			expected = set(expected)
			self.assertEqual(result, expected, \
				"REQUIRED_USE: '%s', expected: '%s', got: '%s'" % (required_use, expected, result))

		for required_use in test_cases_xfail:
			self.assertRaisesMsg("REQUIRED_USE: '%s'" % (required_use,), \
				InvalidDependString, get_required_use_flags, required_use)
	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
Exemplo n.º 4
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
Exemplo n.º 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
Exemplo n.º 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