Spack#

Learning Objectives#

By the end of this section, learners will be able to:

  • Explain what Spack is and why it is widely used in high-performance computing (HPC) environments.

  • Install and configure Spack, including setting up the environment for persistent use across shell sessions.

  • Create, activate, and manage Spack environments to isolate project-specific dependencies.

  • Use Spack to list available packages, search for software, and add packages to an environment.

  • Interpret and analyze spack spec output to understand software versions, compilers, variants, and dependency trees.

  • Modify specifications to request specific package versions, variants, or compiler configurations.

  • Install, load, and unload packages using Spack, and verify successful installations.

  • Compare different compiler or library builds by leveraging Spack’s ability to manage multiple versions and variants.

  • Apply Spack commands effectively, including spack list, spack info, spack add, spack remove, spack spec, spack install, spack find, spack load, and spack unload.

  • Recognize and address common pitfalls in using Spack, such as dependency conflicts or environment misconfigurations.

  • Summarize the advantages of Spack in managing complex dependencies, multiple builds, and optimised software stacks for GPU/HPC workloads.

Overview#

Spack is a flexible package manager tailored for high-performance computing (HPC). It helps build, install, and manage multiple versions of software and their dependencies, which is especially useful when working with GPU computing, where specific compiler versions and libraries might be needed. For example, it could be the case that you want to use the Intel oneAPI for the best performance of Intel-optimised code or to build your code with GCC to have a fully open-source toolchain, alongside a number of other reasons, such as particular compilers have preferential performance on a given architecture or perform more stably.

Getting Started with Spack#

Even when running GPU code with an interpreted language like Python, you may need compiled libraries under the hood (e.g., CUDA, numerical libraries). This is where a tool like Spack is highly recommended to manage those dependencies.

Install Spack: Clone the Spack repository from GitHub:

git clone -c feature.manyFiles=true --branch v0.23.1 --depth=2 https://github.com/spack/spack.git

Source Spack Setup: Add Spack to your shell by sourcing the setup script:

source spack/share/spack/setup-env.sh

then you can let spack find pre-installed programs with

spack external find
spack compiler add

You will need to source the setup-env file for each terminal session you open. If you want this to be done automatically for you, then you can:

Bash (e.g. Ubuntu, WSL) Open your ~/.bashrc file At the bottom of the file, add:

# — Load Spack on shell startup
source /path/to/spack/share/spack/setup-env.sh

Zsh (e.g. macOS): Open your ~/.zshrc file At the bottom of the file, add the following:

# — Load Spack on shell startup
source /path/to/spack/share/spack/setup-env.sh

Note

Replace /path/to/spack with the actual directory where you cloned or installed Spack. If you ever move or upgrade Spack, just update that one line in your startup file.

Now every time you open the terminal, you should have Spack loaded and ready to use that you can verify with:

spack --version

Create and Activate an Environment: It’s good practice to use a Spack environment to isolate your project’s packages. For example, for this course, you might want to do the following:

spack env create my_spack_env
spack env activate -p my_spack_env

This makes packages from this environment visible in your shell, with the -p modifying the prompt with the name of the environment.

Note

Spack environments allow you to experiment without affecting your system or other projects. If something goes wrong, you can deactivate or delete the environment and start fresh, which is very useful in HPC contexts where different projects might require different dependencies.

Why Use Spack?#

Spack’s core purpose is to simplify handling complex dependencies and multiple versions/variants of software in HPC:

  • Complex Dependencies: Spack can manage intricate dependency trees (including multiple versions of the same library or specific compiler requirements). For example, one compiler might produce faster GPU code than another. Spack makes it easy to create a separate environment for each compiler to compare.

  • Multiple Versions and Variants: If you need different builds of the same software, then Spack can install different compilers such as GCC 9 and GCC 11 side by side or build a library with different features (variants) like +mpi or different precision modes. This fine-grained control is crucial in HPC, where you might tune the build for performance.

These features are the main draw of Spack. You can even create your own package receipt, apply patches, and maintain custom package repositories. Those topics are beyond this course, but it is good to understand that these options are available if you need to implement them down the road.

Guided Tour of Spack Commands#

Let’s go through common Spack commands and concepts using our my_spack_env environment as context:

Listing Available Packages#

To see what software is available through Spack (it has thousands of packages, including Python libraries, scientific libraries, compilers, etc.), use:

spack list

This will display a list of package names. For instance, many Python libraries are prefixed with py- (e.g. pu-numpy for NumPy). You can search within this list or use spack search <name> for a keyword search.

Adding Packages to an Environment#

To add a package to your current Spack environment (without installing yet), use spack add. For example, to add NumPy:

spack add py-numpy 

At this point, you’re just declaring you want NumPy in the environment. You haven’t installed anything yet. You can add multiple packages (e.g. Spack add py-scipy py-cupy to add SciPy and CuPy as well). Think of spack add as saying *”I plan to use these packages.”.

