Repository: spack/spack

Issue:
EnvironmentModifications does not work if the package depends on a specific python package
Using sys.executable in EnvironmentModifications does not work if the package depends on a specific python package, since in this case the PYTHONHOME environment variable is set in the build environment, and it is not the correct python home when using the sys.executable interpreter, which is the one used for the spack command itself.

This issue comes from #14252.

For instance consider the following package (we use the spack source url only to have a downloadable package):

```
import os
import tempfile

from spack import *
from spack.util.environment import EnvironmentModifications

class SourceBug(Package):
    homepage = "https://github.com/spack"
    url      = "https://github.com/spack/spack/releases/download/v0.13.3/spack-0.13.3.tar.gz"

    version('0.13.3', sha256='dc6f4d6b89d72f1713324e6fd7d4eba8534ba731c680c848d4169d94e76ef85e')
    version('0.13.2', sha256='30c1a3eb42429344107acdea132be34ed7529489a8dafac9640fbcc86bee0ce1')

    depends_on('python@3.6.8', when='@0.13.3')

    def install(self, spec, prefix):
        with tempfile.NamedTemporaryFile() as source_me:
            source_me.write("MYDIR=/my/dir\n")
            source_me.flush()
            EnvironmentModifications.from_sourcing_file(source_me.name).apply_modifications()
        touch(os.path.join(spec.prefix, 'readme'))
```
In this case installing source-bug@0.13.2 works, while installing source-bug@0.13.3 fails:
```
==> Error: ProcessError: Command exited with status 1:
    '/bin/bash' '-c' 'source /tmp/tmpOePPWf &> /dev/null && /usr/bin/python -c "import os, json; print(json.dumps(dict(os.environ)))"'
See build log for details:
  .../spack/var/spack/stage/spack-stage-source-bug-0.13.3-wux6dhmj5htnxqhxqyqsykzdc2lptjto/spack-build-out.txt
```
The error is:
```
==> [2020-01-07-09:47:11.939684] '/bin/bash' '-c' 'source /tmp/tmpOePPWf &> /dev/null && /usr/bin/python -c "import os, json; print(json.dumps(dict(os.environ)))"'
ImportError: No module named site
```



Discussion:

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
@@ -17,6 +17,7 @@
 
 import llnl.util.tty as tty
 import spack.util.executable as executable
+from spack.util.module_cmd import py_cmd
 
 from llnl.util.lang import dedupe
 
@@ -918,8 +919,8 @@ def _source_single_file(file_and_args, environment):
         source_file.extend(x for x in file_and_args)
         source_file = ' '.join(source_file)
 
-        dump_cmd = 'import os, json; print(json.dumps(dict(os.environ)))'
-        dump_environment = sys.executable + ' -c "{0}"'.format(dump_cmd)
+        dump_environment = 'PYTHONHOME="{0}" "{1}" -c "{2}"'.format(
+            sys.prefix, sys.executable, py_cmd)
 
         # Try to source the file
         source_file_arguments = ' '.join([
diff --git a/lib/spack/spack/util/module_cmd.py b/lib/spack/spack/util/module_cmd.py
--- a/lib/spack/spack/util/module_cmd.py
+++ b/lib/spack/spack/util/module_cmd.py
@@ -18,7 +18,7 @@
 # This list is not exhaustive. Currently we only use load and unload
 # If we need another option that changes the environment, add it here.
 module_change_commands = ['load', 'swap', 'unload', 'purge', 'use', 'unuse']
-py_cmd = "'import os;import json;print(json.dumps(dict(os.environ)))'"
+py_cmd = 'import os; import json; print(json.dumps(dict(os.environ)))'
 
 # This is just to enable testing. I hate it but we can't find a better way
 _test_mode = False
@@ -32,7 +32,8 @@ def module(*args):
     if args[0] in module_change_commands:
         # Do the module manipulation, then output the environment in JSON
         # and read the JSON back in the parent process to update os.environ
-        module_cmd += ' >/dev/null;' + sys.executable + ' -c %s' % py_cmd
+        module_cmd += ' > /dev/null; PYTHONHOME="{0}" "{1}" -c "{2}"'.format(
+            sys.prefix, sys.executable, py_cmd)
         module_p  = subprocess.Popen(module_cmd,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.STDOUT,


Test patch: