"--fetchonly", "--fetch-all-uri",
"--getbinpkg", "--getbinpkgonly",
"--help", "--ignore-default-opts",
+"--keep-going",
"--noconfmem",
"--newuse", "--nocolor",
"--nodeps", "--noreplace",
world_set.update(all_added)
world_set.unlock()
- def loadResumeCommand(self, resume_data):
+ def loadResumeCommand(self, resume_data, skip_masked=False):
"""
Add a resume command to the graph and validate it in the process. This
will raise a PackageNotFound exception if a package is not available.
if not isinstance(mergelist, list):
mergelist = []
- skipfirst = "--skipfirst" in self.myopts
fakedb = self.mydbapi
trees = self.trees
serialized_tasks = []
root_config = self.roots[pkg.root]
if "merge" == pkg.operation and \
not visible(root_config.settings, pkg):
- if skipfirst:
+ if skip_masked:
masked_tasks.append(Dependency(root=pkg.root, parent=pkg))
else:
self._unsatisfied_deps_for_display.append(
# was dropped via --skipfirst. It makes the
# resume list invalid, so convert it to a
# UnsatisfiedResumeDep exception.
- raise self.UnsatisfiedResumeDep(
+ raise self.UnsatisfiedResumeDep(self,
masked_tasks + self._unsatisfied_deps)
self._serialized_tasks_cache = None
try:
can occur when a required package is dropped from the
merge list via --skipfirst.
"""
+ def __init__(self, depgraph, value):
+ portage.exception.PortageException.__init__(self, value)
+ self.depgraph = depgraph
class _internal_exception(portage.exception.PortageException):
def __init__(self, value=""):
"--fetchonly", "--fetch-all-uri",
"--nodeps", "--pretend"])
- def __init__(self, settings, trees, myopts):
+ def __init__(self, settings, trees, mtimedb, myopts,
+ spinner, mergelist, favorites):
self.settings = settings
self.target_root = settings["ROOT"]
self.trees = trees
self.myopts = myopts
+ self._spinner = spinner
+ self._mtimedb = mtimedb
+ self._mergelist = mergelist
+ self._favorites = favorites
self.edebug = 0
if settings.get("PORTAGE_DEBUG", "") == "1":
self.edebug = 1
return blocker_dblinks
- def merge(self, mylist, favorites, mtimedb):
- try:
- return self._merge(mylist, favorites, mtimedb)
- finally:
- if self._spawned_pids:
- from portage import process
- process.spawned_pids.extend(self._spawned_pids)
- self._spawned_pids = []
+ def merge(self):
+
+ keep_going = "--keep-going" in self.myopts
+
+ while True:
+ try:
+ rval = self._merge()
+ finally:
+ spawned_pids = self._spawned_pids
+ while spawned_pids:
+ pid = spawned_pids.pop()
+ try:
+ if os.waitpid(pid, os.WNOHANG) == (0, 0):
+ os.kill(pid, signal.SIGTERM)
+ os.waitpid(pid, 0)
+ except OSError:
+ pass # cleaned up elsewhere.
+
+ if rval == os.EX_OK or not keep_going:
+ break
+ mtimedb = self._mtimedb
+ if "resume" not in mtimedb:
+ break
+ mergelist = self._mtimedb["resume"].get("mergelist")
+ if not mergelist:
+ break
+ if mergelist[0][-1] != "merge":
+ break
+ # Skip the first one because it failed to build or install.
+ del mergelist[0]
+ if not mergelist:
+ break
+ mylist = self._calc_resume_list()
+ clear_caches(self.trees)
+ if not mylist:
+ break
+ self.curval += 1
+ self._mergelist = mylist
+
+ return rval
+
+ def _calc_resume_list(self):
+ """
+ Use the current resume list to calculate a new one,
+ dropping any packages with unsatisfied deps.
+ """
+ print colorize("GOOD", "*** Resuming merge...")
+
+ show_spinner = "--quiet" not in self.myopts and \
+ "--nodeps" not in self.myopts
+
+ if show_spinner:
+ print "Calculating dependencies ",
+
+ myparams = create_depgraph_params(self.myopts, None)
+ success, mydepgraph, dropped_tasks = resume_depgraph(
+ self.settings, self.trees, self._mtimedb, self.myopts,
+ myparams, self._spinner, skip_unsatisfied=True)
+
+ if show_spinner:
+ print "\b\b... done!"
+
+ if not success:
+ mydepgraph.display_problems()
+ return None
+
+ if dropped_tasks:
+ portage.writemsg("!!! One or more packages have been " + \
+ "dropped due to\n" + \
+ "!!! masking or unsatisfied dependencies:\n\n",
+ noiselevel=-1)
+ for task in dropped_tasks:
+ portage.writemsg(" " + str(task) + "\n", noiselevel=-1)
+ portage.writemsg("\n", noiselevel=-1)
+
+ mylist = mydepgraph.altlist()
+ mydepgraph.break_refs(mylist)
+ return mylist
def _poll_child_processes(self):
"""
pass
spawned_pids.remove(pid)
- def _merge(self, mylist, favorites, mtimedb):
+ def _merge(self):
+ mylist = self._mergelist
+ favorites = self._favorites
+ mtimedb = self._mtimedb
from portage.elog import elog_process
from portage.elog.filtering import filter_mergephases
buildpkgonly = "--buildpkgonly" in self.myopts
else:
print "Number removed: "+str(len(cleanlist))
+def resume_depgraph(settings, trees, mtimedb, myopts, myparams, spinner,
+ skip_masked=False, skip_unsatisfied=False):
+ """
+ Construct a depgraph for the given resume list. This will raise
+ PackageNotFound or depgraph.UnsatisfiedResumeDep when necessary.
+ @rtype: tuple
+ @returns: (success, depgraph, dropped_tasks)
+ """
+ mergelist = mtimedb["resume"]["mergelist"]
+ dropped_tasks = set()
+ while True:
+ mydepgraph = depgraph(settings, trees,
+ myopts, myparams, spinner)
+ try:
+ success = mydepgraph.loadResumeCommand(mtimedb["resume"],
+ skip_masked=skip_masked)
+ except depgraph.UnsatisfiedResumeDep, e:
+ if not skip_unsatisfied:
+ raise
+
+ graph = mydepgraph.digraph
+ unsatisfied_parents = dict((dep.parent, dep.parent) \
+ for dep in e.value)
+ traversed_nodes = set()
+ unsatisfied_stack = list(unsatisfied_parents)
+ while unsatisfied_stack:
+ pkg = unsatisfied_stack.pop()
+ if pkg in traversed_nodes:
+ continue
+ traversed_nodes.add(pkg)
+
+ # If this package was pulled in by a parent
+ # package scheduled for merge, removing this
+ # package may cause the the parent package's
+ # dependency to become unsatisfied.
+ for parent_node in graph.parent_nodes(pkg):
+ if not isinstance(parent_node, Package) \
+ or parent_node.operation != "merge":
+ continue
+ unsatisfied = \
+ graph.child_nodes(parent_node,
+ ignore_priority=DepPriority.SOFT)
+ if pkg in unsatisfied:
+ unsatisfied_parents[parent_node] = parent_node
+ unsatisfied_stack.append(parent_node)
+
+ pruned_mergelist = [x for x in mergelist \
+ if isinstance(x, list) and \
+ tuple(x) not in unsatisfied_parents]
+
+ # It shouldn't happen, but if the size of mergelist
+ # does not decrease for some reason then the loop
+ # will be infinite. Therefore, if that case ever
+ # occurs for some reason, raise the exception to
+ # break out of the loop.
+ if not pruned_mergelist or \
+ len(pruned_mergelist) == len(mergelist):
+ raise
+ mergelist[:] = pruned_mergelist
+ dropped_tasks.update(unsatisfied_parents)
+ del e, graph, traversed_nodes, \
+ unsatisfied_parents, unsatisfied_stack
+ continue
+ else:
+ break
+ return (success, mydepgraph, dropped_tasks)
+
def action_build(settings, trees, mtimedb,
myopts, myaction, myfiles, spinner):
del mergelist[i]
break
- dropped_tasks = set()
-
+ skip_masked = "--skipfirst" in myopts
+ skip_unsatisfied = "--skipfirst" in myopts
success = False
+ mydepgraph = None
try:
- while True:
- mydepgraph = depgraph(settings, trees,
- myopts, myparams, spinner)
- try:
- success = mydepgraph.loadResumeCommand(mtimedb["resume"])
- except depgraph.UnsatisfiedResumeDep, e:
- if "--skipfirst" not in myopts:
- raise
-
- graph = mydepgraph.digraph
- unsatisfied_parents = dict((dep.parent, dep.parent) \
- for dep in e.value)
- traversed_nodes = set()
- unsatisfied_stack = list(unsatisfied_parents)
- while unsatisfied_stack:
- pkg = unsatisfied_stack.pop()
- if pkg in traversed_nodes:
- continue
- traversed_nodes.add(pkg)
-
- # If this package was pulled in by a parent
- # package scheduled for merge, removing this
- # package may cause the the parent package's
- # dependency to become unsatisfied.
- for parent_node in graph.parent_nodes(pkg):
- if not isinstance(parent_node, Package) \
- or parent_node.operation != "merge":
- continue
- unsatisfied = \
- graph.child_nodes(parent_node,
- ignore_priority=DepPriority.SOFT)
- if pkg in unsatisfied:
- unsatisfied_parents[parent_node] = parent_node
- unsatisfied_stack.append(parent_node)
-
- pruned_mergelist = [x for x in mergelist \
- if isinstance(x, list) and \
- tuple(x) not in unsatisfied_parents]
-
- # It shouldn't happen, but if the size of mergelist
- # does not decrease for some reason then the loop
- # will be infinite. Therefore, if that case ever
- # occurs for some reason, raise the exception to
- # break out of the loop.
- if not pruned_mergelist or \
- len(pruned_mergelist) == len(mergelist):
- raise
- mergelist[:] = pruned_mergelist
- dropped_tasks.update(unsatisfied_parents)
- del e, graph, traversed_nodes, \
- unsatisfied_parents, unsatisfied_stack
- continue
- else:
- break
+ success, mydepgraph, dropped_tasks = resume_depgraph(
+ settings, trees, mtimedb, myopts, myparams, spinner,
+ skip_masked=skip_masked, skip_unsatisfied=skip_unsatisfied)
except (portage.exception.PackageNotFound,
depgraph.UnsatisfiedResumeDep), e:
+ if isinstance(e, depgraph.UnsatisfiedResumeDep):
+ mydepgraph = e.depgraph
if show_spinner:
print
from textwrap import wrap
portage.writemsg("\n", noiselevel=-1)
del dropped_tasks
else:
- mydepgraph.display_problems()
+ if mydepgraph is not None:
+ mydepgraph.display_problems()
if not (ask or pretend):
# delete the current list and also the backup
# since it's probably stale too.
if ("--resume" in myopts):
favorites=mtimedb["resume"]["favorites"]
- mergetask = MergeTask(settings, trees, myopts)
if "PORTAGE_PARALLEL_FETCHONLY" in settings:
""" parallel-fetch uses --resume --fetchonly and we don't want
it to write the mtimedb"""
del mydepgraph
clear_caches(trees)
- retval = mergetask.merge(mymergelist, favorites, mtimedb)
+ mergetask = MergeTask(settings, trees, mtimedb, myopts,
+ spinner, mymergelist, favorites)
+ retval = mergetask.merge()
merge_count = mergetask.curval
else:
if "resume" in mtimedb and \
del mydepgraph
clear_caches(trees)
- mergetask = MergeTask(settings, trees, myopts)
- retval = mergetask.merge(pkglist, favorites, mtimedb)
+ mergetask = MergeTask(settings, trees, mtimedb, myopts,
+ spinner, pkglist, favorites)
+ retval = mergetask.merge()
merge_count = mergetask.curval
if retval == os.EX_OK and not (buildpkgonly or fetchonly or pretend):