Building and Using Spack Environments with mache.spack
This page documents the main public functions in mache.spack for building
and using Spack environments, as leveraged by downstream packages such as
compass,
polaris, and
e3sm-unified.
Overview
The mache.spack module provides three primary functions for Spack
environment management:
make_spack_env: Build a Spack environment for a given machine, compiler, and MPI library.get_spack_script: Generate a shell script snippet to activate a Spack environment.get_modules_env_vars_and_mpi_compilers: Query modules, environment variables, and MPI compiler wrappers for a given configuration.
These functions are typically called from bootstrap or deployment scripts in downstream packages.
Spack sources and version pins
mache builds environments with an unmodified Spack 1.x release and two
package repositories, searched in this order:
Role |
Repository |
Namespace |
|---|---|---|
E3SM packages and early updates |
|
|
upstream packages |
|
A package in e3sm shadows the upstream package of the same name. Each
mache release pins the three sources in mache/spack/pins.yaml:
spack:
git: https://github.com/spack/spack.git
tag: v1.2.2
repos:
e3sm:
git: https://github.com/E3SM-Project/e3sm-spack-packages.git
tag: v2026.06.1
builtin:
git: https://github.com/spack/spack-packages.git
tag: v2026.06.0
Each entry names exactly one of tag, commit or branch. To build against
an unreleased recipe, pass overrides in the same schema (a mapping or the path
to a YAML file) as pins= to make_spack_env, or --spack-pins <file> to
mache deploy run. Overrides merge per repository and replace that
repository’s ref:
repos:
e3sm:
branch: my-fix
The Spack checkout at spack_path holds everything: the two package
repositories under var/spack/package_repos/, the user configuration scope,
caches and bootstrap store (through spack isolate --self), the managed
environments under var/spack/environments/ and the install tree. Use a new
spack_path for each major mache release; a checkout of Spack 0.x is
refused rather than converted.
Spec syntax
Specs from spack_specs (or deploy/spack.yaml.j2) are passed to Spack
unchanged; mache no longer appends %<compiler>. The compiler is selected
by a toolchain in the machine template, so specs normally need no %. If a
spec does use %, remember that in Spack 1.x everything after it applies to
that dependency: trilinos %gcc +mpi asks for gcc+mpi, so put variants
before %.
make_spack_env
from mache.spack import make_spack_env
Purpose: Checks out the pinned Spack sources (see Spack sources and version pins) and builds a Spack environment for a specified machine, compiler, and MPI library, using a set of package specs and optional configuration.
Typical usage in downstream packages:
Called during environment setup (e.g., in compass polaris or e3sm-unified).
Used to automate the creation of a Spack environment with the correct packages and configuration for the target HPC system.
Example usage:
make_spack_env(
spack_path=spack_base,
env_name=spack_env,
spack_specs=specs,
compiler=compiler,
mpi=mpi,
machine=machine,
config_file=machine_config,
include_e3sm_lapack=include_e3sm_lapack,
e3sm_hdf5_netcdf=e3sm_hdf5_netcdf,
exclude_packages=exclude_packages,
yaml_template=yaml_template,
tmpdir=tmpdir,
spack_mirror=spack_mirror,
custom_spack=custom_spack,
pins=pins,
activation='captured',
build_jobs=build_jobs,
)
Key arguments:
spack_path: Path to the Spack clone to use.env_name: Name for the Spack environment.spack_specs: List of package specs (e.g.,["hdf5@1.12.2+mpi", "netcdf-c@4.8.1"]).compiler,mpi: Compiler and MPI library names.machine: Machine name (optional, auto-detected if not provided).config_file: Path to a machine config file (optional).include_e3sm_lapack: Whether to include E3SM-specific LAPACK packages.exclude_packages: A package name or list of package names whose machine-provided externals, modules, and related environment variables should be removed so Spack can build them instead. For example, settingexclude_packages=["cmake"]lets a downstream package build a newer CMake than the system provides. The template’s own root spec for an excluded package is removed too, but a spec for it inspack_specsis kept.e3sm_hdf5_netcdf: Deprecated compatibility flag for opting into the machine-provided HDF5/NetCDF bundle. New code should preferexclude_packages=["hdf5_netcdf"](or the individual package nameshdf5,netcdf-c,netcdf-fortran, andparallel-netcdf) instead.yaml_template: Path to a custom Jinja2 YAML template (optional).tmpdir: Temporary directory for builds (optional).spack_mirror: Path to a local Spack mirror (optional).custom_spack: Additional Spack commands to run after environment creation (optional).pins: Overrides for the pinned Spack sources, a mapping or the path to a YAML file (optional).activation:captured(default) writesactivate.shandactivate.cshinto the environment directory after the build;dynamicdoes not (seeget_spack_script).build_jobs: Number of parallel build jobs forspack install -j(optional).
Behavior:
Writes
<env_name>.yaml,build_<env_name>.bashand<env_name>.prologue.sh(the module loads and environment variables the build runs with) to the current directory.Runs the build script in a fresh login shell: it clones or updates Spack and the package repositories at their pinned refs, writes the instance’s
etc/spack/repos.yaml, isolates the instance from~/.spack, recreates the environment and installs it. Only the proxy variables (http_proxy,https_proxy,ftp_proxy,no_proxyand their uppercase forms) carry over from the calling environment, so a build on a compute node that reaches the network through a proxy works when the job script exports them.Copies
<env_name>.spack.lockand writes<env_name>.provenance.yaml(the resolved commit of each source) to the current directory.With
activation='captured', captures the environment’s activation intoactivate.shandactivate.cshin the environment directory.
Recommended pattern for downstream packages:
exclude_packages = []
if needs_newer_cmake:
exclude_packages.append("cmake")
make_spack_env(
...,
exclude_packages=exclude_packages,
)
To opt out of the machine-provided HDF5/NetCDF bundle, use either:
exclude_packages=["hdf5_netcdf"]
or the individual package names:
exclude_packages=[
"hdf5",
"netcdf-c",
"netcdf-fortran",
"parallel-netcdf",
]
get_spack_script
from mache.spack import get_spack_script
Purpose: Generates a shell script snippet to activate a Spack environment and load the required modules or environment variables.
Typical usage in downstream packages:
Used to generate activation scripts for users (e.g.,
load_compass.sh,load_polaris.sh,load_e3sm_unified.sh).Ensures that the correct modules are loaded and the Spack environment is activated in the user’s shell.
Example usage:
spack_script = get_spack_script(
spack_path=spack_base,
env_name=spack_env,
compiler=compiler,
mpi=mpi,
shell='sh', # or 'csh'
machine=machine,
include_e3sm_lapack=include_e3sm_lapack,
e3sm_hdf5_netcdf=e3sm_hdf5_netcdf,
exclude_packages=exclude_packages,
activation='captured', # or 'dynamic'
)
How the script is composed:
The returned snippet is assembled in three steps:
Optionally activate the requested environment. By default this is a single line:
source <spack_path>/var/spack/environments/<env_name>/activate.sh
activate.sh(andactivate.csh) is captured when the environment is built:macherunsspack env activateonce in a clean shell and rewrites the result so that path-like variables are prepended to, rather than replaced. Sourcing it costs milliseconds and runs no Spack. It also setsSPACK_ROOTandSPACK_ENVand puts the plainspackexecutable onPATH, sospack findandspack config getwork on the loaded environment; forspack loadorspack env activate, source$SPACK_ROOT/share/spack/setup-env.shfirst. Withactivation='dynamic'the snippet sourcessetup-env.shand runsspack env activateinstead.Auto-generate module loads and environment exports from the E3SM CIME machine configuration (
mache/cime_machine_config/config_machines.xml) for the given(machine, compiler, mpi)and target shell (shorcsh).Append any Mache template override present in
mache/spack/templates/named<machine>.<sh|csh>or<machine>_<compiler>_<mpi>.<sh|csh>.
This design keeps Mache aligned with E3SM’s authoritative machine configuration and minimizes maintenance.
exclude_packages applies here too, so get_spack_script() removes matching
machine-provided module loads and environment variables from both:
shell snippets derived from
config_machines.xml, andany package-local shell overrides in
mache/spack/templates/*.shor*.csh.
Usage in activation scripts:
# Example in a load script
{{ spack_script }}
get_modules_env_vars_and_mpi_compilers
from mache.spack import get_modules_env_vars_and_mpi_compilers
Purpose: Returns the MPI compiler wrappers and a shell snippet to load modules and set environment variables for a given machine, compiler, and MPI library.
Typical usage in downstream packages:
Used when building or installing packages that require knowledge of the correct MPI compiler wrappers (e.g.,
mpicc,mpicxx,mpifc).Used to generate build scripts for additional software (e.g., building
mpi4py,ilamb, oresmpyine3sm-unified).
Example usage:
mpicc, mpicxx, mpifc, mod_env_commands = get_modules_env_vars_and_mpi_compilers(
machine=machine,
compiler=compiler,
mpi=mpi,
shell='sh', # or 'csh'
include_e3sm_lapack=include_e3sm_lapack,
e3sm_hdf5_netcdf=e3sm_hdf5_netcdf,
exclude_packages=exclude_packages,
)
Returns:
mpicc: Name of the MPI C compiler wrapper (e.g.,mpiccorcc).mpicxx: Name of the MPI C++ compiler wrapper (e.g.,mpicxxorCC).mpifc: Name of the MPI Fortran compiler wrapper (e.g.,mpif90orftn).mod_env_commands: Shell commands to load modules and set environment variables.
As with get_spack_script(), exclude_packages can be used to remove
machine-provided package setup from the generated shell snippet.
Notes and usage in build scripts:
{{ mod_env_commands }}
# Now safe to use $mpicc, $mpicxx, $mpifc for building MPI-dependent software
This helper uses the same shell-generation logic as
get_spack_script()but does not activate a Spack environment. It therefore includes the machine-derived setup fromconfig_machines.xmlplus any matching Mache shell overrides.
Example: How Downstream Packages Use These Functions
compass: Uses
make_spack_envto build the Spack environment, then callsget_spack_scriptto generate activation scripts for users. See:compass' conda/bootstrap.pypolaris: Similar usage to
compass, with additional logic for “soft” and “libs” Spack environments. See:polaris' deploy/bootstrap.pye3sm-unified: Uses all three functions to build Spack environments, generate activation scripts, and build additional packages (e.g.,
mpi4py,ilamb,esmpy) using the correct compilers and environment. See:e3sm-unified's e3sm_supported_machines/bootstrap.py
Notes
These functions are intended for use in deployment scripts, not for interactive use.
e3sm_hdf5_netcdfandinclude_e3sm_hdf5_netcdfremain supported for backward compatibility, but new downstream code should useexclude_packagesinstead.The downstream package is responsible for determining the correct arguments (machine, compiler, MPI, etc.) and for integrating the generated scripts into their activation workflow.
For more details, see the source code and examples in the downstream packages listed above.