From d1f327960793ffdf5d83538ed636da0181f906a2 Mon Sep 17 00:00:00 2001 From: Michael Kuhn Date: Mon, 11 Oct 2021 17:54:59 +0200 Subject: 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. --- lib/spack/docs/config_yaml.rst | 13 +++++++++++++ lib/spack/llnl/util/tty/log.py | 2 +- lib/spack/spack/installer.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) (limited to 'lib') 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) -- cgit v1.2.3-60-g2f50