summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHarmen Stoppels <harmenstoppels@gmail.com>2022-01-26 10:42:08 +0100
committerGitHub <noreply@github.com>2022-01-26 10:42:08 +0100
commite3d62b2f7be10bddc3c93c074fdbd3e22436f7f2 (patch)
tree25bf5f5d5d413d084dfc1a90e3e0d8b4ecbe824c /lib
parent5300cbbb2e569190015c72d0970d25425ea38647 (diff)
downloadspack-e3d62b2f7be10bddc3c93c074fdbd3e22436f7f2.tar.gz
spack-e3d62b2f7be10bddc3c93c074fdbd3e22436f7f2.tar.bz2
spack-e3d62b2f7be10bddc3c93c074fdbd3e22436f7f2.tar.xz
spack-e3d62b2f7be10bddc3c93c074fdbd3e22436f7f2.zip
Print 'Waiting for another process to install x, y, z' in distributed builds (#28535)
Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/installer.py53
1 files changed, 53 insertions, 0 deletions
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
index c8fdd4e87a..dec5bab626 100644
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -648,6 +648,49 @@ class TermTitle(object):
sys.stdout.flush()
+class TermStatusLine(object):
+ """
+ This class is used in distributed builds to inform the user that other packages are
+ being installed by another process.
+ """
+ def __init__(self, enabled):
+ self.enabled = enabled
+ self.pkg_set = set()
+ self.pkg_list = []
+
+ def add(self, pkg_id):
+ """
+ Add a package to the waiting list, and if it is new, update the status line.
+ """
+ if not self.enabled or pkg_id in self.pkg_set:
+ return
+
+ self.pkg_set.add(pkg_id)
+ self.pkg_list.append(pkg_id)
+ tty.msg(colorize('@*{Waiting for} @*g{%s}' % pkg_id))
+ sys.stdout.flush()
+
+ def clear(self):
+ """
+ Clear the status line.
+ """
+ if not self.enabled:
+ return
+
+ lines = len(self.pkg_list)
+
+ if lines == 0:
+ return
+
+ self.pkg_set.clear()
+ self.pkg_list.clear()
+
+ # Move the cursor to the beginning of the first "Waiting for" message and clear
+ # everything after it.
+ sys.stdout.write('\x1b[%sF\x1b[J' % lines)
+ sys.stdout.flush()
+
+
class PackageInstaller(object):
'''
Class for managing the install process for a Spack instance based on a
@@ -1500,6 +1543,10 @@ class PackageInstaller(object):
term_title = TermTitle(len(self.build_pq))
+ # Only enable the terminal status line when we're in a tty without debug info
+ # enabled, so that the output does not get cluttered.
+ term_status = TermStatusLine(enabled=sys.stdout.isatty() and not tty.is_debug())
+
while self.build_pq:
term_title.next_pkg()
@@ -1523,6 +1570,7 @@ class PackageInstaller(object):
# all subsequent tasks will have non-zero priorities or may be
# dependencies of this task.
if task.priority != 0:
+ term_status.clear()
tty.error('Detected uninstalled dependencies for {0}: {1}'
.format(pkg_id, task.uninstalled_deps))
left = [dep_id for dep_id in task.uninstalled_deps if
@@ -1545,12 +1593,14 @@ class PackageInstaller(object):
# some package likely depends on it.
if not task.explicit:
if _handle_external_and_upstream(pkg, False):
+ term_status.clear()
self._flag_installed(pkg, task.dependents)
continue
# Flag a failed spec. Do not need an (install) prefix lock since
# assume using a separate (failed) prefix lock file.
if pkg_id in self.failed or spack.store.db.prefix_failed(spec):
+ term_status.clear()
tty.warn('{0} failed to install'.format(pkg_id))
self._update_failed(task)
@@ -1569,6 +1619,7 @@ class PackageInstaller(object):
# determined the spec has already been installed (though the
# other process may be hung).
term_title.set('Acquiring lock for {0}'.format(pkg.name))
+ term_status.add(pkg_id)
ltype, lock = self._ensure_locked('write', pkg)
if lock is None:
# Attempt to get a read lock instead. If this fails then
@@ -1583,6 +1634,8 @@ class PackageInstaller(object):
self._requeue_task(task)
continue
+ term_status.clear()
+
# Take a timestamp with the overwrite argument to allow checking
# whether another process has already overridden the package.
if task.request.overwrite and task.explicit: