A versionsuffix is part of the name

What you will be able to do

  1. Say what a versionsuffix does, and what it does not.

  2. Tell a derived suffix from a hand-written one, and say why the format cannot report which is which.

  3. Say what a module named for a version does and does not guarantee about the tree under it.

NVHPC/25.11-CUDA-12.9.1

The default naming scheme builds that string. Edit versionsuffix. The name moves. The dependency list does not.

name = 'NVHPC'
version = '25.11'
versionsuffix = '-CUDA-12.9.1'
toolchain = SYSTEM

Version 25.11 of the NVIDIA HPC SDK (NVHPC) appears. A reference to CUDA 12.9.1 follows. The later part is a versionsuffix. The versionsuffix resembles a constraint but is a label.

module name railroad with optional versionsuffix on a loop

The suffix rides the optional loop on the module name.

name and version required, versionsuffix and toolchain optional

The dependency tuple. Name and version are on the rail. The rest is optional.

The suffix and the dependency are two different lines

The easyconfig behind that module states both of these.

name = 'NVHPC'
version = '25.11'
versionsuffix = '-CUDA-%(cudaver)s'

dependencies = [
 ('CUDA', '12.9.1'),
 ('nvidia-compilers', version, versionsuffix),
]

Reading the two statements together produces a mistaken interpretation of each statement’s function. The dependency binds the exact pin from A pin is for one generation, one module, with no range. The versionsuffix reports. It reports into the module’s own name. %(cudaver)s is the templating from An easyconfig is Python. It resolves from the CUDA dependency. EasyBuild derives the suffix from the dependency once, at parse time. It writes the result into a filename. No check occurs afterwards that the name still describes the tree. The third line also shows that nvidia-compilers takes versionsuffix as its own third positional attribute. That is the ('name', 'version', 'versionsuffix', 'toolchain') tuple from A pin is for one generation. A suffix travels as data from one easyconfig into another’s dependency list. Placing it in the fourth slot causes EasyBuild to treat it as a toolchain.

From below, a suffix is not a suffix

Inspect the identical toolchain used by a package.

name = 'VASP6'
version = '6.5.1'
versionsuffix = '-ACC'

toolchain = {'name': 'NVHPC', 'version': '25.11-CUDA-12.9.1'}

The toolchain’s version reads 25.11-CUDA-12.9.1, one string, suffix already folded in. That is what the consumer sees. The hierarchy in A toolchain is a hierarchy is keyed on exactly that string. So a versionsuffix is structural for whoever declares it. It is opaque to whoever depends on it. NVHPC-25.11-CUDA-12.9.1 and NVHPC-25.11-CUDA-12.8.0 are two unrelated toolchains as far as the hierarchy walk is concerned. The same relationship is true for foss-2025a and foss-2024a.

EB-Suffix-1 — Are these the same toolchain

Four module names:

NVHPC/25.11-CUDA-12.9.1
NVHPC/25.11-CUDA-12.8.0
NVHPC/25.11
VASP6/6.5.1-NVHPC-25.11-CUDA-12.9.1-ACC
  1. A package declares toolchain = {'name': 'NVHPC', 'version': '25.11-CUDA-12.9.1'}. Which of the first three satisfies it?

  2. To the hierarchy walk of A toolchain is a hierarchy, how many distinct toolchains do the first three names represent?

  3. In the fourth name, which part is derived from a dependency and which is a label somebody typed?

Solution

Only the first. The toolchain’s version is the whole string 25.11-CUDA-12.9.1, suffix folded in, and a version match is exact.

Three. The suffix is not a modifier the walk looks through; it is part of the key. 25.11-CUDA-12.9.1 and 25.11-CUDA-12.8.0 are as unrelated to each other as foss-2025a and foss-2024a, and 25.11 on its own is a third thing again.

