模块 (modules.yaml)¶
在高性能计算(HPC)中心,使用模块系统以受控方式管理用户环境是一种常见做法,有时个人程序员在开发机器上也会采用这种做法。为了支持这种通用做法,Spack 通过提供生成模块文件和操作命令的安装后钩子(post-install hooks),集成了 Environment Modules 和 Lmod。
模块是使用已安装软件包的多种方式之一,其他方式还包括 spack load 和 environments(环境)。
快速入门¶
Spack 可以生成 Tcl 和 Lua 两种模块文件,既可以通过安装后钩子自动生成,也可以根据用户请求通过命令生成。
如果您使用默认配置值,可以从为所有当前已安装的软件包生成 Tcl 模块文件开始:
$ spack module tcl refresh
要生成 Lua 模块文件,只需使用 spack module lmod 命令,而不是 spack module tcl。
一旦模块文件就绪,将上述生成的 Tcl 模块目录添加到您的模块路径中:
$ module use $SPACK_ROOT/share/spack/modules/$(spack arch)
现在,您可以浏览并加载可用的模块了:
$ module avail
------------------------------------------------------------ <spack-root>/share/spack/modules/linux-ubuntu24.04-skylake ------------------------------------------------------------
bison/3.8.2-gcc-13.3.0-hs4pc4c flex/2.6.3-gcc-13.3.0-jz6pfzf libevent/2.1.12-gcc-13.3.0-5k3sh64 openmpi/5.0.10-gcc-13.3.0-fjv7c7n
ca-certificates-mozilla/2026-03-19-none-none-som6bt4 gcc-runtime/13.3.0-none-none-5opaxqi libpciaccess/0.17-gcc-13.3.0-rcke4u2 openssh/10.3p1-gcc-13.3.0-bwrgshz
cmake/3.31.11-gcc-13.3.0-g2bzgpi gromacs/2026.1-gcc-13.3.0-lyxhacq libxcrypt/4.5.2-gcc-13.3.0-2snor6m openssl/3.6.1-gcc-13.3.0-fhuz67z
compiler-wrapper/1.1.0-none-none-zr6dbk4 hwloc/2.13.0-gcc-13.3.0-a4elwgb nghttp2/1.67.1-gcc-13.3.0-oofdxin pmix/6.1.0-gcc-13.3.0-gwijn2q
curl/8.20.0-gcc-13.3.0-jdcsqbb krb5/1.22.2-gcc-13.3.0-cg4l6qb numactl/2.0.19-gcc-13.3.0-p7sk7en prrte/4.1.0-gcc-13.3.0-wrgywxy
fftw/3.3.11-gcc-13.3.0-addcjv5 libedit/3.1-20251016-gcc-13.3.0-fv2f7sz openblas/0.3.33-gcc-13.3.0-ew5pzcz util-macros/1.20.2-none-none-l3eawtn
$ module load openmpi/5.0.10-gcc-13.3.0-fjv7c7n
默认的模块名称编码了软件包版本、编译器和一个短哈希值,以区分具有不同配置的构建。当然,您可以自定义命名方案以符合您站点的惯例,接下来的章节将对此进行介绍。
模块文件自定义¶
下表总结了与 Spack 可生成的不同文件格式相关的基本信息:
语言 |
默认分层模式 |
默认根目录 |
默认模板文件 |
兼容工具 |
|---|---|---|---|---|
|
非分层 |
|
|
Environment Modules, Lmod |
|
分层 |
|
|
Lmod |
扁平与分层模块文件¶
在扁平(非分层)模式下,所有模块文件都安装在单个目录中。默认设置如下:
每个模块名称都会编码软件包版本、编译器和一个用于区分不同配置构建的哈希值。
每个已安装的软件包都有一个可见的模块,且依赖项通过 自动加载 (autoloading) 可用。
命名方案可以按照 命名与投影 (Naming and projections) 章节的说明进行自定义。
在分层模式下,模块文件被组织为不同的层级:Core(核心层)、Compiler(编译器层),以及可选的其他层,例如 mpi、lapack 或 python。加载编译器模块会扩展 MODULEPATH 以显示使用该编译器构建的软件包。同样,加载 MPI 模块会显示依赖于 MPI 的软件包,依此类推。因此,用户只会看到与他们加载的环境相协调的软件包。
扁平模式是 tcl 的默认模式,而分层模式是 lmod 的默认模式,但每种生成器都支持这两种模式。在 tcl 键下设置 hierarchical: true 可启用分层 Tcl 模块,或者在 lmod 键下设置 hierarchical: false 可生成扁平 Lua 模块。请参阅 分层模块文件 以了解完整的配置选项和工具版本要求。
Spack 随附了模块文件生成的合理默认值,但您可以自定义其中的许多方面以适应软件包或站点的特定需求。通常,您可以通过以下方式覆盖或扩展默认行为:
在 Python 软件包中覆盖特定的回调 API
在
modules.yaml配置文件中编写特定规则编写您自己的模板来覆盖或扩展默认值
覆盖回调 API 让您可以表达正确使用已安装软件所需的运行时环境变更,例如将变量从语言解释器注入到其扩展中。而配置规则和自定义模板则允许您微调文件系统布局、内容以及模块文件的创建方式,以满足特定站点的惯例。
配置结构¶
控制模块生成行为的配置文件名为 modules.yaml。默认配置如下所示:
# -------------------------------------------------------------------------
# This is the default configuration for Spack's module file generation.
#
# Settings here are versioned with Spack and are intended to provide
# sensible defaults out of the box. Spack maintainers should edit this
# file to keep it current.
#
# Users can override these settings by editing the following files.
#
# Per-spack-instance settings (overrides defaults):
# $SPACK_ROOT/etc/spack/modules.yaml
#
# Per-user settings (overrides default and site settings):
# ~/.spack/modules.yaml
# -------------------------------------------------------------------------
modules:
# This maps paths in the package install prefix to environment variables
# they should be added to. For example, <prefix>/bin should be in PATH.
prefix_inspections:
./bin:
- PATH
./man:
- MANPATH
./share/man:
- MANPATH
./share/aclocal:
- ACLOCAL_PATH
./lib/pkgconfig:
- PKG_CONFIG_PATH
./lib64/pkgconfig:
- PKG_CONFIG_PATH
./share/pkgconfig:
- PKG_CONFIG_PATH
./:
- CMAKE_PREFIX_PATH
# These are configurations for the module set named "default"
default:
# Where to install modules
roots:
tcl: $spack/share/spack/modules
lmod: $spack/share/spack/lmod
# What type of modules to use ("tcl" and/or "lmod")
enable: []
tcl:
all:
autoload: direct
# Default configurations if lmod is enabled
lmod:
all:
autoload: direct
hierarchy:
- mpi
您可以定义一个或多个模块集 (module sets),每一个都可以分别配置安装位置、命名方案、包含与排除、自动加载等。
默认的模块集恰当地命名为 default。除非显式指定了其他模块集(使用 --name 标志),否则所有 操作模块的 Spack 命令 都应用于 default 模块集。
要在软件包安装期间自动生成模块文件,请将所需的模块系统添加到 enable 列表中:
modules:
default:
enable:
- tcl
- lmod
您可以分别在对应于所自定义生成器的键下,配置任一模块系统的行为:
modules:
default:
tcl:
# contains environment modules specific customizations
lmod:
# contains lmod specific customizations
通常,您可以在 modules.yaml 中使用的配置选项,要么会改变文件系统上模块文件的布局,要么会影响其内容。对于后者,可以使用 spec 模式 (spec patterns) 来微调应应用修改的软件包集合。
对于任何模块集,都可以通过更改配置的 roots 键来覆盖默认的模块根目录。例如,要将 tcl 模块安装到自定义路径:
modules:
default:
roots:
tcl: /path/to/install/tcl/modules
默认情况下,根目录中会添加一个架构特定的子目录。模块集可以通过将 arch_folder 配置值设置为 false 来覆盖该行为。
modules:
default:
roots:
tcl: /path/to/install/tcl/modules
arch_folder: false
模块布局¶
默认情况下,Spack 将软件包版本、编译器和一个短哈希值编码到每个模块名称中,以区分具有不同配置的构建。本节中的选项允许您更改模块文件在文件系统上的放置位置、命名方式、是否以分层方式排列,以及哪个版本是默认版本。
命名与投影¶
Spack 提供了三个选项,使模块名称更短、更易读。第一个是调整哈希长度的全局设置。它接受 0 到 32 的值(默认值:7):
modules:
default:
tcl:
hash_length: 7
哈希长度为 0 将完全移除哈希值,使模块名称更具可读性,但也更容易产生命名冲突。
第二个选项是 suffixes(后缀),它将字符串附加到与 spec 匹配的模块名称后面。例如,以下配置为任何使用 Python 3.12 构建的软件包添加 python3.12 后缀:
modules:
default:
tcl:
all:
suffixes:
^python@3: "python{^python.version.up_to_2}"
^openblas: "openblas"
这对于识别一组 Python 扩展与哪个版本的 Python 相关联非常有用。同样,openblas 字符串会被附加到任何 spec 中包含 openblas 的程序上,这通常是通过 +blas 变体规范实现的。
第三个也是最灵活的选项是使用 视图投影 (View Projections) 中介绍的投影格式来更改整个命名惯例:
modules:
default:
tcl:
projections:
all: "{name}/{version}-{compiler.name}-{compiler.version}-module"
^mpi: "{name}/{version}-{^mpi.name}-{^mpi.version}-{compiler.name}-{compiler.version}-module"
请注意,模块文件的默认投影因模式而异:
在分层模式下为
{name}/{version}在扁平模式下为
{name}/{version}-{compiler.name}-{compiler.version}
分层模块文件¶
三个键控制层次结构的形状:
core_compilers列出了其软件包位于Core层的编译器。core_specs列出了任何也应放入Core层的附加软件包,从而绕过常规层次结构。hierarchy列出了定义Core和Compiler之外的额外层的虚拟软件包。
以下配置生成了一个层次结构,其中 python、lapack 和 mpi 层可以独立切换:
modules:
default:
lmod:
core_compilers:
- "gcc@13"
core_specs:
- "r"
hierarchy:
- "mpi"
- "lapack"
- "python"
对于默认布局为扁平的 tcl,在同一配置中添加 hierarchical: true(这需要 Environment Modules >= 5.6 或 Lmod >= 8.7.31)。
请注意,core_specs 会绕过允许模块工具在一致的软件栈之间安全切换的层次结构,因此用户有责任维护这些软件包之间的一致性。
默认模块版本¶
要将特定版本固定为默认版本,请在模块配置中添加 defaults 键:
modules:
my-module-set:
tcl:
defaults:
- gcc@10.2.1
- hdf5@1.2.10+mpi+hl%gcc
Spec 可以根据需要进行具体指定。如果同一目录中有多个软件包匹配,则最后一个生成的胜出。
模块内容¶
当用户加载模块时,模块系统会执行模块文件,该文件设置环境变量并可选择加载依赖项。Spack 从以下三个来源填充模块文件:
从安装布局自动派生的前缀检查
应用于匹配软件包的
modules.yaml中的规则package.py中为特定软件包定义的逻辑回调
本节中的选项允许您控制哪些依赖项被自动加载,为哪些软件包设置了什么环境变量,以及最终生成哪些模块文件。
自动加载和隐藏依赖项¶
每个软件包的模块文件仅设置该软件包本身的环境变量。自动加载处理其余部分:当加载一个模块时,Spack 可以自动加载其依赖项的模块,因此用户不需要手动跟踪它们。
autoload 键控制自动加载行为,并可取以下值之一:
none:不进行自动加载run:自动加载直接的运行 (run) 类型依赖项direct:自动加载直接的链接 (link) 和运行 (run) 类型依赖项all:自动加载所有依赖项
direct 是最正确的选择,但 run 通常已足够,并且加载速度可能更快。
hide_implicits: true 将 module avail 限制为仅显示您明确安装的软件包,同时仍自动加载隐藏的依赖项。我们建议同时启用这两个选项:
modules:
default:
tcl:
hide_implicits: true
all:
autoload: direct # or 'run'
lmod:
hide_implicits: true
all:
autoload: direct # or 'run'
这可确保所有运行时变量设置正确,将可见模块限制为用户明确安装的模块,并使较短的模块名称更易于使用且不会冲突(见 命名与投影)。
模块冲突¶
Tcl 和 Lua 模块允许模块文件之间存在显式冲突。在 spec 模式下使用 conflict 键,您可以防止同时加载两个不兼容的模块:
modules:
default:
tcl:
all:
conflict:
- "{name}"
- "intel/14.0.1"
这会阻止加载同一软件包的任何其他版本(通过 {name})或 intel/14.0.1。对于 Lmod,以及 4.2 之前的 Environment Modules 版本,必须在彼此冲突的两个模块文件上都表达冲突。
全局环境修改¶
Spack 通过检查安装前缀,自动将环境变量修改添加到每个模块文件中。prefix_inspections 部分将前缀内的相对路径映射到应随其更新的环境变量。在配置中,路径是相对于安装前缀编写的,因此 ./bin 意味着 <prefix>/bin,./ 意味着 <prefix> 本身:
modules:
prefix_inspections:
./bin:
- PATH
./man:
- MANPATH
./:
- CMAKE_PREFIX_PATH
与其他模块设置不同,放置在顶级 modules 级别的 prefix_inspections 会同时应用于所有模块集。只有当安装前缀中存在相应的子目录时,才会应用检查。下表列出了默认检查:
路径 |
环境变量 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在 Linux 上,LD_LIBRARY_PATH 被省略:Spack 软件包嵌入了 RPATH,使其不再必要,且全局设置它会影响系统可执行文件。
要禁止从所有模块文件中输出某个变量,请使用 exclude_env_vars:
modules:
default:
tcl:
all:
filter:
exclude_env_vars: ["CPATH", "LIBRARY_PATH"]
选择性环境修改¶
environment 键允许您为任何匹配 spec 模式的软件包集设置、预置 (prepend) 或取消设置变量。当您想一次性对许多软件包应用更改,或者更喜欢将逻辑保留在配置中而不是 package.py 中时,这非常有用。
以下示例展示了四种主要的模式形式以及 environment 下可用的操作:
modules:
default:
tcl:
# Selects every package
all:
environment:
set:
BAR: "bar"
# Selects MPI-dependent packages. The :: clears previously matched rules
^mpi::
environment:
set:
BAR: "baz"
# Selects any zlib package
zlib:
environment:
prepend_path:
LD_LIBRARY_PATH: "{prefix}/lib"
# Selects zlib compiled with gcc@13
zlib%gcc@13:
environment:
unset:
- FOOBAR
每条规则选择一组软件包并应用相应的修改:
all:为每个模块设置BAR=bar。对于匹配
^mpi::的 spec:BAR被覆盖为baz。末尾的::清除了所有先前匹配的规则,因此all块的BAR=bar不会延续下去。zlib:将{prefix}/lib预置到LD_LIBRARY_PATH。zlib%gcc@13:取消设置FOOBAR。
all 块总是首先被评估,无论它出现在文件中的什么位置。所有其他规则均按从上到下的顺序进行评估。一般建议是,仅设置软件包实际需要的变量:与其他工具的意外交互很难调试。
特定软件包的环境修改¶
package.py 中的两种方法允许您将环境修改直接注入到模块文件中:
setup_run_environment 根据软件包自身的 spec 设置变量。
def setup_run_environment(self, env: EnvironmentModifications) -> None:
if self.spec.satisfies("+foo"):
env.set("FOO", "bar")
setup_dependent_run_environment 允许软件包在其依赖者的模块文件中设置变量。这用于解释器软件包,如 python、r 和 perl。当构建依赖者时,解释器会将依赖者的前缀预置到其搜索路径(PYTHONPATH、R_LIBS 或 PERL5LIB)中,以便解释器在运行时能够定位它。简化的 python 软件包如下所示:
def setup_dependent_run_environment(
self, env: EnvironmentModifications, dependent_spec: Spec
) -> None:
if dependent_spec.package.extends(self.spec):
env.prepend_path("PYTHONPATH", dependent_spec.prefix.lib.python)
这确保了每个 Python 扩展都在 PYTHONPATH 上拥有其库目录,而无需在每个扩展的 setup_run_environment 中重复此逻辑。
相对于环境视图的模块¶
当 Spack 环境 使用视图时,您可以配置一个模块集,以相对于视图路径而不是安装树来生成模块文件。这需要一个视图,其 投影 为每个软件包分配一个唯一的子目录。
use_view 键控制此行为:
true:使用环境的默认视图。视图名称(例如
my_view):使用该命名视图;前缀检查随后相对于视图中每个软件包的路径。false:禁用(默认)。
spack:
modules:
view_relative_modules:
use_view: my_view
prefix_inspections:
./bin:
- PATH
view:
my_view:
root: /path/to/my/view
projections:
all: "{name}-{hash}"
view 键在 配置环境视图 中有详细讨论。通过此配置,为软件包 foo 生成的模块会将 PATH 设置为包含 /path/to/my/view/foo-<hash>/bin 而不是 /spack/prefix/foo/bin。这对于大型部署非常有用,因为视图路径比安装树路径更简单、更稳定。
排除软件包¶
exclude 和 include 键控制哪些软件包生成模块文件。例如,要禁止除 gcc 和 llvm 本身之外,使用系统编译器编译的所有软件包的模块文件:
modules:
default:
tcl:
include: ["gcc", "llvm"] # include will have precedence over exclude
exclude: ["%gcc@13"] # Assuming gcc@13 is the system compiler
这会禁止除 gcc 和 llvm 之外,使用 gcc@13 编译的所有软件包的模块文件。
结合使用 exclude 和 autoload 是安全的:当依赖项没有模块文件时,autoload 只会忽略其加载语句。
维护模块文件¶
Spack 提供 spack module tcl 和 spack module lmod 命令用于管理模块文件。可用的子命令有:
$ spack module tcl --help
usage: spack module tcl [-h] [-n MODULE_SET_NAME] SUBCOMMAND ...
positional arguments:
SUBCOMMAND
refresh regenerate module files
find find module files for packages
rm remove module files
loads prompt the list of modules associated with a constraint
setdefault set the default module file for a package
options:
-h, --help show this help message and exit
-n, --name MODULE_SET_NAME
named module set to use from modules configuration
refresh 子命令重新生成模块文件。使用可选的 constraint 参数仅重新生成匹配 spec 模式的软件包。传递 --delete-tree 以在重新生成之前删除整个模块树,这对于处理彻底的布局更改非常有用。
$ spack module tcl refresh --help
usage: spack module tcl refresh [-h] [--delete-tree] [--upstream-modules] [-y] ...
positional arguments:
installed_specs constraint to select a subset of installed packages
options:
--delete-tree delete the module file tree before refresh
--upstream-modules generate modules for packages installed upstream
-h, --help show this help message and exit
-y, --yes-to-all assume "yes" is the answer to every confirmation request
rm 子命令删除模块文件。默认情况下,Spack 在删除前会请求确认;传递 -y 以在脚本中跳过提示。
$ spack module tcl rm --help
usage: spack module tcl rm [-h] [-y] ...
positional arguments:
installed_specs constraint to select a subset of installed packages
options:
-h, --help show this help message and exit
-y, --yes-to-all assume "yes" is the answer to every confirmation request
find 子命令将 spec 转换为其模块名称,这对于脚本编写非常有用:
$ spack module tcl find --help
usage: spack module tcl find [-h] [--full-path] [-r] ...
positional arguments:
installed_specs constraint to select a subset of installed packages
options:
--full-path display full path to module file
-h, --help show this help message and exit
-r, --dependencies recursively traverse spec dependencies
$ spack module tcl find openmpi
openmpi/5.0.6-gcc-13.3.0-fjv7c7n
setdefault 子命令将特定模块版本标记为默认,从而创建一个 default 符号链接。
$ spack module lmod setdefault --help
usage: spack module lmod setdefault [-h] ...
positional arguments:
installed_specs constraint to select a subset of installed packages
options:
-h, --help show this help message and exit
在 Shell 脚本中使用 Spack 模块¶
Spack 的 shell 函数和 module 命令仅在加载(source)适当的安装文件后才可用。假设 Spack 安装在 $SPACK_ROOT,请为您的 shell 运行适当的命令:
# For bash/zsh/sh
$ . $SPACK_ROOT/share/spack/setup-env.sh
# For tcsh/csh
$ source $SPACK_ROOT/share/spack/setup-env.csh
# For fish
$ . $SPACK_ROOT/share/spack/setup-env.fish
要使 Spack 的 shell 支持在每次登录时可用,请将上述适当的行添加到您的 shell 启动文件(.profile、.bashrc 或 .cshrc)中。
spack module tcl loads¶
spack module tcl loads(或 spack module lmod loads)为 spec 生成 module load 语句。这在直接调用 spack load 速度太慢的脚本中很有用。
--dependencies 标志包含所有依赖项的加载语句。由于 Spack 软件包嵌入了 RPATH,通常不需要这样做,但某些软件包(尤其是 Python 扩展)则没有。
$ spack module tcl loads --dependencies openmpi
# hwloc@2.11.2%gcc@13.3.0 arch=linux-ubuntu24.04-x86_64
module load hwloc/2.11.2-gcc-13.3.0-o7qgalw
# numactl@2.0.19%gcc@13.3.0 arch=linux-ubuntu24.04-x86_64
module load numactl/2.0.19-gcc-13.3.0-p7sk7en
# pmix@6.0.0%gcc@13.3.0 arch=linux-ubuntu24.04-x86_64
module load pmix/6.0.0-gcc-13.3.0-gwijn2q
# openmpi@5.0.6%gcc@13.3.0 arch=linux-ubuntu24.04-x86_64
module load openmpi/5.0.6-gcc-13.3.0-fjv7c7n
输出可以使用 进程替换 (process substitution) 直接进行 source:
$ source <( spack module tcl loads --dependencies <spec> )
模块前缀¶
在某些系统上,模块会自动加上前缀字符串。使用 --prefix 告诉 spack module tcl loads。例如,在模块加上架构字符串前缀的系统上,如果没有 --prefix,生成的 module load 语句将与实际模块名称不匹配:
$ module avail
linux-SuSE11-x86_64/antlr/2.7.7-gcc-5.3.0-bdpl46y
$ spack module tcl loads antlr
# antlr@2.7.7%gcc@5.3.0~csharp+cxx~java~python arch=linux-SuSE11-x86_64
module load antlr/2.7.7-gcc-5.3.0-bdpl46y
$ spack module tcl loads --prefix linux-SuSE11-x86_64/ antlr
# antlr@2.7.7%gcc@5.3.0~csharp+cxx~java~python arch=linux-SuSE11-x86_64
module load linux-SuSE11-x86_64/antlr/2.7.7-gcc-5.3.0-bdpl46y
同样的 --prefix 选项也适用于 spack module lmod loads。