Repository: spack/spack

Issue:
module refresh fails for libxml2+python
the package `libxml2 +python` builds ok, but later module refresh fails:

```
$ spack module tcl refresh libxml2
==> You are about to regenerate tcl module files for:

-- linux-ubuntu16.04-x86_64 / gcc@7.4.0 -------------------------
tqifilo libxml2@2.9.8  e4joz6t libxml2@2.9.8

==> Do you want to proceed? [y/n] y
==> Regenerating tcl module files
==> Warning: Could not write module file [/usr/local/pkg/Modules/spack/modules/linux-ubuntu16.04-x86_64/libxml2/.2.9.8-gcc7.4.0-py3-e4joz6t]
==> Warning: 	--> global name 'site_packages_dir' is not defined <--
```
otf2: fix `NameError: name spack_cc is not defined` when installing fr…
Installation of `otf2` from build cache fails because of:

```
NameError: name 'spack_cc' is not defined
```

which is referenced in `configure_args()` section of the otf2 package. I'm not sure this is the best way to address it, here I'm substituting the logic from I was trying to substitute how spack_cc et. al. get defined in https://github.com/spack/spack/blob/develop/lib/spack/spack/build_environment.py#L460



Discussion:

Resolving patch:
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -463,6 +463,10 @@ def log(pkg):
     # Archive the environment used for the build
     install(pkg.env_path, pkg.install_env_path)
 
+    if os.path.exists(pkg.configure_args_path):
+        # Archive the args used for the build
+        install(pkg.configure_args_path, pkg.install_configure_args_path)
+
     # Finally, archive files that are specific to each package
     with working_dir(pkg.stage.path):
         errors = six.StringIO()
@@ -1013,6 +1017,18 @@ def build_process():
                         # Save the build environment in a file before building.
                         dump_environment(pkg.env_path)
 
+                        for attr in ('configure_args', 'cmake_args'):
+                            try:
+                                configure_args = getattr(pkg, attr)()
+                                configure_args = ' '.join(configure_args)
+
+                                with open(pkg.configure_args_path, 'w') as args_file:
+                                    args_file.write(configure_args)
+
+                                break
+                            except Exception:
+                                pass
+
                         # cache debug settings
                         debug_enabled = tty.is_debug()
 
diff --git a/lib/spack/spack/modules/common.py b/lib/spack/spack/modules/common.py
--- a/lib/spack/spack/modules/common.py
+++ b/lib/spack/spack/modules/common.py
@@ -623,16 +623,9 @@ def configure_options(self):
             msg = 'unknown, software installed outside of Spack'
             return msg
 
-        # This is quite simple right now, but contains information on how
-        # to call different build system classes.
-        for attr in ('configure_args', 'cmake_args'):
-            try:
-                configure_args = getattr(pkg, attr)()
-                return ' '.join(configure_args)
-            except (AttributeError, IOError, KeyError):
-                # The method doesn't exist in the current spec,
-                # or it's not usable
-                pass
+        if os.path.exists(pkg.install_configure_args_path):
+            with open(pkg.install_configure_args_path, 'r') as args_file:
+                return args_file.read()
 
         # Returning a false-like value makes the default templates skip
         # the configure option section
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -68,6 +68,9 @@
 # Filename for the Spack build/install environment file.
 _spack_build_envfile = 'spack-build-env.txt'
 
+# Filename for the Spack configure args file.
+_spack_configure_argsfile = 'spack-configure-args.txt'
+
 
 class InstallPhase(object):
     """Manages a single phase of the installation.
@@ -896,6 +899,18 @@ def install_log_path(self):
         # Otherwise, return the current install log path name.
         return os.path.join(install_path, _spack_build_logfile)
 
+    @property
+    def configure_args_path(self):
+        """Return the configure args file path associated with staging."""
+        return os.path.join(self.stage.path, _spack_configure_argsfile)
+
+    @property
+    def install_configure_args_path(self):
+        """Return the configure args file path on successful installation."""
+        install_path = spack.store.layout.metadata_path(self.spec)
+
+        return os.path.join(install_path, _spack_configure_argsfile)
+
     def _make_fetcher(self):
         # Construct a composite fetcher that always contains at least
         # one element (the root package). In case there are resources


Test patch:
diff --git a/lib/spack/spack/test/install.py b/lib/spack/spack/test/install.py
--- a/lib/spack/spack/test/install.py
+++ b/lib/spack/spack/test/install.py
@@ -15,7 +15,8 @@
 import spack.repo
 import spack.store
 from spack.spec import Spec
-from spack.package import _spack_build_envfile, _spack_build_logfile
+from spack.package import (_spack_build_envfile, _spack_build_logfile,
+                           _spack_configure_argsfile)
 
 
 def test_install_and_uninstall(install_mockery, mock_fetch, monkeypatch):
@@ -410,6 +411,9 @@ def test_pkg_install_paths(install_mockery):
     env_path = os.path.join(spec.prefix, '.spack', _spack_build_envfile)
     assert spec.package.install_env_path == env_path
 
+    args_path = os.path.join(spec.prefix, '.spack', _spack_configure_argsfile)
+    assert spec.package.install_configure_args_path == args_path
+
     # Backward compatibility checks
     log_dir = os.path.dirname(log_path)
     mkdirp(log_dir)
@@ -448,6 +452,7 @@ def test_pkg_install_log(install_mockery):
     with working_dir(log_dir):
         touch(log_path)
         touch(spec.package.env_path)
+        touch(spec.package.configure_args_path)
 
     install_path = os.path.dirname(spec.package.install_log_path)
     mkdirp(install_path)
@@ -456,6 +461,7 @@ def test_pkg_install_log(install_mockery):
 
     assert os.path.exists(spec.package.install_log_path)
     assert os.path.exists(spec.package.install_env_path)
+    assert os.path.exists(spec.package.install_configure_args_path)
 
     # Cleanup
     shutil.rmtree(log_dir)