Repository: spack/spack

Issue:
`spack env activate` does not traverse run dependencies for environment modifications
Moving this from #gripes on Slack.

Mine @SteVwonder question was:

> Why does spack env activate not run setup_run_environment for dependencies of packages listed in an env yaml?  Due to this behavior spack load flux-sched behaves differently from spack env activate . with a yaml file containing flux-sched as the only package.

@tgamblin answer was:

> `_env_modifications_for_default_view` doesn’t currently traverse run deps, but it really should



Discussion:
@scheibelp do you have time to take a stab at this one?
I think I can look at this on May 4th

EDIT (5/4): other issues are taking longer than expected. My new estimated start date for this is 5/6.

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
@@ -1276,19 +1276,36 @@ def check_views(self):
     def _env_modifications_for_default_view(self, reverse=False):
         all_mods = spack.util.environment.EnvironmentModifications()
 
+        visited = set()
+
         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)
+        for _, root_spec in self.concretized_specs():
+            if root_spec in self.default_view and root_spec.package.installed:
+                for spec in root_spec.traverse(deptype='run', root=True):
+                    if spec.name in visited:
+                        # It is expected that only one instance of the package
+                        # can be added to the environment - do not attempt to
+                        # add multiple.
+                        tty.debug(
+                            "Not adding {0} to shell modifications: "
+                            "this package has already been added".format(
+                                spec.format("{name}/{hash:7}")
+                            )
+                        )
+                        continue
+                    else:
+                        visited.add(spec.name)
+
+                    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
 
diff --git a/var/spack/repos/builtin.mock/packages/depends-on-run-env/package.py b/var/spack/repos/builtin.mock/packages/depends-on-run-env/package.py
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/depends-on-run-env/package.py
@@ -0,0 +1,22 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from spack import *
+
+
+class DependsOnRunEnv(Package):
+    """This package has a runtime dependency on another package which needs
+       to perform shell modifications to run.
+    """
+
+    homepage = "http://www.example.com"
+    url      = "http://www.example.com/a-1.0.tar.gz"
+
+    version('1.0', '0123456789abcdef0123456789abcdef')
+
+    depends_on('modifies-run-env', type=('run',))
+
+    def install(self, spec, prefix):
+        mkdirp(prefix.bin)
diff --git a/var/spack/repos/builtin.mock/packages/modifies-run-env/package.py b/var/spack/repos/builtin.mock/packages/modifies-run-env/package.py
new file mode 100644
--- /dev/null
+++ b/var/spack/repos/builtin.mock/packages/modifies-run-env/package.py
@@ -0,0 +1,21 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from spack import *
+
+
+class ModifiesRunEnv(Package):
+    """Dependency package which needs to make shell modifications to run"""
+
+    homepage = "http://www.example.com"
+    url      = "http://www.example.com/a-1.0.tar.gz"
+
+    version('1.0', '0123456789abcdef0123456789abcdef')
+
+    def setup_run_environment(self, env):
+        env.set('DEPENDENCY_ENV_VAR', '1')
+
+    def install(self, spec, prefix):
+        mkdirp(prefix.bin)


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
@@ -200,6 +200,19 @@ def setup_error(pkg, env):
     assert "Warning: couldn't get environment settings" in err
 
 
+def test_activate_adds_transitive_run_deps_to_path(
+        install_mockery, mock_fetch, monkeypatch):
+    env('create', 'test')
+    install = SpackCommand('install')
+
+    e = ev.read('test')
+    with e:
+        install('depends-on-run-env')
+
+    cmds = spack.environment.activate(e)
+    assert 'DEPENDENCY_ENV_VAR=1' in cmds
+
+
 def test_env_install_same_spec_twice(install_mockery, mock_fetch):
     env('create', 'test')