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.
This commit is contained in:
Michael Kuhn 2021-10-11 17:54:59 +02:00 committed by GitHub
parent 8f62039d45
commit d1f3279607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 2 deletions

View File

@ -190,3 +190,8 @@ config:
# Set to 'false' to allow installation on filesystems that doesn't allow setgid bit
# manipulation by unprivileged user (e.g. AFS)
allow_sgid: true
# Whether to set the terminal title to display status information during
# building and installing packages. This gives information about Spack's
# current progress as well as the current and total number of packages.
terminal_title: false

View File

@ -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.

View File

@ -33,7 +33,7 @@
# 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
#

View File

@ -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 @@ def install(self):
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 @@ def install(self):
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 @@ def install(self):
# 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 @@ def install(self):
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 @@ def install(self):
# 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)