summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Becker <becker33@llnl.gov>2023-04-17 22:17:11 -0700
committerGitHub <noreply@github.com>2023-04-17 22:17:11 -0700
commit480b7f397e029a3de8d8a032170cd9e9862d64d3 (patch)
tree9068a3f6182621bb5da534fac935c3f5ec4c116c
parent2bc1779a71c3ddc3a228df553196e16ff0ad59b3 (diff)
downloadspack-480b7f397e029a3de8d8a032170cd9e9862d64d3.tar.gz
spack-480b7f397e029a3de8d8a032170cd9e9862d64d3.tar.bz2
spack-480b7f397e029a3de8d8a032170cd9e9862d64d3.tar.xz
spack-480b7f397e029a3de8d8a032170cd9e9862d64d3.zip
Allow users to remove items from hierarchy per-path (#31351)
* lmod modules: allow users to remove items from hierarchy per-spec This allows MPI wrappers that depend on MPI to be removed from the MPI portion of the hierarchy and be made available when the appropriate compiler is loaded. module load gcc module load mpi-wrapper # implicitly loads mpi module load hdf5 This allows users to treat an mpi wrapper like an mpi program
-rw-r--r--lib/spack/spack/modules/lmod.py15
-rw-r--r--lib/spack/spack/schema/modules.py6
-rw-r--r--lib/spack/spack/test/data/modules/lmod/complex_hierarchy.yaml3
-rw-r--r--lib/spack/spack/test/modules/lmod.py12
4 files changed, 34 insertions, 2 deletions
diff --git a/lib/spack/spack/modules/lmod.py b/lib/spack/spack/modules/lmod.py
index 81c3e74c42..812d4fcc9e 100644
--- a/lib/spack/spack/modules/lmod.py
+++ b/lib/spack/spack/modules/lmod.py
@@ -127,6 +127,11 @@ class LmodConfiguration(BaseConfiguration):
return configuration(self.name).get("core_specs", [])
@property
+ def filter_hierarchy_specs(self):
+ """Returns the dict of specs with modified hierarchies"""
+ return configuration(self.name).get("filter_hierarchy_specs", {})
+
+ @property
def hierarchy_tokens(self):
"""Returns the list of tokens that are part of the modulefile
hierarchy. 'compiler' is always present.
@@ -160,11 +165,21 @@ class LmodConfiguration(BaseConfiguration):
if any(self.spec.satisfies(core_spec) for core_spec in self.core_specs):
return {"compiler": self.core_compilers[0]}
+ hierarchy_filter_list = []
+ for spec, filter_list in self.filter_hierarchy_specs.items():
+ if self.spec.satisfies(spec):
+ hierarchy_filter_list = filter_list
+ break
+
# Keep track of the requirements that this package has in terms
# of virtual packages that participate in the hierarchical structure
requirements = {"compiler": self.spec.compiler}
# For each virtual dependency in the hierarchy
for x in self.hierarchy_tokens:
+ # Skip anything filtered for this spec
+ if x in hierarchy_filter_list:
+ continue
+
# If I depend on it
if x in self.spec and not self.spec.package.provides(x):
requirements[x] = self.spec[x] # record the actual provider
diff --git a/lib/spack/spack/schema/modules.py b/lib/spack/spack/schema/modules.py
index 975b512c7f..261065c8b3 100644
--- a/lib/spack/spack/schema/modules.py
+++ b/lib/spack/spack/schema/modules.py
@@ -17,7 +17,7 @@ import spack.schema.projections
#: THIS NEEDS TO BE UPDATED FOR EVERY NEW KEYWORD THAT
#: IS ADDED IMMEDIATELY BELOW THE MODULE TYPE ATTRIBUTE
spec_regex = (
- r"(?!hierarchy|core_specs|verbose|hash_length|defaults|"
+ r"(?!hierarchy|core_specs|verbose|hash_length|defaults|filter_hierarchy_specs|"
r"whitelist|blacklist|" # DEPRECATED: remove in 0.20.
r"include|exclude|" # use these more inclusive/consistent options
r"projections|naming_scheme|core_compilers|all)(^\w[\w-]*)"
@@ -127,6 +127,10 @@ module_config_properties = {
"core_compilers": array_of_strings,
"hierarchy": array_of_strings,
"core_specs": array_of_strings,
+ "filter_hierarchy_specs": {
+ "type": "object",
+ "patternProperties": {spec_regex: array_of_strings},
+ },
},
}, # Specific lmod extensions
]
diff --git a/lib/spack/spack/test/data/modules/lmod/complex_hierarchy.yaml b/lib/spack/spack/test/data/modules/lmod/complex_hierarchy.yaml
index cb39ecfa92..618163de01 100644
--- a/lib/spack/spack/test/data/modules/lmod/complex_hierarchy.yaml
+++ b/lib/spack/spack/test/data/modules/lmod/complex_hierarchy.yaml
@@ -14,6 +14,9 @@ lmod:
- blas
- mpi
+ filter_hierarchy_specs:
+ 'mpileaks@:2.1': [mpi]
+
verbose: false
all:
diff --git a/lib/spack/spack/test/modules/lmod.py b/lib/spack/spack/test/modules/lmod.py
index 3ba4953844..30e1b58905 100644
--- a/lib/spack/spack/test/modules/lmod.py
+++ b/lib/spack/spack/test/modules/lmod.py
@@ -35,6 +35,8 @@ def compiler(request):
("mpich@3.0.1", []),
("openblas@0.2.15", ("blas",)),
("openblas-with-lapack@0.2.15", ("blas", "lapack")),
+ ("mpileaks@2.3", ("mpi",)),
+ ("mpileaks@2.1", []),
]
)
def provider(request):
@@ -69,12 +71,20 @@ class TestLmod(object):
path_parts = layout.available_path_parts
service_part = spec_string.replace("@", "/")
service_part = "-".join([service_part, layout.spec.dag_hash(length=7)])
- assert service_part in path_parts
+
+ if "mpileaks" in spec_string:
+ # It's a user, not a provider, so create the provider string
+ service_part = layout.spec["mpi"].format("{name}/{version}-{hash:7}")
+ else:
+ # Only relevant for providers, not users, of virtuals
+ assert service_part in path_parts
# Check that multi-providers have repetitions in path parts
repetitions = len([x for x in path_parts if service_part == x])
if spec_string == "openblas-with-lapack@0.2.15":
assert repetitions == 2
+ elif spec_string == "mpileaks@2.1":
+ assert repetitions == 0
else:
assert repetitions == 1