summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMichael Kuhn <michael.kuhn@ovgu.de>2021-10-11 17:54:59 +0200
committerGitHub <noreply@github.com>2021-10-11 17:54:59 +0200
commitd1f327960793ffdf5d83538ed636da0181f906a2 (patch)
tree15fe8fbca478b65ecdd37e3e5fc42e98add57fb5 /lib
parent8f62039d45ec6545d0a226611bdf8998665844d8 (diff)
downloadspack-d1f327960793ffdf5d83538ed636da0181f906a2.tar.gz
spack-d1f327960793ffdf5d83538ed636da0181f906a2.tar.bz2
spack-d1f327960793ffdf5d83538ed636da0181f906a2.tar.xz
spack-d1f327960793ffdf5d83538ed636da0181f906a2.zip
installer: Support showing status information in terminal title (#16259)
Installing packages with a lot of dependencies does not have an easy way of judging the current progress (apart from running `spack spec -I pkg` in another terminal). This change allows Spack to update the terminal's title with status information, including its current progress as well as information about the current and total number of packages.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/docs/config_yaml.rst13
-rw-r--r--lib/spack/llnl/util/tty/log.py2
-rw-r--r--lib/spack/spack/installer.py29
3 files changed, 43 insertions, 1 deletions
diff --git a/lib/spack/docs/config_yaml.rst b/lib/spack/docs/config_yaml.rst
index 631b8f6b1b..8173e8a0a9 100644
--- a/lib/spack/docs/config_yaml.rst
+++ b/lib/spack/docs/config_yaml.rst
@@ -259,3 +259,16 @@ and ld.so will ONLY search for dependencies in the ``RUNPATH`` of
the loading object.
DO NOT MIX the two options within the same install tree.
+
+----------------------
+``terminal_title``
+----------------------
+
+By setting this option to ``true``, Spack will update the terminal's title to
+provide information about its current progress as well as the current and
+total package numbers.
+
+To work properly, this requires your terminal to reset its title after
+Spack has finished its work, otherwise Spack's status information will
+remain in the terminal's title indefinitely. Most terminals should already
+be set up this way and clear Spack's status information.
diff --git a/lib/spack/llnl/util/tty/log.py b/lib/spack/llnl/util/tty/log.py
index bc79b32335..7003f58f8d 100644
--- a/lib/spack/llnl/util/tty/log.py
+++ b/lib/spack/llnl/util/tty/log.py
@@ -33,7 +33,7 @@ except ImportError:
# Use this to strip escape sequences
-_escape = re.compile(r'\x1b[^m]*m|\x1b\[?1034h')
+_escape = re.compile(r'\x1b[^m]*m|\x1b\[?1034h|\x1b\][0-9]+;[^\x07]*\x07')
# control characters for enabling/disabling echo
#
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
index 11430bfc94..d7a6ca0a67 100644
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -627,6 +627,27 @@ def package_id(pkg):
return "{0}-{1}-{2}".format(pkg.name, pkg.version, pkg.spec.dag_hash())
+class TermTitle(object):
+ def __init__(self, pkg_count):
+ # Counters used for showing status information in the terminal title
+ self.pkg_num = 0
+ self.pkg_count = pkg_count
+
+ def next_pkg(self):
+ self.pkg_num += 1
+
+ def set(self, text):
+ if not spack.config.get('config:terminal_title', False):
+ return
+
+ if not sys.stdout.isatty():
+ return
+
+ status = '{0} [{1}/{2}]'.format(text, self.pkg_num, self.pkg_count)
+ sys.stdout.write('\033]0;Spack: {0}\007'.format(status))
+ sys.stdout.flush()
+
+
class PackageInstaller(object):
'''
Class for managing the install process for a Spack instance based on a
@@ -1476,7 +1497,11 @@ class PackageInstaller(object):
failed_explicits = []
exists_errors = []
+ term_title = TermTitle(len(self.build_pq))
+
while self.build_pq:
+ term_title.next_pkg()
+
task = self._pop_task()
if task is None:
continue
@@ -1486,6 +1511,7 @@ class PackageInstaller(object):
keep_prefix = install_args.get('keep_prefix')
pkg, pkg_id, spec = task.pkg, task.pkg_id, task.pkg.spec
+ term_title.set('Processing {0}'.format(pkg.name))
tty.verbose('Processing {0}: task={1}'.format(pkg_id, task))
# Ensure that the current spec has NO uninstalled dependencies,
# which is assumed to be reflected directly in its priority.
@@ -1541,6 +1567,7 @@ class PackageInstaller(object):
# another process is likely (un)installing the spec or has
# determined the spec has already been installed (though the
# other process may be hung).
+ term_title.set('Acquiring lock for {0}'.format(pkg.name))
ltype, lock = self._ensure_locked('write', pkg)
if lock is None:
# Attempt to get a read lock instead. If this fails then
@@ -1561,6 +1588,7 @@ class PackageInstaller(object):
task.request.overwrite_time = time.time()
# Determine state of installation artifacts and adjust accordingly.
+ term_title.set('Preparing {0}'.format(pkg.name))
self._prepare_for_install(task)
# Flag an already installed package
@@ -1605,6 +1633,7 @@ class PackageInstaller(object):
# Proceed with the installation since we have an exclusive write
# lock on the package.
+ term_title.set('Installing {0}'.format(pkg.name))
try:
action = self._install_action(task)