in lib/ramble/spack/installer.py [0:0]
def install(self):
"""
Install the requested package(s) and or associated dependencies.
Args:
pkg (spack.package.Package): the package to be built and installed"""
self._init_queue()
fail_fast_err = 'Terminating after first install failure'
single_explicit_spec = len(self.build_requests) == 1
failed_explicits = []
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:
task = self._pop_task()
if task is None:
continue
spack.hooks.on_install_start(task.request.pkg.spec)
install_args = task.request.install_args
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))
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.
#
# If the spec has uninstalled dependencies, then there must be
# a bug in the code (e.g., priority queue or uninstalled
# dependencies handling). So terminate under the assumption that
# 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
dep_id not in self.installed]
if not left:
tty.warn('{0} does NOT actually have any uninstalled deps'
' left'.format(pkg_id))
dep_str = 'dependencies' if task.priority > 1 else 'dependency'
# Hook to indicate task failure, but without an exception
spack.hooks.on_install_failure(task.request.pkg.spec)
raise InstallError(
'Cannot proceed with {0}: {1} uninstalled {2}: {3}'
.format(pkg_id, task.priority, dep_str,
','.join(task.uninstalled_deps)))
# Skip the installation if the spec is not being installed locally
# (i.e., if external or upstream) BUT flag it as installed since
# 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)
# Mark that the package failed
# TODO: this should also be for the task.pkg, but we don't
# model transitive yet.
spack.hooks.on_install_failure(task.request.pkg.spec)
if self.fail_fast:
raise InstallError(fail_fast_err)
continue
# Attempt to get a write lock. If we can't get the lock then
# 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))
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
# another process has a write lock so must be (un)installing
# the spec (or that process is hung).
ltype, lock = self._ensure_locked('read', pkg)
# Requeue the spec if we cannot get at least a read lock so we
# 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)
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:
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
if pkg_id in self.installed:
# Downgrade to a read lock to preclude other processes from
# uninstalling the package until we're done installing its
# dependents.
ltype, lock = self._ensure_locked('read', pkg)
if lock is not None:
self._update_installed(task)
_print_installed_pkg(pkg.prefix)
# It's an already installed compiler, add it to the config
if task.compiler:
spack.compilers.add_compilers_to_config(
spack.compilers.find_compilers([pkg.spec.prefix]))
else:
# At this point we've failed to get a write or a read
# lock, which means another process has taken a write
# lock between our releasing the write and acquiring the
# read.
#
# Requeue the task so we can re-check the status
# established by the other process -- failed, installed,
# or uninstalled -- on the next pass.
self.installed.remove(pkg_id)
self._requeue_task(task)
continue
# Having a read lock on an uninstalled pkg may mean another
# process completed an uninstall of the software between the
# time we failed to acquire the write lock and the time we
# took the read lock.
#
# Requeue the task so we can check the status presumably
# established by the other process -- failed, installed, or
# uninstalled -- on the next pass.
if ltype == 'read':
lock.release_read()
self._requeue_task(task)
continue
# 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)
if action == InstallAction.INSTALL:
self._install_task(task)
elif action == InstallAction.OVERWRITE:
OverwriteInstall(self, spack.store.db, task).install()
self._update_installed(task)
# If we installed then we should keep the prefix
stop_before_phase = getattr(pkg, 'stop_before_phase', None)
last_phase = getattr(pkg, 'last_phase', None)
keep_prefix = keep_prefix or \
(stop_before_phase is None and last_phase is None)
except KeyboardInterrupt as exc:
# The build has been terminated with a Ctrl-C so terminate
# regardless of the number of remaining specs.
err = 'Failed to install {0} due to {1}: {2}'
tty.error(err.format(pkg.name, exc.__class__.__name__,
str(exc)))
spack.hooks.on_install_cancel(task.request.pkg.spec)
raise
except (Exception, SystemExit) as exc:
self._update_failed(task, True, exc)
spack.hooks.on_install_failure(task.request.pkg.spec)
# Best effort installs suppress the exception and mark the
# package as a failure.
if (not isinstance(exc, spack.error.SpackError) or
not exc.printed):
exc.printed = True
# SpackErrors can be printed by the build process or at
# lower levels -- skip printing if already printed.
# TODO: sort out this and SpackError.print_context()
tty.error('Failed to install {0} due to {1}: {2}'
.format(pkg.name, exc.__class__.__name__,
str(exc)))
# Terminate if requested to do so on the first failure.
if self.fail_fast:
raise InstallError('{0}: {1}'
.format(fail_fast_err, str(exc)))
# Terminate at this point if the single explicit spec has
# failed to install.
if single_explicit_spec and task.explicit:
raise
# Track explicit spec id and error to summarize when done
if task.explicit:
failed_explicits.append((pkg_id, str(exc)))
finally:
# Remove the install prefix if anything went wrong during
# install.
if not keep_prefix and not action == InstallAction.OVERWRITE:
pkg.remove_prefix()
# The subprocess *may* have removed the build stage. Mark it
# not created so that the next time pkg.stage is invoked, we
# check the filesystem for it.
pkg.stage.created = False
# Perform basic task cleanup for the installed spec to
# include downgrading the write to a read lock
self._cleanup_task(pkg)
# Cleanup, which includes releasing all of the read locks
self._cleanup_all_tasks()
# Ensure we properly report if one or more explicit specs failed
# or were not installed when should have been.
missing = [request.pkg_id for request in self.build_requests if
request.install_args.get('install_package') and
request.pkg_id not in self.installed]
if failed_explicits or missing:
for pkg_id, err in failed_explicits:
tty.error('{0}: {1}'.format(pkg_id, err))
for pkg_id in missing:
tty.error('{0}: Package was not installed'.format(pkg_id))
raise InstallError('Installation request failed. Refer to '
'reported errors for failing package(s).')