Viewing the Environment Spec#

After adding packages, you can see what Spack plans to install by checking the specification:

spack spec 

This command outputs a detailed tree of all packages and dependencies that would be installed for the current environment configuration. **Importantly, this shows the plan before installation. The output includes each package’s name, version, compiler, variants, etc., and whether it’s already installed or not.

Some symbols you might see in the spack spec output:

  • - (dash): The package is not yet installed; Spack will build it.

  • + (plus): The package has already been installed and will be reused.

  • [e] (external): Package is found externally on the system (e.g. system MPI or CUDA library); Spack will use that rather than build a new one. Each line also shows a spec string for the package. A spec string has the format:

<package-name>@<version>%<compiler>@<compiler-version> build_system=<build-system> arch=<platform>-<os>-<target> [variants]

Such as:

py-numpy@1.21.2%gcc@11.4.1 build_system=python_pip arch=linux-ubuntu20.04-zen2

Including the components of:

  • Package name: py-numpy

  • Version: 1.21.2

  • Compiler: gcc

  • Compiler version: 11.4.1

  • Build system: python_pip, meaning it uses pip/wheel to install.

  • Architecture: platform linux, OS ubuntu20.04, target CPU architecture zen2 (AMD Zen2 in this case).

Variants and additional options (if any) would appear as +feature or ~feature flags and other key=value settings in the spec. For instance, a spec might include +cuda if a package can be built with CUDA support.

Understanding the spec output helps you verify that Spack picked the right versions and options. If not, you can adjust by specifying versions or variants in the spack add command (for example, spack add py-numpy@1.19.5 to request a specific older NumPy).

Dependencies: In the spec output, you’ll see dependencies indented under main packages, often prefixed by ^. For example, NumPy may depend on BLAS libraries or a specific Python version. Spack will list those. A snippet might look like:

 -   py-numpy@1.21.2%gcc@11.4.1 ...
[+]      ^python@3.10.4%gcc@11.4.1 ...
 -         ^openblas@0.3.18%gcc@11.4.1 +fortran +pic ...

Here, NumPy depends on Python (already installed, as indicated by +) and OpenBLAS (not installed yet, indicated by - and will be built with Fortran support +fortran). This hierarchical view is a key feature of Spack.

Modifying the Spec#

Until you actually install it, you can modify your environment specs. For instance, if you realise you need a different version of a package, you can add it with a version qualifier and remove the old one:

Suppose we added py-numpy (which defaulted to the latest 2.x), but we need NumPy 1.x for compatibility; we can do the:

spack add py-numpy@1.21.0

Spack will now plan to use NumPy 1.21.0. Running spack spec again will show the updated plan. You could remove the undesired spec with spack remove py-numpy@2.1.2 if it was explicitly listed, but if you add a new version, Spack will typically reconcile the spec to use the one you last added.

Installing Packages#

Once you are satisfied with the planned specs, install everything in the environment with the following:

spack install 

This will download, build, and install all packages in the spec (including dependencies). This may take some time for big packages or if a compiler needs to be built.

After installation, the packages are now present in Spack’s directory (spack/opt/...) and linked to your environment.

Using Installed Packages#

If you activated the environment, Spack auto-generates a view of the installed packages. This means you can immediately use the software. For example, after installing py-numpy, you should be able to run python and import numpy because the environment’s Python points to one with NumPy installed, or your PYTHONPATH is set appropriately.

In cases where you did not activate the environment or if you want fine control, you can manually load packages into your shell session. For example:

spack load py-numpy 

This modifies environment variables (like PATH and LD_LIBRARY_PATH) so that the loaded package is accessible. If you installed multiple versions, you can specify which one to load (space will tell you if multiple matches are found). You can also unload packages with spack unload.

To see all packages installed in your current environment (or globally), use:

spack find

This lists installed packages and versions, which can be hopeful to verify installation.

Deactivate the Environment#

When you are done or want to switch context, deactivate the Spack environment with:

spack env deactivate 

This will restore your shell to not knowing about the environment’s packages. You can later reactivate it with spack env activate -p my_spack_env when needed.

Spack Quick Reference#

  • spack list <query> – List available packages matching (or all packages if no query).

  • spack info <package> – Show details about a package (versions, variants, dependencies).

  • spack add <package> – Add a package to the current environment (does not install).

  • spack remove <package> – Remove a package from the current environment spec.

  • spack spec – Show the full dependency graph (spec) for the environment and which parts are installed.

  • spack install – Install the environment’s spec (download and build everything needed).

  • spack find – List installed packages (in current env or globally).

  • spack load <package> – Load an installed package to use in the shell (especially if not using -p activation).

  • spack unload <package> – Unload a package from the environment variables.

  • spack env activate <env> – Activate a named environment (use -p to modify the prompt).

  • spack env deactivate – Deactivate the current environment.

Note

