Repository: spack/spack

Issue:
Spack breaking when installing Python in an environment
Spack breaks when installing an environment that contains python + a package that fails to build.

### Steps to reproduce the issue

Assume that you have a package "somepackage" that fails to build with spack (in my case this was a one my own python packages that would fail in the setup.py script for some reasons). Do the following to break spack:

```console
$ spack env create myenv
$ spack env activate myenv
$ spack add python
$ spack add somepackage
$ spack install # this will install everything (including python) up to somepackage, which will fail to install, as expected
```
After this series of commands, all spack commands will fail with a RuntimeError from python indicating:
```
RuntimeError: Unable to locate python command in /projects/spack/var/spack/environments/myenv/.spack-env/view/bin
```


Discussion:
I experienced this exact same problem, although I was struggling to figure out exactly how to reproduce it. The workaround I've found for now is to `rm -rf .spack-env spack.lock` and start over. You won't have to re-install anything, just re-concretize and re-symlink.
Yes, I figured out this solution too, though it still requires to log out and log back in to your machine (or open a new terminal if it's your laptop).

The problem is, I had this happen in the middle of a CI job that tries to build about 20 packages, reporting which ones failed to build and which ones didn't. You can imagine how puzzled I was that everytime this particular package that required python failed to build, all the subsequent packages failed as well (simply because the spack command would no longer work).
[Another case](https://groups.google.com/forum/#!topic/spack/RBzLouf-vGA) of a user hit by this

Resolving patch:
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
--- a/lib/spack/spack/environment.py
+++ b/lib/spack/spack/environment.py
@@ -1091,6 +1091,25 @@ def regenerate_views(self):
         for view in self.views.values():
             view.regenerate(specs, self.roots())
 
+    def _env_modifications_for_default_view(self, reverse=False):
+        all_mods = spack.util.environment.EnvironmentModifications()
+
+        errors = []
+        for _, spec in self.concretized_specs():
+            if spec in self.default_view and spec.package.installed:
+                try:
+                    mods = uenv.environment_modifications_for_spec(
+                        spec, self.default_view)
+                except Exception as e:
+                    msg = ("couldn't get environment settings for %s"
+                           % spec.format("{name}@{version} /{hash:7}"))
+                    errors.append((msg, str(e)))
+                    continue
+
+                all_mods.extend(mods.reversed() if reverse else mods)
+
+        return all_mods, errors
+
     def add_default_view_to_shell(self, shell):
         env_mod = spack.util.environment.EnvironmentModifications()
 
@@ -1101,10 +1120,11 @@ def add_default_view_to_shell(self, shell):
         env_mod.extend(uenv.unconditional_environment_modifications(
             self.default_view))
 
-        for _, spec in self.concretized_specs():
-            if spec in self.default_view and spec.package.installed:
-                env_mod.extend(uenv.environment_modifications_for_spec(
-                    spec, self.default_view))
+        mods, errors = self._env_modifications_for_default_view()
+        env_mod.extend(mods)
+        if errors:
+            for err in errors:
+                tty.warn(*err)
 
         # deduplicate paths from specs mapped to the same location
         for env_var in env_mod.group_by_name():
@@ -1122,11 +1142,9 @@ def rm_default_view_from_shell(self, shell):
         env_mod.extend(uenv.unconditional_environment_modifications(
             self.default_view).reversed())
 
-        for _, spec in self.concretized_specs():
-            if spec in self.default_view and spec.package.installed:
-                env_mod.extend(
-                    uenv.environment_modifications_for_spec(
-                        spec, self.default_view).reversed())
+        mods, _ = self._env_modifications_for_default_view(reverse=True)
+        env_mod.extend(mods)
+
         return env_mod.shell_modifications(shell)
 
     def _add_concrete_spec(self, spec, concrete, new=True):


Test patch:
diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py
--- a/lib/spack/spack/test/cmd/env.py
+++ b/lib/spack/spack/test/cmd/env.py
@@ -163,6 +163,28 @@ def test_env_install_single_spec(install_mockery, mock_fetch):
     assert e.specs_by_hash[e.concretized_order[0]].name == 'cmake-client'
 
 
+def test_env_modifications_error_on_activate(
+        install_mockery, mock_fetch, monkeypatch, capfd):
+    env('create', 'test')
+    install = SpackCommand('install')
+
+    e = ev.read('test')
+    with e:
+        install('cmake-client')
+
+    def setup_error(pkg, env):
+        raise RuntimeError("cmake-client had issues!")
+
+    pkg = spack.repo.path.get_pkg_class("cmake-client")
+    monkeypatch.setattr(pkg, "setup_run_environment", setup_error)
+    with e:
+        pass
+
+    _, err = capfd.readouterr()
+    assert "cmake-client had issues!" in err
+    assert "Warning: couldn't get environment settings" in err
+
+
 def test_env_install_same_spec_twice(install_mockery, mock_fetch, capfd):
     env('create', 'test')