bugfix: concrete dependencies are now copied properly.

- Dependencies in concrete specs did not previously have their cache
  fields (_concrete, _normal, etc.) preserved.

- _dup and _dup_deps weren't passing each other enough information to
  preserve concreteness properly, so only the root was properly
  preserved.

- cached concreteness is now preserved properly for the entire DAG, not
  just the root.

- added method docs.
This commit is contained in:
Todd Gamblin 2017-09-11 11:41:21 -07:00
parent 14cd73ed3c
commit 8c42aed9d5

View file

@ -2542,7 +2542,7 @@ def virtual_dependencies(self):
"""Return list of any virtual deps in this spec."""
return [spec for spec in self.traverse() if spec.virtual]
def _dup(self, other, deps=True, cleardeps=True):
def _dup(self, other, deps=True, cleardeps=True, caches=None):
"""Copy the spec other into self. This is an overwriting
copy. It does not copy any dependents (parents), but by default
copies dependencies.
@ -2557,6 +2557,10 @@ def _dup(self, other, deps=True, cleardeps=True):
cleardeps (bool): if True clears the dependencies of ``self``,
before possibly copying the dependencies of ``other`` onto
``self``
caches (bool or None): preserve cached fields such as
``_normal``, ``_concrete``, and ``_cmp_key_cache``. By
default this is ``False`` if DAG structure would be
changed by the copy, ``True`` if it's an exact copy.
Returns:
True if ``self`` changed because of the copy operation,
@ -2594,21 +2598,20 @@ def _dup(self, other, deps=True, cleardeps=True):
self.external_module = other.external_module
self.namespace = other.namespace
# If we copy dependencies, preserve DAG structure in the new spec
if deps:
deptypes = alldeps # by default copy all deptypes
# if caller restricted deptypes to be copied, adjust that here.
if isinstance(deps, (tuple, list)):
deptypes = deps
self._dup_deps(other, deptypes)
# These fields are all cached results of expensive operations.
# Cached fields are results of expensive operations.
# If we preserved the original structure, we can copy them
# safely. If not, they need to be recomputed.
# TODO: dependency hashes can be copied more aggressively.
if deps is True or deps == alldeps:
if caches is None:
caches = (deps is True or deps == alldeps)
# If we copy dependencies, preserve DAG structure in the new spec
if deps:
# If caller restricted deptypes to be copied, adjust that here.
# By default, just copy all deptypes
deptypes = deps if isinstance(deps, (tuple, list)) else alldeps
self._dup_deps(other, deptypes, caches)
if caches:
self._hash = other._hash
self._cmp_key_cache = other._cmp_key_cache
self._normal = other._normal
@ -2621,7 +2624,7 @@ def _dup(self, other, deps=True, cleardeps=True):
return changed
def _dup_deps(self, other, deptypes):
def _dup_deps(self, other, deptypes, caches):
new_specs = {self.name: self}
for dspec in other.traverse_edges(cover='edges', root=False):
if (dspec.deptypes and
@ -2629,29 +2632,45 @@ def _dup_deps(self, other, deptypes):
continue
if dspec.parent.name not in new_specs:
new_specs[dspec.parent.name] = dspec.parent.copy(deps=False)
new_specs[dspec.parent.name] = dspec.parent.copy(
deps=False, caches=caches)
if dspec.spec.name not in new_specs:
new_specs[dspec.spec.name] = dspec.spec.copy(deps=False)
new_specs[dspec.spec.name] = dspec.spec.copy(
deps=False, caches=caches)
new_specs[dspec.parent.name]._add_dependency(
new_specs[dspec.spec.name], dspec.deptypes)
def copy(self, deps=True):
"""Return a copy of this spec.
def copy(self, deps=True, **kwargs):
"""Make a copy of this spec.
By default, returns a deep copy. To control how dependencies are
copied, supply:
Args:
deps (bool or tuple): Defaults to True. If boolean, controls
whether dependencies are copied (copied if True). If a
tuple is provided, *only* dependencies of types matching
those in the tuple are copied.
kwargs: additional arguments for internal use (passed to ``_dup``).
deps=True: deep copy
Returns:
A copy of this spec.
deps=False: shallow copy (no dependencies)
Examples:
Deep copy with dependnecies::
deps=('link', 'build'):
only build and link dependencies. Similar for other deptypes.
spec.copy()
spec.copy(deps=True)
Shallow copy (no dependencies)::
spec.copy(deps=False)
Only build and run dependencies::
deps=('build', 'run'):
"""
clone = Spec.__new__(Spec)
clone._dup(self, deps=deps)
clone._dup(self, deps=deps, **kwargs)
return clone
@property