If Spack ever behaves oddly (perhaps due to old cache or config issues), a last resort is to remove ~/.spack (Spack’s config cache in your home) to reset Spack’s state. This is rarely needed, but can resolve mysterious issues by giving Spack a “clean slate”.

Summary#

  • Spack manages multiple versions and configurations of software seamlessly, which is crucial in GPU/HPC setups.

  • Use environments to isolate projects. Add desired packages, then install them.

  • spack spec helps you review what will be installed before building anything – use it to verify versions and dependencies.

  • Installing via spack install in an environment builds all needed software. After that, you can use the software (especially with -p activated environments or by loading packages).

  • Spack’s power is in handling complex dependency scenarios that other package managers struggle with, making it ideal for research and GPU computing setups.

Exercise#

Below is the output of a spack spec command on a HPC cluster. The quiz below contains some questions based on the output to help you practice reading spack command outputs.

[+]  cuda@12.8.0~allow-unsupported-compilers~dev build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^libxml2@2.13.5~http+pic~python+shared build_system=autotools arch=linux-ubuntu22.04-zen2
[e]          ^gcc@12.3.0~binutils+bootstrap~graphite~mold~nvptx~piclibs~profiled~strip build_system=autotools build_type=RelWithDebInfo languages:='c,c++,fortran' arch=linux-ubuntu22.04-zen2
[+]          ^gcc-runtime@12.3.0 build_system=generic arch=linux-ubuntu22.04-zen2
[+]          ^libiconv@1.17 build_system=autotools libs:=shared,static arch=linux-ubuntu22.04-zen2
[+]  python@3.12.9+bz2+crypt+ctypes+dbm~debug+libxml2+lzma~optimizations+pic+pyexpat+pythoncmd+readline+shared+sqlite3+ssl~tkinter+uuid+zlib build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^bzip2@1.0.8~debug~pic+shared build_system=generic arch=linux-ubuntu22.04-zen2
[+]          ^diffutils@3.10 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^compiler-wrapper@1.0 build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^expat@2.7.1+libbsd build_system=autotools arch=linux-ubuntu22.04-zen2
[+]          ^libbsd@0.12.2 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]              ^libmd@1.1.0 build_system=autotools arch=linux-ubuntu22.04-zen2
[e]      ^gcc@14.2.0~binutils+bootstrap~graphite~mold~nvptx~piclibs~profiled~strip build_system=autotools build_type=RelWithDebInfo languages:='c,c++,fortran' arch=linux-ubuntu22.04-zen2
[+]      ^gcc-runtime@14.2.0 build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^gdbm@1.23 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^gettext@0.23.1+bzip2+curses+git~libunistring+libxml2+pic+shared+tar+xz build_system=autotools arch=linux-ubuntu22.04-zen2
[+]          ^tar@1.35 build_system=autotools zip=pigz arch=linux-ubuntu22.04-zen2
[+]              ^pigz@2.8 build_system=makefile arch=linux-ubuntu22.04-zen2
[+]              ^zstd@1.5.7+programs build_system=makefile compression:=none libs:=shared,static arch=linux-ubuntu22.04-zen2
[e]      ^glibc@2.35 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^gmake@4.4.1~guile build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^libffi@3.4.7 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^libxcrypt@4.4.38~obsolete_api build_system=autotools arch=linux-ubuntu22.04-zen2
[+]          ^perl@5.40.0+cpanm+opcode+open+shared+threads build_system=generic arch=linux-ubuntu22.04-zen2
[+]              ^berkeley-db@18.1.40+cxx~docs+stl build_system=autotools patches:=26090f4,b231fcc arch=linux-ubuntu22.04-zen2
[+]      ^ncurses@6.5~symlinks+termlib abi=none build_system=autotools patches:=7a351bc arch=linux-ubuntu22.04-zen2
[+]      ^openssl@3.4.1~docs+shared build_system=generic certs=mozilla arch=linux-ubuntu22.04-zen2
[+]          ^ca-certificates-mozilla@2025-02-25 build_system=generic arch=linux-ubuntu22.04-zen2
[+]      ^pkgconf@2.3.0 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^readline@8.2 build_system=autotools patches:=1ea4349,24f587b,3d9885e,5911a5b,622ba38,6c8adf8,758e2ec,79572ee,a177edc,bbf97f1,c7b45ff,e0013d9,e065038 arch=linux-ubuntu22.04-zen2
[+]      ^sqlite@3.46.0+column_metadata+dynamic_extensions+fts~functions+rtree build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^util-linux-uuid@2.41 build_system=autotools arch=linux-ubuntu22.04-zen2
[+]      ^xz@5.6.3~pic build_system=autotools libs:=shared,static arch=linux-ubuntu22.04-zen2
[+]      ^zlib-ng@2.2.4+compat+new_strategies+opt+pic+shared build_system=autotools arch=linux-ubuntu22.04-zen2

Quiz#