def _expand_virtual_packages()

in lib/ramble/spack/spec.py [0:0]


    def _expand_virtual_packages(self, concretizer):
        """Find virtual packages in this spec, replace them with providers,
           and normalize again to include the provider's (potentially virtual)
           dependencies.  Repeat until there are no virtual deps.

           Precondition: spec is normalized.

           .. todo::

              If a provider depends on something that conflicts with
              other dependencies in the spec being expanded, this can
              produce a conflicting spec.  For example, if mpich depends
              on hwloc@:1.3 but something in the spec needs hwloc1.4:,
              then we should choose an MPI other than mpich.  Cases like
              this are infrequent, but should implement this before it is
              a problem.
        """
        # Make an index of stuff this spec already provides
        self_index = spack.provider_index.ProviderIndex(
            self.traverse(), restrict=True)
        changed = False
        done = False

        while not done:
            done = True
            for spec in list(self.traverse()):
                replacement = None
                if spec.external:
                    continue
                if spec.virtual:
                    replacement = self._find_provider(spec, self_index)
                    if replacement:
                        # TODO: may break if in-place on self but
                        # shouldn't happen if root is traversed first.
                        spec._replace_with(replacement)
                        done = False
                        break

                if not replacement:
                    # Get a list of possible replacements in order of
                    # preference.
                    candidates = concretizer.choose_virtual_or_external(spec)

                    # Try the replacements in order, skipping any that cause
                    # satisfiability problems.
                    for replacement in candidates:
                        if replacement is spec:
                            break

                        # Replace spec with the candidate and normalize
                        copy = self.copy()
                        copy[spec.name]._dup(replacement, deps=False)

                        try:
                            # If there are duplicate providers or duplicate
                            # provider deps, consolidate them and merge
                            # constraints.
                            copy.normalize(force=True)
                            break
                        except spack.error.SpecError:
                            # On error, we'll try the next replacement.
                            continue

                # If replacement is external then trim the dependencies
                if replacement.external:
                    if spec._dependencies:
                        for dep in spec.dependencies():
                            del dep._dependents.edges[spec.name]
                        changed = True
                        spec.clear_dependencies()
                    replacement.clear_dependencies()
                    replacement.architecture = self.architecture

                # TODO: could this and the stuff in _dup be cleaned up?
                def feq(cfield, sfield):
                    return (not cfield) or (cfield == sfield)

                if replacement is spec or (
                        feq(replacement.name, spec.name) and
                        feq(replacement.versions, spec.versions) and
                        feq(replacement.compiler, spec.compiler) and
                        feq(replacement.architecture, spec.architecture) and
                        feq(replacement._dependencies, spec._dependencies) and
                        feq(replacement.variants, spec.variants) and
                        feq(replacement.external_path,
                            spec.external_path) and
                        feq(replacement.external_modules,
                            spec.external_modules)):
                    continue
                # Refine this spec to the candidate. This uses
                # replace_with AND dup so that it can work in
                # place. TODO: make this more efficient.
                if spec.virtual:
                    spec._replace_with(replacement)
                    changed = True
                if spec._dup(replacement, deps=False, cleardeps=False):
                    changed = True

                spec._dependencies.owner = spec
                self_index.update(spec)
                done = False
                break

        return changed