Repository: spack/spack

Issue:
Intel compiler fails to install
Installation of the intel compiler fails when trying to generate the `tcl` module. According to `git bisect` this is due to commit 2fe1ecbaa2f87bc3f11f73ee41828ea5b9a6b3d5, #10753.
The error produced is:
```console
==> Error: TypeError: string indices must be integers, not str

/opt/ssoft/spack/lib/spack/spack/build_systems/intel.py:1030, in setup_environment:
       1027        # I am not sure about earlier versions.
       1028        # if sys.platform == 'darwin':
       1029        #     args = ()
  >>   1030
       1031        run_env.extend(EnvironmentModifications.from_sourcing_file(f, *args))
```



Discussion:
@glennpj I'll have a look asap, as it seems something @svenevs already reported in https://github.com/spack/spack/pull/10753#issuecomment-475212313 I wasn't able to reproduce it, but I wonder if I missed to clean my configuration files properly and that hid the problem.
I'm hitting this problem too in a slightly different case: when installing intel-mpi on a system where the intel compiler is already installed.  I was able to narrow it down to the same commit as well.

In my case the error looks like this:

```
[carns@beboplogin2 spack ((2fe1ecb...))]$ spack install intel-mpi
==> intel-mpi@2017.3 : has external module in intel-mpi/2017.3-dfphq6k
==> intel-mpi@2017.3 : is actually installed in /blues/gpfs/home/software/spack-0.10.1/opt/spack/linux-centos7-x86_64/intel-17.0.4/intel-mpi-2017.3-dfphq6kavje2olnichisvjjndtridrok/compilers_and_libraries_2017.4.196/linux/mpi/intel64
==> intel-mpi@2017.3 : generating module file
==> Warning: Quotes in command arguments can confuse scripts like configure.
  The following arguments may cause problems when executed:
      source /blues/gpfs/home/software/spack-0.10.1/opt/spack/linux-centos7-x86_64/intel-17.0.4/intel-mpi-2017.3-dfphq6kavje2olnichisvjjndtridrok/compilers_and_libraries_2017.4.196/linux/mpi/intel64/bin/mpivars.sh intel64 &> /dev/null && python -c "import os, json; print(json.dumps(dict(os.environ)))"
  Quotes aren't needed because spack doesn't use a shell.
  Consider removing them
==> Error: string indices must be integers, not str
```

I can trigger the same error if I attempt to install intel-mpi from scratch, but in the above example I'm trying to use an intel-mpi that's already present on the system by putting this in my packages.yaml:

```
    intel-mpi:
        modules:
            intel-mpi@2017.3%gcc@7.3.0: intel-mpi/2017.3-dfphq6k
        buildable: False
```

This works fine with older git revisions.
I was able to reproduce the same error as @svenevs on one of our clusters. I'll try to fix this by the day.

Resolving patch:
diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py
--- a/lib/spack/spack/util/environment.py
+++ b/lib/spack/spack/util/environment.py
@@ -607,12 +607,12 @@ def return_separator_if_any(*args):
             env.unset(x)
 
         for x in modified_variables:
-            before = before[x]
-            after = after[x]
-            sep = return_separator_if_any(before, after)
+            value_before = before[x]
+            value_after = after[x]
+            sep = return_separator_if_any(value_before, value_after)
             if sep:
-                before_list = before.split(sep)
-                after_list = after.split(sep)
+                before_list = value_before.split(sep)
+                after_list = value_after.split(sep)
 
                 # Filter out empty strings
                 before_list = list(filter(None, before_list))
@@ -623,8 +623,8 @@ def return_separator_if_any(*args):
                     before_list = list(dedupe(before_list))
                     after_list = list(dedupe(after_list))
                     # The reassembled cleaned entries
-                    before = sep.join(before_list)
-                    after = sep.join(after_list)
+                    value_before = sep.join(before_list)
+                    value_after = sep.join(after_list)
 
                 # Paths that have been removed
                 remove_list = [
@@ -638,12 +638,12 @@ def return_separator_if_any(*args):
                     end = after_list.index(remaining_list[-1])
                     search = sep.join(after_list[start:end + 1])
                 except IndexError:
-                    env.prepend_path(x, after)
+                    env.prepend_path(x, value_after)
                     continue
 
-                if search not in before:
+                if search not in value_before:
                     # We just need to set the variable to the new value
-                    env.prepend_path(x, after)
+                    env.prepend_path(x, value_after)
                 else:
                     try:
                         prepend_list = after_list[:start]
@@ -663,7 +663,7 @@ def return_separator_if_any(*args):
                         env.prepend_path(x, item)
             else:
                 # We just need to set the variable to the new value
-                env.set(x, after)
+                env.set(x, value_after)
 
         return env
 


Test patch:
diff --git a/lib/spack/spack/test/environment_modifications.py b/lib/spack/spack/test/environment_modifications.py
--- a/lib/spack/spack/test/environment_modifications.py
+++ b/lib/spack/spack/test/environment_modifications.py
@@ -399,6 +399,7 @@ def test_sanitize_regex(env, blacklist, whitelist, expected, deleted):
     assert all(x not in after for x in deleted)
 
 
+@pytest.mark.regression('12085')
 @pytest.mark.parametrize('before,after,search_list', [
     # Set environment variables
     ({}, {'FOO': 'foo'}, [environment.SetEnv('FOO', 'foo')]),
@@ -420,7 +421,12 @@ def test_sanitize_regex(env, blacklist, whitelist, expected, deleted):
     ({'FOO_PATH': '/a/path:/b/path'}, {'FOO_PATH': '/c/path:/a/path'}, [
         environment.RemovePath('FOO_PATH', '/b/path'),
         environment.PrependPath('FOO_PATH', '/c/path')
-    ])
+    ]),
+    # Modify two variables in the same environment
+    ({'FOO': 'foo', 'BAR': 'bar'}, {'FOO': 'baz', 'BAR': 'baz'}, [
+        environment.SetEnv('FOO', 'baz'),
+        environment.SetEnv('BAR', 'baz'),
+    ]),
 ])
 def test_from_environment_diff(before, after, search_list):