In the fourth, -CUDA-12.9.1 belongs to the toolchain and was derived from its CUDA dependency through %(cudaver)s. -ACC is VASP6’s own versionsuffix and names a build variant: the OpenACC target rather than the CPU one. Nothing derives it, and nothing in the format marks the difference between the two.

The same syntax carries two unrelated meanings

VASP6 in that sample has a versionsuffix that says -ACC, short for OpenACC. No dependency anywhere derives it. It names a build variant. This easyconfig compiles the OpenACC target, and a sibling easyconfig compiles the CPU one from the same sources. The suffix exists so both can install side by side under one name and version. Two suffixes, one syntax, and nothing in the format distinguishes them. -CUDA-12.9.1 is derived from a pin and reports it. -ACC is a human’s label for a variant. Reading the first pattern onto the second makes the OpenACC label a dependency on a package. Reading the second onto the first makes -CUDA-12.9.1 a decoration. The convention states that a derived suffix names the thing it was derived from. The convention is also all there is.

What happens when the name stops being true

A label without enforcement can become stale. A suffix that no longer matches the CUDA the module declares is a rebuild. The 25.11 tarball is a cuda_multi build. It includes both CUDA 12.9 and CUDA 13.0 in a single archive. It is placed in versioned directories alongside unversioned entries. math_libs/include and math_libs/lib64 are links. comm_libs/nccl and comm_libs/nvshmem are links. The targets of those four links are what a compiler and a linker locate. The SDK’s installer determines the link destinations. It does not consistently follow the version resolved by EasyBuild. A single installation of that tarball on 2026-01-27 covered four architectures. On AMD-ZEN2, math_libs/lib64 points at 12.9. On AMD-ZEN4, math_libs/lib64 points at 12.9. On AMD-ZEN4-H100, math_libs/lib64 points at 13.0. On INTEL-AVX512, math_libs/lib64 points at 13.0. Two of the four architectures do not follow any predictable pattern. INTEL-AVX512 lacks a GPU. EBNVHPCCUDAVER reported 12.9 for all four. The easyblock resolved the version correctly. The installer ignored it on two of the four architectures in that batch. No aspect of this contradicts the module’s name. The name remained -CUDA-12.9.1. The dependency remained pinned to CUDA 12.9.1. Both matched the specification. On two of the four architectures the underlying tree contained CUDA 13.0. Any component linking cuSPARSE via that toolchain extracts libcusparse.so.12 from the 13.0 tree. It requires libnvJitLink.so.13. That library exists only within the 13.0 redistributable. Nothing adds it to the runtime path. The binary links. The binary installs. The binary then fails to start.

The check that found it, and the way it nearly did not

EasyBuild provides a check that examines this situation. The check inspects installed binaries against their recorded runtime library search path (RPATH). The check reports each library it cannot resolve. libnvJitLink.so.13 was reported by the check. The build specified skipsteps = ['sanitycheck'] in its easyconfig. The specification took for granted that the check produced a failure the package could not fix. Both components of that reasoning were true. The conclusion remained incorrect. The check identified a defect in the toolchain module. Disabling the module did not enable the binary to start. The responsibility for discovery shifted to the user. In a GPU package the check appears to fail on libcuda.so.1. The library resides with the driver and exists only on GPU nodes. EasyBuild exempts that library by default, together with libcuda.so, libnvidia-ml.so and libnvidia-ml.so.1. Any other reported library is a genuine finding.

The one-line version

A versionsuffix forms part of a module name. A name is not a mechanism. The dependency performs the binding. The suffix reports once at parse time. No further checks occur. The test shows a module named for one version may behave as if it carries another. Disregard the name and examine the tree. The sanity check relies on appearance. It justifies retaining it even when it reports another’s problem. Next: the definition of “installed” and the two factors that determine it.

What to remember

  • A versionsuffix is part of the module name, not a binding mechanism.

  • The dependency does the binding. The suffix reports it once at parse time.

  • The RPATH check already exempts the NVIDIA driver libraries.