Speed up install of environments with dev packages (#27167)
* only check file modification times once per dev package
This commit is contained in:
parent
4858a26400
commit
ecb588740a
1 changed files with 60 additions and 44 deletions
|
@ -286,6 +286,51 @@ def _eval_conditional(string):
|
||||||
return eval(string, valid_variables)
|
return eval(string, valid_variables)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_dev_spec_and_has_changed(spec):
|
||||||
|
"""Check if the passed spec is a dev build and whether it has changed since the
|
||||||
|
last installation"""
|
||||||
|
# First check if this is a dev build and in the process already try to get
|
||||||
|
# the dev_path
|
||||||
|
dev_path_var = spec.variants.get('dev_path', None)
|
||||||
|
if not dev_path_var:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Now we can check whether the code changed since the last installation
|
||||||
|
if not spec.package.installed:
|
||||||
|
# Not installed -> nothing to compare against
|
||||||
|
return False
|
||||||
|
|
||||||
|
_, record = spack.store.db.query_by_spec_hash(spec.dag_hash())
|
||||||
|
mtime = fs.last_modification_time_recursive(dev_path_var.value)
|
||||||
|
return mtime > record.installation_time
|
||||||
|
|
||||||
|
|
||||||
|
def _spec_needs_overwrite(spec, changed_dev_specs):
|
||||||
|
"""Check whether the current spec needs to be overwritten because either it has
|
||||||
|
changed itself or one of its dependencies have changed"""
|
||||||
|
# if it's not installed, we don't need to overwrite it
|
||||||
|
if not spec.package.installed:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If the spec itself has changed this is a trivial decision
|
||||||
|
if spec in changed_dev_specs:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# if spec and all deps aren't dev builds, we don't need to overwrite it
|
||||||
|
if not any(spec.satisfies(c)
|
||||||
|
for c in ('dev_path=*', '^dev_path=*')):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If any dep needs overwrite, or any dep is missing and is a dev build then
|
||||||
|
# overwrite this package
|
||||||
|
if any(
|
||||||
|
((not dep.package.installed) and dep.satisfies('dev_path=*')) or
|
||||||
|
_spec_needs_overwrite(dep, changed_dev_specs)
|
||||||
|
for dep in spec.traverse(root=False)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ViewDescriptor(object):
|
class ViewDescriptor(object):
|
||||||
def __init__(self, base_path, root, projections={}, select=[], exclude=[],
|
def __init__(self, base_path, root, projections={}, select=[], exclude=[],
|
||||||
link=default_view_link, link_type='symlink'):
|
link=default_view_link, link_type='symlink'):
|
||||||
|
@ -1392,52 +1437,19 @@ def _add_concrete_spec(self, spec, concrete, new=True):
|
||||||
self.concretized_order.append(h)
|
self.concretized_order.append(h)
|
||||||
self.specs_by_hash[h] = concrete
|
self.specs_by_hash[h] = concrete
|
||||||
|
|
||||||
def _spec_needs_overwrite(self, spec):
|
|
||||||
# Overwrite the install if it's a dev build (non-transitive)
|
|
||||||
# and the code has been changed since the last install
|
|
||||||
# or one of the dependencies has been reinstalled since
|
|
||||||
# the last install
|
|
||||||
|
|
||||||
# if it's not installed, we don't need to overwrite it
|
|
||||||
if not spec.package.installed:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# if spec and all deps aren't dev builds, we don't need to overwrite it
|
|
||||||
if not any(spec.satisfies(c)
|
|
||||||
for c in ('dev_path=*', '^dev_path=*')):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# if any dep needs overwrite, or any dep is missing and is a dev build
|
|
||||||
# then overwrite this package
|
|
||||||
if any(
|
|
||||||
self._spec_needs_overwrite(dep) or
|
|
||||||
((not dep.package.installed) and dep.satisfies('dev_path=*'))
|
|
||||||
for dep in spec.traverse(root=False)
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
# if it's not a direct dev build and its dependencies haven't
|
|
||||||
# changed, it hasn't changed.
|
|
||||||
# We don't merely check satisfaction (spec.satisfies('dev_path=*')
|
|
||||||
# because we need the value of the variant in the next block of code
|
|
||||||
dev_path_var = spec.variants.get('dev_path', None)
|
|
||||||
if not dev_path_var:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# if it is a direct dev build, check whether the code changed
|
|
||||||
# we already know it is installed
|
|
||||||
_, record = spack.store.db.query_by_spec_hash(spec.dag_hash())
|
|
||||||
mtime = fs.last_modification_time_recursive(dev_path_var.value)
|
|
||||||
return mtime > record.installation_time
|
|
||||||
|
|
||||||
def _get_overwrite_specs(self):
|
def _get_overwrite_specs(self):
|
||||||
ret = []
|
# Collect all specs in the environment first before checking which ones
|
||||||
|
# to rebuild to avoid checking the same specs multiple times
|
||||||
|
specs_to_check = set()
|
||||||
for dag_hash in self.concretized_order:
|
for dag_hash in self.concretized_order:
|
||||||
spec = self.specs_by_hash[dag_hash]
|
root_spec = self.specs_by_hash[dag_hash]
|
||||||
ret.extend([d.dag_hash() for d in spec.traverse(root=True)
|
specs_to_check.update(root_spec.traverse(root=True))
|
||||||
if self._spec_needs_overwrite(d)])
|
|
||||||
|
|
||||||
return ret
|
changed_dev_specs = set(s for s in specs_to_check if
|
||||||
|
_is_dev_spec_and_has_changed(s))
|
||||||
|
|
||||||
|
return [s.dag_hash() for s in specs_to_check if
|
||||||
|
_spec_needs_overwrite(s, changed_dev_specs)]
|
||||||
|
|
||||||
def _install_log_links(self, spec):
|
def _install_log_links(self, spec):
|
||||||
if not spec.external:
|
if not spec.external:
|
||||||
|
@ -1508,8 +1520,12 @@ def install_specs(self, specs=None, **install_args):
|
||||||
tty.debug('Processing {0} uninstalled specs'.format(
|
tty.debug('Processing {0} uninstalled specs'.format(
|
||||||
len(specs_to_install)))
|
len(specs_to_install)))
|
||||||
|
|
||||||
|
specs_to_overwrite = self._get_overwrite_specs()
|
||||||
|
tty.debug('{0} specs need to be overwritten'.format(
|
||||||
|
len(specs_to_overwrite)))
|
||||||
|
|
||||||
install_args['overwrite'] = install_args.get(
|
install_args['overwrite'] = install_args.get(
|
||||||
'overwrite', []) + self._get_overwrite_specs()
|
'overwrite', []) + specs_to_overwrite
|
||||||
|
|
||||||
installs = []
|
installs = []
|
||||||
for spec in specs_to_install:
|
for spec in specs_to_install:
|
||||||
|
|
Loading…
Reference in a new issue