Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Auto resolve cyclic USE conflicts by trying the first suggestion #1385

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/_emerge/Package.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,15 +405,15 @@ def _validate_deps(self):
except InvalidData as e:
self._invalid_metadata(k + ".syntax", f"{k}: {e}")

def copy(self):
def copy(self, operation=None):
return Package(
built=self.built,
cpv=self.cpv,
depth=self.depth,
installed=self.installed,
metadata=self._raw_metadata,
onlydeps=self.onlydeps,
operation=self.operation,
operation=operation or self.operation,
root_config=self.root_config,
type_name=self.type_name,
)
Expand Down
77 changes: 50 additions & 27 deletions lib/_emerge/depgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -8936,15 +8936,7 @@ def _validate_blockers(self):

if not unresolved_blocks and depends_on_order:
for inst_pkg, inst_task in depends_on_order:
uninst_task = Package(
built=inst_pkg.built,
cpv=inst_pkg.cpv,
installed=inst_pkg.installed,
metadata=inst_pkg._metadata,
operation="uninstall",
root_config=inst_pkg.root_config,
type_name=inst_pkg.type_name,
)
uninst_task = inst_pkg.copy(operation="uninstall")
# Enforce correct merge order with a hard dep.
self._dynamic_config.digraph.addnode(
uninst_task, inst_task, priority=BlockerDepPriority.instance
Expand Down Expand Up @@ -9232,6 +9224,7 @@ def _serialize_tasks(self):
complete = "complete" in self._dynamic_config.myparams
ignore_world = self._dynamic_config.myparams.get("ignore_world", False)
asap_nodes = []
changed_pkgs = {}

def get_nodes(**kwargs):
"""
Expand Down Expand Up @@ -9909,8 +9902,6 @@ def find_smallest_cycle(mergeable_nodes, local_priority_range):
continue

if not selected_nodes:
self._dynamic_config._circular_deps_for_display = mygraph

unsolved_cycle = False
if self._dynamic_config._allow_backtracking:
backtrack_infos = self._dynamic_config._backtrack_infos
Expand All @@ -9935,11 +9926,55 @@ def find_smallest_cycle(mergeable_nodes, local_priority_range):
)

if unsolved_cycle or not self._dynamic_config._allow_backtracking:
self._dynamic_config._circular_deps_for_display = mygraph
self._dynamic_config._skip_restart = True
raise self._unknown_internal_error()
else:
self._dynamic_config._need_restart = True
uniq_selected_nodes = set()
while True:
changed_pkg = None
handler = circular_dependency_handler(self, mygraph)

if handler.solutions:
pkg = list(handler.solutions.keys())[0]
parent, solution = list(handler.solutions[pkg])[0]
solution = list(solution)[0]
changed_pkg = changed_pkgs.get(parent, parent)
enabled = set(changed_pkg.use.enabled)

if solution[1]:
enabled.add(solution[0])
else:
enabled.remove(solution[0])

# To avoid unnecessarily complexity, only try to
# automatically resolve the conflict if the solution
# does not pull in additional dependencies.
before = self._flatten_atoms(parent, parent.use.enabled)
after = self._flatten_atoms(changed_pkg, frozenset(enabled))

if before.issuperset(after):
changed_pkgs[parent] = changed_pkg.with_use(enabled)
uniq_selected_nodes.update((pkg, parent))
mygraph.remove_edge(pkg, parent)
ignored_uninstall_tasks = set(
uninst_task
for uninst_task in ignored_uninstall_tasks
if uninst_task.cp != pkg.cp or uninst_task.slot != pkg.slot
)
else:
changed_pkg = None

raise self._unknown_internal_error()
if changed_pkg is not None:
pass
elif uniq_selected_nodes:
break
else:
self._dynamic_config._circular_deps_for_display = mygraph
self._dynamic_config._need_restart = True
raise self._unknown_internal_error()

selected_nodes = list(changed_pkgs.values()) + list(uniq_selected_nodes)

# At this point, we've succeeded in selecting one or more nodes, so
# reset state variables for leaf node selection.
Expand All @@ -9965,20 +10000,8 @@ def find_smallest_cycle(mergeable_nodes, local_priority_range):
if inst_pkg:
# The package will be replaced by this one, so remove
# the corresponding Uninstall task if necessary.
inst_pkg = inst_pkg[0]
uninst_task = Package(
built=inst_pkg.built,
cpv=inst_pkg.cpv,
installed=inst_pkg.installed,
metadata=inst_pkg._metadata,
operation="uninstall",
root_config=inst_pkg.root_config,
type_name=inst_pkg.type_name,
)
try:
mygraph.remove(uninst_task)
except KeyError:
pass
uninst_task = inst_pkg[0].copy(operation="uninstall")
mygraph.discard(uninst_task)

if (
uninst_task is not None
Expand Down
2 changes: 1 addition & 1 deletion lib/_emerge/resolver/circular_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def _find_suggestions(self):
" (This change might require USE changes on parent packages.)"
)
suggestions.append(msg)
final_solutions.setdefault(pkg, set()).add(solution)
final_solutions.setdefault(pkg, set()).add((parent, solution))

return final_solutions, suggestions

Expand Down
Loading