|  |  |  | @@ -6,6 +6,7 @@ | 
		
	
		
			
				|  |  |  |  | from __future__ import print_function | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | import sys | 
		
	
		
			
				|  |  |  |  | from typing import Dict, List, Optional | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | from llnl.util import tty | 
		
	
		
			
				|  |  |  |  | from llnl.util.tty.colify import colify | 
		
	
	
		
			
				
					
					|  |  |  | @@ -16,6 +17,7 @@ | 
		
	
		
			
				|  |  |  |  | import spack.error | 
		
	
		
			
				|  |  |  |  | import spack.package_base | 
		
	
		
			
				|  |  |  |  | import spack.repo | 
		
	
		
			
				|  |  |  |  | import spack.spec | 
		
	
		
			
				|  |  |  |  | import spack.store | 
		
	
		
			
				|  |  |  |  | import spack.traverse as traverse | 
		
	
		
			
				|  |  |  |  | from spack.database import InstallStatuses | 
		
	
	
		
			
				
					
					|  |  |  | @@ -78,15 +80,19 @@ def setup_parser(subparser): | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def find_matching_specs(env, specs, allow_multiple_matches=False, force=False, origin=None): | 
		
	
		
			
				|  |  |  |  | def find_matching_specs( | 
		
	
		
			
				|  |  |  |  |     env: Optional[ev.Environment], | 
		
	
		
			
				|  |  |  |  |     specs: List[spack.spec.Spec], | 
		
	
		
			
				|  |  |  |  |     allow_multiple_matches: bool = False, | 
		
	
		
			
				|  |  |  |  |     origin=None, | 
		
	
		
			
				|  |  |  |  | ) -> List[spack.spec.Spec]: | 
		
	
		
			
				|  |  |  |  |     """Returns a list of specs matching the not necessarily | 
		
	
		
			
				|  |  |  |  |        concretized specs given from cli | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Args: | 
		
	
		
			
				|  |  |  |  |         env (spack.environment.Environment): active environment, or ``None`` | 
		
	
		
			
				|  |  |  |  |             if there is not one | 
		
	
		
			
				|  |  |  |  |         specs (list): list of specs to be matched against installed packages | 
		
	
		
			
				|  |  |  |  |         allow_multiple_matches (bool): if True multiple matches are admitted | 
		
	
		
			
				|  |  |  |  |         env: optional active environment | 
		
	
		
			
				|  |  |  |  |         specs: list of specs to be matched against installed packages | 
		
	
		
			
				|  |  |  |  |         allow_multiple_matches: if True multiple matches are admitted | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Return: | 
		
	
		
			
				|  |  |  |  |         list: list of specs | 
		
	
	
		
			
				
					
					|  |  |  | @@ -128,91 +134,52 @@ def find_matching_specs(env, specs, allow_multiple_matches=False, force=False, o | 
		
	
		
			
				|  |  |  |  |     return specs_from_cli | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def installed_runtime_dependents(specs, env): | 
		
	
		
			
				|  |  |  |  |     """Map each spec to a list of its installed dependents. | 
		
	
		
			
				|  |  |  |  | def installed_dependents(specs: List[spack.spec.Spec]) -> List[spack.spec.Spec]: | 
		
	
		
			
				|  |  |  |  |     # Note: the combination of arguments (in particular order=breadth | 
		
	
		
			
				|  |  |  |  |     # and root=False) ensures dependents and matching_specs are non-overlapping; | 
		
	
		
			
				|  |  |  |  |     # In the extreme case of "spack uninstall --all" we get the entire database as | 
		
	
		
			
				|  |  |  |  |     # input; in that case we return an empty list. | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Args: | 
		
	
		
			
				|  |  |  |  |         specs (list): list of Specs | 
		
	
		
			
				|  |  |  |  |         env (spack.environment.Environment or None): the active environment, or None | 
		
	
		
			
				|  |  |  |  |     def is_installed(spec): | 
		
	
		
			
				|  |  |  |  |         record = spack.store.db.query_local_by_spec_hash(spec.dag_hash()) | 
		
	
		
			
				|  |  |  |  |         return record and record.installed | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Returns: | 
		
	
		
			
				|  |  |  |  |         tuple: two mappings: one from specs to their dependent installs in the | 
		
	
		
			
				|  |  |  |  |         active environment, and one from specs to dependent installs outside of | 
		
	
		
			
				|  |  |  |  |         the active environment. | 
		
	
		
			
				|  |  |  |  |     specs = traverse.traverse_nodes( | 
		
	
		
			
				|  |  |  |  |         specs, | 
		
	
		
			
				|  |  |  |  |         root=False, | 
		
	
		
			
				|  |  |  |  |         order="breadth", | 
		
	
		
			
				|  |  |  |  |         cover="nodes", | 
		
	
		
			
				|  |  |  |  |         deptype=("link", "run"), | 
		
	
		
			
				|  |  |  |  |         direction="parents", | 
		
	
		
			
				|  |  |  |  |         key=lambda s: s.dag_hash(), | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         Every installed dependent spec is listed once. | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         If there is not current active environment, the first mapping will be | 
		
	
		
			
				|  |  |  |  |         empty. | 
		
	
		
			
				|  |  |  |  |     """ | 
		
	
		
			
				|  |  |  |  |     active_dpts = {} | 
		
	
		
			
				|  |  |  |  |     outside_dpts = {} | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     env_hashes = set(env.all_hashes()) if env else set() | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     # Ensure we stop traversal at input specs. | 
		
	
		
			
				|  |  |  |  |     visited = set(s.dag_hash() for s in specs) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     for spec in specs: | 
		
	
		
			
				|  |  |  |  |         for dpt in traverse.traverse_nodes( | 
		
	
		
			
				|  |  |  |  |             spec.dependents(deptype=("link", "run")), | 
		
	
		
			
				|  |  |  |  |             direction="parents", | 
		
	
		
			
				|  |  |  |  |             visited=visited, | 
		
	
		
			
				|  |  |  |  |             deptype=("link", "run"), | 
		
	
		
			
				|  |  |  |  |             root=True, | 
		
	
		
			
				|  |  |  |  |             key=lambda s: s.dag_hash(), | 
		
	
		
			
				|  |  |  |  |         ): | 
		
	
		
			
				|  |  |  |  |             hash = dpt.dag_hash() | 
		
	
		
			
				|  |  |  |  |             # Ensure that all the specs we get are installed | 
		
	
		
			
				|  |  |  |  |             record = spack.store.db.query_local_by_spec_hash(hash) | 
		
	
		
			
				|  |  |  |  |             if record is None or not record.installed: | 
		
	
		
			
				|  |  |  |  |                 continue | 
		
	
		
			
				|  |  |  |  |             if hash in env_hashes: | 
		
	
		
			
				|  |  |  |  |                 active_dpts.setdefault(spec, set()).add(dpt) | 
		
	
		
			
				|  |  |  |  |             else: | 
		
	
		
			
				|  |  |  |  |                 outside_dpts.setdefault(spec, set()).add(dpt) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     return active_dpts, outside_dpts | 
		
	
		
			
				|  |  |  |  |     return [spec for spec in specs if is_installed(spec)] | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def dependent_environments(specs): | 
		
	
		
			
				|  |  |  |  |     """Map each spec to environments that depend on it. | 
		
	
		
			
				|  |  |  |  | def dependent_environments( | 
		
	
		
			
				|  |  |  |  |     specs: List[spack.spec.Spec], current_env: Optional[ev.Environment] = None | 
		
	
		
			
				|  |  |  |  | ) -> Dict[ev.Environment, List[spack.spec.Spec]]: | 
		
	
		
			
				|  |  |  |  |     # For each tracked environment, get the specs we would uninstall from it. | 
		
	
		
			
				|  |  |  |  |     # Don't instantiate current environment twice. | 
		
	
		
			
				|  |  |  |  |     env_names = ev.all_environment_names() | 
		
	
		
			
				|  |  |  |  |     if current_env: | 
		
	
		
			
				|  |  |  |  |         env_names = (name for name in env_names if name != current_env.name) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Args: | 
		
	
		
			
				|  |  |  |  |         specs (list): list of Specs | 
		
	
		
			
				|  |  |  |  |     # Mapping from Environment -> non-zero list of specs contained in it. | 
		
	
		
			
				|  |  |  |  |     other_envs_to_specs: Dict[ev.Environment, List[spack.spec.Spec]] = {} | 
		
	
		
			
				|  |  |  |  |     for other_env in (ev.Environment(ev.root(name)) for name in env_names): | 
		
	
		
			
				|  |  |  |  |         specs_in_other_env = all_specs_in_env(other_env, specs) | 
		
	
		
			
				|  |  |  |  |         if specs_in_other_env: | 
		
	
		
			
				|  |  |  |  |             other_envs_to_specs[other_env] = specs_in_other_env | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Returns: | 
		
	
		
			
				|  |  |  |  |         dict: mapping from spec to lists of dependent Environments | 
		
	
		
			
				|  |  |  |  |     """ | 
		
	
		
			
				|  |  |  |  |     dependents = {} | 
		
	
		
			
				|  |  |  |  |     for env in ev.all_environments(): | 
		
	
		
			
				|  |  |  |  |         hashes = set(env.all_hashes()) | 
		
	
		
			
				|  |  |  |  |         for spec in specs: | 
		
	
		
			
				|  |  |  |  |             if spec.dag_hash() in hashes: | 
		
	
		
			
				|  |  |  |  |                 dependents.setdefault(spec, []).append(env) | 
		
	
		
			
				|  |  |  |  |     return dependents | 
		
	
		
			
				|  |  |  |  |     return other_envs_to_specs | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def inactive_dependent_environments(spec_envs): | 
		
	
		
			
				|  |  |  |  |     """Strip the active environment from a dependent map. | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Take the output of ``dependent_environment()`` and remove the active | 
		
	
		
			
				|  |  |  |  |     environment from all mappings.  Remove any specs in the map that now | 
		
	
		
			
				|  |  |  |  |     have no dependent environments.  Return the result. | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Args: | 
		
	
		
			
				|  |  |  |  |         spec_envs (dict): mapping from spec to lists of dependent Environments | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Returns: | 
		
	
		
			
				|  |  |  |  |         dict: mapping from spec to lists of *inactive* dependent Environments | 
		
	
		
			
				|  |  |  |  |     """ | 
		
	
		
			
				|  |  |  |  |     spec_inactive_envs = {} | 
		
	
		
			
				|  |  |  |  |     for spec, de_list in spec_envs.items(): | 
		
	
		
			
				|  |  |  |  |         inactive = [de for de in de_list if not de.active] | 
		
	
		
			
				|  |  |  |  |         if inactive: | 
		
	
		
			
				|  |  |  |  |             spec_inactive_envs[spec] = inactive | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     return spec_inactive_envs | 
		
	
		
			
				|  |  |  |  | def all_specs_in_env(env: ev.Environment, specs: List[spack.spec.Spec]) -> List[spack.spec.Spec]: | 
		
	
		
			
				|  |  |  |  |     """Given a list of specs, return those that are in the env""" | 
		
	
		
			
				|  |  |  |  |     hashes = set(env.all_hashes()) | 
		
	
		
			
				|  |  |  |  |     return [s for s in specs if s.dag_hash() in hashes] | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def _remove_from_env(spec, env): | 
		
	
	
		
			
				
					
					|  |  |  | @@ -225,7 +192,7 @@ def _remove_from_env(spec, env): | 
		
	
		
			
				|  |  |  |  |         pass  # ignore non-root specs | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def do_uninstall(specs, force=False): | 
		
	
		
			
				|  |  |  |  | def do_uninstall(specs: List[spack.spec.Spec], force: bool = False): | 
		
	
		
			
				|  |  |  |  |     # TODO: get rid of the call-sites that use this function, | 
		
	
		
			
				|  |  |  |  |     # so that we don't have to do a dance of list -> set -> list -> set | 
		
	
		
			
				|  |  |  |  |     hashes_to_remove = set(s.dag_hash() for s in specs) | 
		
	
	
		
			
				
					
					|  |  |  | @@ -237,102 +204,56 @@ def do_uninstall(specs, force=False): | 
		
	
		
			
				|  |  |  |  |             spack.package_base.PackageBase.uninstall_by_spec(s, force=force) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def get_uninstall_list(args, specs, env): | 
		
	
		
			
				|  |  |  |  |     """Returns uninstall_list and remove_list: these may overlap (some things | 
		
	
		
			
				|  |  |  |  | def get_uninstall_list(args, specs: List[spack.spec.Spec], env: Optional[ev.Environment]): | 
		
	
		
			
				|  |  |  |  |     """Returns unordered uninstall_list and remove_list: these may overlap (some things | 
		
	
		
			
				|  |  |  |  |     may be both uninstalled and removed from the current environment). | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     It is assumed we are in an environment if --remove is specified (this | 
		
	
		
			
				|  |  |  |  |     method raises an exception otherwise). | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     uninstall_list is topologically sorted: dependents come before | 
		
	
		
			
				|  |  |  |  |     dependencies (so if a user uninstalls specs in the order provided, | 
		
	
		
			
				|  |  |  |  |     the dependents will always be uninstalled first). | 
		
	
		
			
				|  |  |  |  |     """ | 
		
	
		
			
				|  |  |  |  |     method raises an exception otherwise).""" | 
		
	
		
			
				|  |  |  |  |     if args.remove and not env: | 
		
	
		
			
				|  |  |  |  |         raise ValueError("Can only use --remove when in an environment") | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     # Gets the list of installed specs that match the ones given via cli | 
		
	
		
			
				|  |  |  |  |     # args.all takes care of the case where '-a' is given in the cli | 
		
	
		
			
				|  |  |  |  |     base_uninstall_specs = set(find_matching_specs(env, specs, args.all, args.force)) | 
		
	
		
			
				|  |  |  |  |     matching_specs = find_matching_specs(env, specs, args.all) | 
		
	
		
			
				|  |  |  |  |     dependent_specs = installed_dependents(matching_specs) | 
		
	
		
			
				|  |  |  |  |     all_uninstall_specs = matching_specs + dependent_specs if args.dependents else matching_specs | 
		
	
		
			
				|  |  |  |  |     other_dependent_envs = dependent_environments(all_uninstall_specs, current_env=env) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     active_dpts, outside_dpts = installed_runtime_dependents(base_uninstall_specs, env) | 
		
	
		
			
				|  |  |  |  |     # It will be useful to track the unified set of specs with dependents, as | 
		
	
		
			
				|  |  |  |  |     # well as to separately track specs in the current env with dependents | 
		
	
		
			
				|  |  |  |  |     spec_to_dpts = {} | 
		
	
		
			
				|  |  |  |  |     for spec, dpts in active_dpts.items(): | 
		
	
		
			
				|  |  |  |  |         spec_to_dpts[spec] = list(dpts) | 
		
	
		
			
				|  |  |  |  |     for spec, dpts in outside_dpts.items(): | 
		
	
		
			
				|  |  |  |  |         if spec in spec_to_dpts: | 
		
	
		
			
				|  |  |  |  |             spec_to_dpts[spec].extend(dpts) | 
		
	
		
			
				|  |  |  |  |         else: | 
		
	
		
			
				|  |  |  |  |             spec_to_dpts[spec] = list(dpts) | 
		
	
		
			
				|  |  |  |  |     # There are dependents and we didn't ask to remove dependents | 
		
	
		
			
				|  |  |  |  |     dangling_dependents = dependent_specs and not args.dependents | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     all_uninstall_specs = set(base_uninstall_specs) | 
		
	
		
			
				|  |  |  |  |     if args.dependents: | 
		
	
		
			
				|  |  |  |  |         for spec, lst in active_dpts.items(): | 
		
	
		
			
				|  |  |  |  |             all_uninstall_specs.update(lst) | 
		
	
		
			
				|  |  |  |  |         for spec, lst in outside_dpts.items(): | 
		
	
		
			
				|  |  |  |  |             all_uninstall_specs.update(lst) | 
		
	
		
			
				|  |  |  |  |     # An environment different than the current env depends on | 
		
	
		
			
				|  |  |  |  |     # one or more of the list of all specs to be uninstalled. | 
		
	
		
			
				|  |  |  |  |     dangling_environments = not args.remove and other_dependent_envs | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     # For each spec that we intend to uninstall, this tracks the set of | 
		
	
		
			
				|  |  |  |  |     # environments outside the current active environment which depend on the | 
		
	
		
			
				|  |  |  |  |     # spec. There may be environments not managed directly with Spack: such | 
		
	
		
			
				|  |  |  |  |     # environments would not be included here. | 
		
	
		
			
				|  |  |  |  |     spec_to_other_envs = inactive_dependent_environments( | 
		
	
		
			
				|  |  |  |  |         dependent_environments(all_uninstall_specs) | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     has_error = not args.force and ( | 
		
	
		
			
				|  |  |  |  |         # There are dependents in the current env and we didn't ask to remove | 
		
	
		
			
				|  |  |  |  |         # dependents | 
		
	
		
			
				|  |  |  |  |         (spec_to_dpts and not args.dependents) | 
		
	
		
			
				|  |  |  |  |         # An environment different than the current env (if any) depends on | 
		
	
		
			
				|  |  |  |  |         # one or more of the specs to be uninstalled. There may also be | 
		
	
		
			
				|  |  |  |  |         # packages in those envs which depend on the base set of packages | 
		
	
		
			
				|  |  |  |  |         # to uninstall, but this covers that scenario. | 
		
	
		
			
				|  |  |  |  |         or (not args.remove and spec_to_other_envs) | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  |     has_error = not args.force and (dangling_dependents or dangling_environments) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if has_error: | 
		
	
		
			
				|  |  |  |  |         # say why each problem spec is needed | 
		
	
		
			
				|  |  |  |  |         specs = set(spec_to_dpts) | 
		
	
		
			
				|  |  |  |  |         specs.update(set(spec_to_other_envs))  # environments depend on this | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         for i, spec in enumerate(sorted(specs)): | 
		
	
		
			
				|  |  |  |  |             # space out blocks of reasons | 
		
	
		
			
				|  |  |  |  |             if i > 0: | 
		
	
		
			
				|  |  |  |  |                 print() | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             spec_format = "{name}{@version}{%compiler}{/hash:7}" | 
		
	
		
			
				|  |  |  |  |             tty.info("Will not uninstall %s" % spec.cformat(spec_format), format="*r") | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             dependents = spec_to_dpts.get(spec) | 
		
	
		
			
				|  |  |  |  |             if dependents and not args.dependents: | 
		
	
		
			
				|  |  |  |  |                 print("The following packages depend on it:") | 
		
	
		
			
				|  |  |  |  |                 spack.cmd.display_specs(dependents, **display_args) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |             envs = spec_to_other_envs.get(spec) | 
		
	
		
			
				|  |  |  |  |             if envs: | 
		
	
		
			
				|  |  |  |  |                 if env: | 
		
	
		
			
				|  |  |  |  |                     env_context_qualifier = " other" | 
		
	
		
			
				|  |  |  |  |                 else: | 
		
	
		
			
				|  |  |  |  |                     env_context_qualifier = "" | 
		
	
		
			
				|  |  |  |  |                 print("It is used by the following{0} environments:".format(env_context_qualifier)) | 
		
	
		
			
				|  |  |  |  |                 colify([e.name for e in envs], indent=4) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |         msgs = [] | 
		
	
		
			
				|  |  |  |  |         if spec_to_dpts and not args.dependents: | 
		
	
		
			
				|  |  |  |  |         tty.info("Refusing to uninstall the following specs") | 
		
	
		
			
				|  |  |  |  |         spack.cmd.display_specs(matching_specs, **display_args) | 
		
	
		
			
				|  |  |  |  |         if dangling_dependents: | 
		
	
		
			
				|  |  |  |  |             print() | 
		
	
		
			
				|  |  |  |  |             tty.info("The following dependents are still installed:") | 
		
	
		
			
				|  |  |  |  |             spack.cmd.display_specs(dependent_specs, **display_args) | 
		
	
		
			
				|  |  |  |  |             msgs.append("use `spack uninstall --dependents` to remove dependents too") | 
		
	
		
			
				|  |  |  |  |         if spec_to_other_envs: | 
		
	
		
			
				|  |  |  |  |             msgs.append("use `spack env remove` to remove from environments") | 
		
	
		
			
				|  |  |  |  |         if dangling_environments: | 
		
	
		
			
				|  |  |  |  |             print() | 
		
	
		
			
				|  |  |  |  |             tty.info("The following environments still reference these specs:") | 
		
	
		
			
				|  |  |  |  |             colify([e.name for e in other_dependent_envs.keys()], indent=4) | 
		
	
		
			
				|  |  |  |  |             msgs.append("use `spack env remove` to remove environments") | 
		
	
		
			
				|  |  |  |  |         msgs.append("use `spack uninstall --force` to override") | 
		
	
		
			
				|  |  |  |  |         print() | 
		
	
		
			
				|  |  |  |  |         tty.die("There are still dependents.", *msgs) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     # If we are in an environment, this will track specs in this environment | 
		
	
		
			
				|  |  |  |  |     # which should only be removed from the environment rather than uninstalled | 
		
	
		
			
				|  |  |  |  |     remove_only = set() | 
		
	
		
			
				|  |  |  |  |     remove_only = [] | 
		
	
		
			
				|  |  |  |  |     if args.remove and not args.force: | 
		
	
		
			
				|  |  |  |  |         remove_only.update(spec_to_other_envs) | 
		
	
		
			
				|  |  |  |  |         for specs_in_other_env in other_dependent_envs.values(): | 
		
	
		
			
				|  |  |  |  |             remove_only.extend(specs_in_other_env) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if remove_only: | 
		
	
		
			
				|  |  |  |  |         tty.info( | 
		
	
		
			
				|  |  |  |  |             "The following specs will be removed but not uninstalled because" | 
		
	
	
		
			
				
					
					|  |  |  | @@ -344,23 +265,9 @@ def get_uninstall_list(args, specs, env): | 
		
	
		
			
				|  |  |  |  |     # Compute the set of specs that should be removed from the current env. | 
		
	
		
			
				|  |  |  |  |     # This may overlap (some specs may be uninstalled and also removed from | 
		
	
		
			
				|  |  |  |  |     # the current environment). | 
		
	
		
			
				|  |  |  |  |     if args.remove: | 
		
	
		
			
				|  |  |  |  |         remove_specs = set(base_uninstall_specs) | 
		
	
		
			
				|  |  |  |  |         if args.dependents: | 
		
	
		
			
				|  |  |  |  |             # Any spec matched from the cli, or dependent of, should be removed | 
		
	
		
			
				|  |  |  |  |             # from the environment | 
		
	
		
			
				|  |  |  |  |             for spec, lst in active_dpts.items(): | 
		
	
		
			
				|  |  |  |  |                 remove_specs.update(lst) | 
		
	
		
			
				|  |  |  |  |     else: | 
		
	
		
			
				|  |  |  |  |         remove_specs = set() | 
		
	
		
			
				|  |  |  |  |     remove_specs = all_specs_in_env(env, all_uninstall_specs) if env and args.remove else [] | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     all_uninstall_specs -= remove_only | 
		
	
		
			
				|  |  |  |  |     # Inefficient topological sort: uninstall dependents before dependencies | 
		
	
		
			
				|  |  |  |  |     all_uninstall_specs = sorted( | 
		
	
		
			
				|  |  |  |  |         all_uninstall_specs, key=lambda x: sum(1 for i in x.traverse()), reverse=True | 
		
	
		
			
				|  |  |  |  |     ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     return list(all_uninstall_specs), list(remove_specs) | 
		
	
		
			
				|  |  |  |  |     return list(set(all_uninstall_specs) - set(remove_only)), remove_specs | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def uninstall_specs(args, specs): | 
		
	
	
		
			
				
					
					|  |  |  | @@ -387,13 +294,13 @@ def uninstall_specs(args, specs): | 
		
	
		
			
				|  |  |  |  |         env.regenerate_views() | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | def confirm_removal(specs): | 
		
	
		
			
				|  |  |  |  | def confirm_removal(specs: List[spack.spec.Spec]): | 
		
	
		
			
				|  |  |  |  |     """Display the list of specs to be removed and ask for confirmation. | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     Args: | 
		
	
		
			
				|  |  |  |  |         specs (list): specs to be removed | 
		
	
		
			
				|  |  |  |  |         specs: specs to be removed | 
		
	
		
			
				|  |  |  |  |     """ | 
		
	
		
			
				|  |  |  |  |     tty.msg("The following packages will be uninstalled:\n") | 
		
	
		
			
				|  |  |  |  |     tty.msg("The following {} packages will be uninstalled:\n".format(len(specs))) | 
		
	
		
			
				|  |  |  |  |     spack.cmd.display_specs(specs, **display_args) | 
		
	
		
			
				|  |  |  |  |     print("") | 
		
	
		
			
				|  |  |  |  |     answer = tty.get_yes_or_no("Do you want to proceed?", default=False) | 
		
	
	
		
			
				
					
					|  |  |  |   |