Repository: spack/spack

Issue:
MANPATH not properly handled by environments/modules
Loading an environment/module breaks the `man` command. `MANPATH` is used to give a list of directories to search for man pages. The default (no path) searches typical system locations. As soon as you populate `MANPATH` (e.g. `/home/adam/foo/.spack-env/view/share/man:/home/adam/foo/.spack-env/view/man`), it no longer searches system locations. You need to add a trailing colon to `MANPATH` to get it to search system locations.

### Steps to reproduce the issue

The issue affects environments:
```console
$ spack env activate foo
$ man bash
No manual entry for bash
```
It also affects modules and the load command:
```console
$ spack load zlib
$ man bash
No manual entry for bash
```

### Information on your system

* **Spack:** 0.14.2-1395-4c976dfe6
* **Python:** 3.7.7
* **Platform:** linux-ubuntu20.04-skylake (via WSL)

### Known Workarounds

For now, I can do this to solve the issue:
```console
$ export MANPATH+=:
```
In the long run, Spack should be smart enough to always add a trailing colon to `MANPATH`, even if we have to make a special case for it.

### Additional information

- [x] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [x] I have searched the issues of this repo and believe this is not a duplicate
- [x] I have run the failing commands in debug mode and reported the output

This is the best summary of the problem I've found so far: https://github.com/fish-shell/fish-shell/issues/2090

@alalazo 


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
@@ -611,7 +611,6 @@ def apply_modifications(self, env=None):
     def shell_modifications(self, shell='sh', explicit=False, env=None):
         """Return shell code to apply the modifications and clears the list."""
         modifications = self.group_by_name()
-        new_env = os.environ.copy()
 
         if env is None:
             env = os.environ
@@ -622,6 +621,9 @@ def shell_modifications(self, shell='sh', explicit=False, env=None):
             for x in actions:
                 x.execute(new_env)
 
+        if 'MANPATH' in new_env and not new_env.get('MANPATH').endswith(':'):
+            new_env['MANPATH'] += ':'
+
         cmds = ''
 
         for name in sorted(set(modifications)):
diff --git a/var/spack/repos/builtin.mock/packages/mpileaks/package.py b/var/spack/repos/builtin.mock/packages/mpileaks/package.py
--- a/var/spack/repos/builtin.mock/packages/mpileaks/package.py
+++ b/var/spack/repos/builtin.mock/packages/mpileaks/package.py
@@ -28,6 +28,7 @@ class Mpileaks(Package):
 
     def install(self, spec, prefix):
         touch(prefix.mpileaks)
+        mkdirp(prefix.man)
 
     def setup_environment(self, senv, renv):
         renv.set('FOOBAR', self.name)


Test patch:
diff --git a/lib/spack/spack/test/cmd/load.py b/lib/spack/spack/test/cmd/load.py
--- a/lib/spack/spack/test/cmd/load.py
+++ b/lib/spack/spack/test/cmd/load.py
@@ -3,6 +3,7 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 import os
+import re
 
 import pytest
 
@@ -16,6 +17,24 @@
 location = SpackCommand('location')
 
 
+def test_manpath_trailing_colon(install_mockery, mock_fetch, mock_archive,
+                                mock_packages, working_env):
+    """Test that the commands generated by load add the MANPATH prefix
+    inspections. Also test that Spack correctly preserves the default/existing
+    manpath search path via a trailing colon"""
+    install('mpileaks')
+
+    sh_out = load('--sh', '--only', 'package', 'mpileaks')
+    lines = sh_out.split('\n')
+    assert any(re.match(r'export MANPATH=.*:;', ln) for ln in lines)
+
+    os.environ['MANPATH'] = '/tmp/man:'
+
+    sh_out = load('--sh', '--only', 'package', 'mpileaks')
+    lines = sh_out.split('\n')
+    assert any(re.match(r'export MANPATH=.*:/tmp/man:;', ln) for ln in lines)
+
+
 def test_load(install_mockery, mock_fetch, mock_archive, mock_packages):
     """Test that the commands generated by load add the specified prefix
     inspections. Also test that Spack records loaded specs by hash in the