Skip to content

Commit

Permalink
modules: append trailing delimiter to MANPATH when set (spack#36678)
Browse files Browse the repository at this point in the history
Update modulefile templates to append a trailing delimiter to MANPATH
environment variable, if the modulefile sets it.

With a trailing delimiter at ends of MANPATH's value, man will search
the system man pages after searching the specific paths set.

Using append-path/append_path to add this element, the module tool
ensures it is appended only once. When modulefile is unloaded, the
number of append attempt is decreased, thus the trailing delimiter is
removed only if this number equals 0.

Disclaimer: no path element should be appended to MANPATH by generated
modulefiles. It should always be prepended to ensure this variable's
value ends with the trailing delimiter.

Fixes spack#11355.
  • Loading branch information
xdelaruelle authored Jun 13, 2023
1 parent bd2f78a commit 746eaaf
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 1 deletion.
16 changes: 15 additions & 1 deletion lib/spack/spack/modules/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

import llnl.util.filesystem
import llnl.util.tty as tty
from llnl.util.lang import dedupe
from llnl.util.lang import dedupe, memoized

import spack.build_environment
import spack.config
Expand Down Expand Up @@ -672,6 +672,7 @@ def configure_options(self):
return None

@tengine.context_property
@memoized
def environment_modifications(self):
"""List of environment modifications to be processed."""
# Modifications guessed by inspecting the spec prefix
Expand Down Expand Up @@ -742,6 +743,19 @@ def environment_modifications(self):

return [(type(x).__name__, x) for x in env if x.name not in exclude]

@tengine.context_property
def has_manpath_modifications(self):
"""True if MANPATH environment variable is modified."""
for modification_type, cmd in self.environment_modifications:
if not isinstance(
cmd, (spack.util.environment.PrependPath, spack.util.environment.AppendPath)
):
continue
if cmd.name == "MANPATH":
return True
else:
return False

@tengine.context_property
def autoload(self):
"""List of modules that needs to be loaded automatically."""
Expand Down
31 changes: 31 additions & 0 deletions lib/spack/spack/test/modules/lmod.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,37 @@ def test_prepend_path_separator(self, modulefile_content, module_configuration):
assert len([x for x in content if 'append_path("SPACE", "qux", " ")' in x]) == 1
assert len([x for x in content if 'remove_path("SPACE", "qux", " ")' in x]) == 1

@pytest.mark.regression("11355")
def test_manpath_setup(self, modulefile_content, module_configuration):
"""Tests specific setup of MANPATH environment variable."""

module_configuration("autoload_direct")

# no manpath set by module
content = modulefile_content("mpileaks")
assert len([x for x in content if 'append_path("MANPATH", "", ":")' in x]) == 0

# manpath set by module with prepend_path
content = modulefile_content("module-manpath-prepend")
assert (
len([x for x in content if 'prepend_path("MANPATH", "/path/to/man", ":")' in x]) == 1
)
assert (
len([x for x in content if 'prepend_path("MANPATH", "/path/to/share/man", ":")' in x])
== 1
)
assert len([x for x in content if 'append_path("MANPATH", "", ":")' in x]) == 1

# manpath set by module with append_path
content = modulefile_content("module-manpath-append")
assert len([x for x in content if 'append_path("MANPATH", "/path/to/man", ":")' in x]) == 1
assert len([x for x in content if 'append_path("MANPATH", "", ":")' in x]) == 1

# manpath set by module with setenv
content = modulefile_content("module-manpath-setenv")
assert len([x for x in content if 'setenv("MANPATH", "/path/to/man")' in x]) == 1
assert len([x for x in content if 'append_path("MANPATH", "", ":")' in x]) == 0

def test_help_message(self, modulefile_content, module_configuration):
"""Tests the generation of module help message."""

Expand Down
40 changes: 40 additions & 0 deletions lib/spack/spack/test/modules/tcl.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,46 @@ def test_prepend_path_separator(self, modulefile_content, module_configuration):
assert len([x for x in content if 'append-path --delim " " SPACE "qux"' in x]) == 1
assert len([x for x in content if 'remove-path --delim " " SPACE "qux"' in x]) == 1

@pytest.mark.regression("11355")
def test_manpath_setup(self, modulefile_content, module_configuration):
"""Tests specific setup of MANPATH environment variable."""

module_configuration("autoload_direct")

# no manpath set by module
content = modulefile_content("mpileaks")
assert len([x for x in content if 'append-path --delim ":" MANPATH ""' in x]) == 0

# manpath set by module with prepend-path
content = modulefile_content("module-manpath-prepend")
assert (
len([x for x in content if 'prepend-path --delim ":" MANPATH "/path/to/man"' in x])
== 1
)
assert (
len(
[
x
for x in content
if 'prepend-path --delim ":" MANPATH "/path/to/share/man"' in x
]
)
== 1
)
assert len([x for x in content if 'append-path --delim ":" MANPATH ""' in x]) == 1

# manpath set by module with append-path
content = modulefile_content("module-manpath-append")
assert (
len([x for x in content if 'append-path --delim ":" MANPATH "/path/to/man"' in x]) == 1
)
assert len([x for x in content if 'append-path --delim ":" MANPATH ""' in x]) == 1

# manpath set by module with setenv
content = modulefile_content("module-manpath-setenv")
assert len([x for x in content if 'setenv MANPATH "/path/to/man"' in x]) == 1
assert len([x for x in content if 'append-path --delim ":" MANPATH ""' in x]) == 0

def test_help_message(self, modulefile_content, module_configuration):
"""Tests the generation of module help message."""

Expand Down
4 changes: 4 additions & 0 deletions share/spack/templates/modules/modulefile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ setenv("{{ cmd.name }}", "{{ cmd.value }}")
unsetenv("{{ cmd.name }}")
{% endif %}
{% endfor %}
{# Make sure system man pages are enabled by appending trailing delimiter to MANPATH #}
{% if has_manpath_modifications %}
append_path("MANPATH", "", ":")
{% endif %}
{% endblock %}

{% block footer %}
Expand Down
4 changes: 4 additions & 0 deletions share/spack/templates/modules/modulefile.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ unsetenv {{ cmd.name }}
{% endif %}
{# #}
{% endfor %}
{# Make sure system man pages are enabled by appending trailing delimiter to MANPATH #}
{% if has_manpath_modifications %}
append-path --delim ":" MANPATH ""
{% endif %}
{% endblock %}

{% block footer %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2013-2023 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.package import *


class ModuleManpathAppend(Package):
homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/module-manpath-append-1.0.tar.gz"

version("1.0", "0123456789abcdef0123456789abcdef")

def setup_run_environment(self, env):
env.append_path("MANPATH", "/path/to/man")
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2013-2023 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.package import *


class ModuleManpathPrepend(Package):
homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/module-manpath-prepend-1.0.tar.gz"

version("1.0", "0123456789abcdef0123456789abcdef")

def setup_run_environment(self, env):
env.prepend_path("MANPATH", "/path/to/man")
env.prepend_path("MANPATH", "/path/to/share/man")
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2013-2023 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.package import *


class ModuleManpathSetenv(Package):
homepage = "http://www.llnl.gov"
url = "http://www.llnl.gov/module-manpath-setenv-1.0.tar.gz"

version("1.0", "0123456789abcdef0123456789abcdef")

def setup_run_environment(self, env):
env.set("MANPATH", "/path/to/man")

0 comments on commit 746eaaf

Please sign in to comment.