summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/docs/config_yaml.rst9
-rw-r--r--lib/spack/spack/installer.py60
-rw-r--r--lib/spack/spack/schema/config.py7
-rw-r--r--lib/spack/spack/test/installer.py36
4 files changed, 64 insertions, 48 deletions
diff --git a/lib/spack/docs/config_yaml.rst b/lib/spack/docs/config_yaml.rst
index b1e7a1d249..294f7c3436 100644
--- a/lib/spack/docs/config_yaml.rst
+++ b/lib/spack/docs/config_yaml.rst
@@ -292,12 +292,13 @@ It is also worth noting that:
non_bindable_shared_objects = ["libinterface.so"]
----------------------
-``terminal_title``
+``install_status``
----------------------
-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.
+When set to ``true``, Spack will show information about its current progress
+as well as the current and total package numbers. Progress is shown both
+in the terminal title and inline. Setting it to ``false`` will not show any
+progress information.
To work properly, this requires your terminal to reset its title after
Spack has finished its work, otherwise Spack's status information will
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
index bc3e17d41d..3b632fdbcc 100644
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -536,7 +536,7 @@ def get_dependent_ids(spec):
return [package_id(d.package) for d in spec.dependents()]
-def install_msg(name, pid):
+def install_msg(name, pid, install_status):
"""
Colorize the name/id of the package being installed
@@ -548,7 +548,12 @@ def install_msg(name, pid):
str: Colorized installing message
"""
pre = "{0}: ".format(pid) if tty.show_pid() else ""
- return pre + colorize("@*{Installing} @*g{%s}" % name)
+ post = (
+ " @*{%s}" % install_status.get_progress()
+ if install_status and spack.config.get("config:install_status", True)
+ else ""
+ )
+ return pre + colorize("@*{Installing} @*g{%s}%s" % (name, post))
def archive_install_logs(pkg, phase_log_dir):
@@ -657,9 +662,9 @@ def package_id(pkg):
return "{0}-{1}-{2}".format(pkg.name, pkg.version, pkg.spec.dag_hash())
-class TermTitle:
+class InstallStatus:
def __init__(self, pkg_count):
- # Counters used for showing status information in the terminal title
+ # Counters used for showing status information
self.pkg_num = 0
self.pkg_count = pkg_count
self.pkg_ids = set()
@@ -671,17 +676,20 @@ class TermTitle:
self.pkg_num += 1
self.pkg_ids.add(pkg_id)
- def set(self, text):
- if not spack.config.get("config:terminal_title", False):
+ def set_term_title(self, text):
+ if not spack.config.get("config:install_status", True):
return
if not sys.stdout.isatty():
return
- status = "{0} [{1}/{2}]".format(text, self.pkg_num, self.pkg_count)
+ status = "{0} {1}".format(text, self.get_progress())
sys.stdout.write("\033]0;Spack: {0}\007".format(status))
sys.stdout.flush()
+ def get_progress(self):
+ return "[{0}/{1}]".format(self.pkg_num, self.pkg_count)
+
class TermStatusLine:
"""
@@ -1240,7 +1248,7 @@ class PackageInstaller:
spack.compilers.find_compilers([compiler_search_prefix])
)
- def _install_task(self, task):
+ def _install_task(self, task, install_status):
"""
Perform the installation of the requested spec and/or dependency
represented by the build task.
@@ -1257,7 +1265,7 @@ class PackageInstaller:
pkg, pkg_id = task.pkg, task.pkg_id
- tty.msg(install_msg(pkg_id, self.pid))
+ tty.msg(install_msg(pkg_id, self.pid, install_status))
task.start = task.start or time.time()
task.status = STATUS_INSTALLING
@@ -1406,7 +1414,7 @@ class PackageInstaller:
else:
return None
- def _requeue_task(self, task):
+ def _requeue_task(self, task, install_status):
"""
Requeues a task that appears to be in progress by another process.
@@ -1416,7 +1424,8 @@ class PackageInstaller:
if task.status not in [STATUS_INSTALLED, STATUS_INSTALLING]:
tty.debug(
"{0} {1}".format(
- install_msg(task.pkg_id, self.pid), "in progress by another process"
+ install_msg(task.pkg_id, self.pid, install_status),
+ "in progress by another process",
)
)
@@ -1595,7 +1604,7 @@ class PackageInstaller:
single_explicit_spec = len(self.build_requests) == 1
failed_explicits = []
- term_title = TermTitle(len(self.build_pq))
+ install_status = InstallStatus(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.
@@ -1611,8 +1620,8 @@ class PackageInstaller:
keep_prefix = install_args.get("keep_prefix")
pkg, pkg_id, spec = task.pkg, task.pkg_id, task.pkg.spec
- term_title.next_pkg(pkg)
- term_title.set("Processing {0}".format(pkg.name))
+ install_status.next_pkg(pkg)
+ install_status.set_term_title("Processing {0}".format(pkg.name))
tty.debug("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.
@@ -1676,7 +1685,7 @@ class PackageInstaller:
# 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))
+ install_status.set_term_title("Acquiring lock for {0}".format(pkg.name))
term_status.add(pkg_id)
ltype, lock = self._ensure_locked("write", pkg)
if lock is None:
@@ -1689,7 +1698,7 @@ class PackageInstaller:
# can check the status presumably established by another process
# -- failed, installed, or uninstalled -- on the next pass.
if lock is None:
- self._requeue_task(task)
+ self._requeue_task(task, install_status)
continue
term_status.clear()
@@ -1700,7 +1709,7 @@ class PackageInstaller:
task.request.overwrite_time = time.time()
# Determine state of installation artifacts and adjust accordingly.
- term_title.set("Preparing {0}".format(pkg.name))
+ install_status.set_term_title("Preparing {0}".format(pkg.name))
self._prepare_for_install(task)
# Flag an already installed package
@@ -1728,7 +1737,7 @@ class PackageInstaller:
# established by the other process -- failed, installed,
# or uninstalled -- on the next pass.
self.installed.remove(pkg_id)
- self._requeue_task(task)
+ self._requeue_task(task, install_status)
continue
# Having a read lock on an uninstalled pkg may mean another
@@ -1741,19 +1750,19 @@ class PackageInstaller:
# uninstalled -- on the next pass.
if ltype == "read":
lock.release_read()
- self._requeue_task(task)
+ self._requeue_task(task, install_status)
continue
# Proceed with the installation since we have an exclusive write
# lock on the package.
- term_title.set("Installing {0}".format(pkg.name))
+ install_status.set_term_title("Installing {0}".format(pkg.name))
try:
action = self._install_action(task)
if action == InstallAction.INSTALL:
- self._install_task(task)
+ self._install_task(task, install_status)
elif action == InstallAction.OVERWRITE:
- OverwriteInstall(self, spack.store.db, task).install()
+ OverwriteInstall(self, spack.store.db, task, install_status).install()
self._update_installed(task)
@@ -1779,7 +1788,7 @@ class PackageInstaller:
err += " Requeueing to install from source."
tty.error(err.format(pkg.name, str(exc)))
task.use_cache = False
- self._requeue_task(task)
+ self._requeue_task(task, install_status)
continue
except (Exception, SystemExit) as exc:
@@ -2092,10 +2101,11 @@ def build_process(pkg, install_args):
class OverwriteInstall:
- def __init__(self, installer, database, task):
+ def __init__(self, installer, database, task, install_status):
self.installer = installer
self.database = database
self.task = task
+ self.install_status = install_status
def install(self):
"""
@@ -2106,7 +2116,7 @@ class OverwriteInstall:
"""
try:
with fs.replace_directory_transaction(self.task.pkg.prefix):
- self.installer._install_task(self.task)
+ self.installer._install_task(self.task, self.install_status)
except fs.CouldNotRestoreDirectoryBackup as e:
self.database.remove(self.task.pkg.spec)
tty.error(
diff --git a/lib/spack/spack/schema/config.py b/lib/spack/spack/schema/config.py
index 106e45ce7a..6c30f0aab9 100644
--- a/lib/spack/spack/schema/config.py
+++ b/lib/spack/spack/schema/config.py
@@ -87,15 +87,16 @@ properties = {
"anyOf": [{"type": "integer", "minimum": 1}, {"type": "null"}]
},
"allow_sgid": {"type": "boolean"},
+ "install_status": {"type": "boolean"},
"binary_index_root": {"type": "string"},
"url_fetch_method": {"type": "string", "enum": ["urllib", "curl"]},
"additional_external_search_paths": {"type": "array", "items": {"type": "string"}},
"binary_index_ttl": {"type": "integer", "minimum": 0},
},
"deprecatedProperties": {
- "properties": ["module_roots"],
- "message": "config:module_roots has been replaced by "
- "modules:[module set]:roots and is ignored",
+ "properties": ["terminal_title"],
+ "message": "config:terminal_title has been replaced by "
+ "install_status and is ignored",
"error": False,
},
}
diff --git a/lib/spack/spack/test/installer.py b/lib/spack/spack/test/installer.py
index 7b9c2b5184..a562fafbb0 100644
--- a/lib/spack/spack/test/installer.py
+++ b/lib/spack/spack/test/installer.py
@@ -148,15 +148,19 @@ def test_install_msg(monkeypatch):
install_msg = "Installing {0}".format(name)
monkeypatch.setattr(tty, "_debug", 0)
- assert inst.install_msg(name, pid) == install_msg
+ assert inst.install_msg(name, pid, None) == install_msg
+
+ install_status = inst.InstallStatus(1)
+ expected = "{0} [0/1]".format(install_msg)
+ assert inst.install_msg(name, pid, install_status) == expected
monkeypatch.setattr(tty, "_debug", 1)
- assert inst.install_msg(name, pid) == install_msg
+ assert inst.install_msg(name, pid, None) == install_msg
# Expect the PID to be added at debug level 2
monkeypatch.setattr(tty, "_debug", 2)
expected = "{0}: {1}".format(pid, install_msg)
- assert inst.install_msg(name, pid) == expected
+ assert inst.install_msg(name, pid, None) == expected
def test_install_from_cache_errors(install_mockery, capsys):
@@ -795,7 +799,7 @@ def test_install_task_use_cache(install_mockery, monkeypatch):
task = create_build_task(request.pkg)
monkeypatch.setattr(inst, "_install_from_cache", _true)
- installer._install_task(task)
+ installer._install_task(task, None)
assert request.pkg_id in installer.installed
@@ -817,7 +821,7 @@ def test_install_task_add_compiler(install_mockery, monkeypatch, capfd):
monkeypatch.setattr(spack.database.Database, "add", _noop)
monkeypatch.setattr(spack.compilers, "add_compilers_to_config", _add)
- installer._install_task(task)
+ installer._install_task(task, None)
out = capfd.readouterr()[0]
assert config_msg in out
@@ -868,7 +872,7 @@ def test_requeue_task(install_mockery, capfd):
# temporarily set tty debug messages on so we can test output
current_debug_level = tty.debug_level()
tty.set_debug(1)
- installer._requeue_task(task)
+ installer._requeue_task(task, None)
tty.set_debug(current_debug_level)
ids = list(installer.build_tasks)
@@ -1031,7 +1035,7 @@ def test_install_fail_on_interrupt(install_mockery, monkeypatch):
spec_name = "a"
err_msg = "mock keyboard interrupt for {0}".format(spec_name)
- def _interrupt(installer, task, **kwargs):
+ def _interrupt(installer, task, install_status, **kwargs):
if task.pkg.name == spec_name:
raise KeyboardInterrupt(err_msg)
else:
@@ -1058,7 +1062,7 @@ def test_install_fail_single(install_mockery, monkeypatch):
class MyBuildException(Exception):
pass
- def _install(installer, task, **kwargs):
+ def _install(installer, task, install_status, **kwargs):
if task.pkg.name == spec_name:
raise MyBuildException(err_msg)
else:
@@ -1085,7 +1089,7 @@ def test_install_fail_multi(install_mockery, monkeypatch):
class MyBuildException(Exception):
pass
- def _install(installer, task, **kwargs):
+ def _install(installer, task, install_status, **kwargs):
if task.pkg.name == spec_name:
raise MyBuildException(err_msg)
else:
@@ -1157,7 +1161,7 @@ def test_install_fail_fast_on_except(install_mockery, monkeypatch, capsys):
def test_install_lock_failures(install_mockery, monkeypatch, capfd):
"""Cover basic install lock failure handling in a single pass."""
- def _requeued(installer, task):
+ def _requeued(installer, task, install_status):
tty.msg("requeued {0}".format(task.pkg.spec.name))
const_arg = installer_args(["b"], {})
@@ -1192,7 +1196,7 @@ def test_install_lock_installed_requeue(install_mockery, monkeypatch, capfd):
# also do not allow the package to be locked again
monkeypatch.setattr(inst.PackageInstaller, "_ensure_locked", _not_locked)
- def _requeued(installer, task):
+ def _requeued(installer, task, install_status):
tty.msg("requeued {0}".format(inst.package_id(task.pkg)))
# Flag the package as installed
@@ -1224,7 +1228,7 @@ def test_install_read_locked_requeue(install_mockery, monkeypatch, capfd):
tty.msg("preparing {0}".format(task.pkg.spec.name))
assert task.pkg.spec.name not in installer.installed
- def _requeued(installer, task):
+ def _requeued(installer, task, install_status):
tty.msg("requeued {0}".format(task.pkg.spec.name))
# Force a read lock
@@ -1289,7 +1293,7 @@ def test_overwrite_install_backup_success(temporary_store, config, mock_packages
fs.touchp(installed_file)
class InstallerThatWipesThePrefixDir:
- def _install_task(self, task):
+ def _install_task(self, task, install_status):
shutil.rmtree(task.pkg.prefix, ignore_errors=True)
fs.mkdirp(task.pkg.prefix)
raise Exception("Some fatal install error")
@@ -1302,7 +1306,7 @@ def test_overwrite_install_backup_success(temporary_store, config, mock_packages
fake_installer = InstallerThatWipesThePrefixDir()
fake_db = FakeDatabase()
- overwrite_install = inst.OverwriteInstall(fake_installer, fake_db, task)
+ overwrite_install = inst.OverwriteInstall(fake_installer, fake_db, task, None)
# Installation should throw the installation exception, not the backup
# failure.
@@ -1323,7 +1327,7 @@ def test_overwrite_install_backup_failure(temporary_store, config, mock_packages
"""
class InstallerThatAccidentallyDeletesTheBackupDir:
- def _install_task(self, task):
+ def _install_task(self, task, install_status):
# Remove the backup directory, which is at the same level as the prefix,
# starting with .backup
backup_glob = os.path.join(
@@ -1351,7 +1355,7 @@ def test_overwrite_install_backup_failure(temporary_store, config, mock_packages
fake_installer = InstallerThatAccidentallyDeletesTheBackupDir()
fake_db = FakeDatabase()
- overwrite_install = inst.OverwriteInstall(fake_installer, fake_db, task)
+ overwrite_install = inst.OverwriteInstall(fake_installer, fake_db, task, None)
# Installation should throw the installation exception, not the backup
# failure.