What “installed” means¶
A green eb line is not a claim that the software works. Three optional checks prove three different things.¶
eb succeeding writes a modulefile. That is not the same object as a working install.¶
Installed means a prefix exists and a module loads it. A successful eb is not the whole claim.¶
What you will be able to do
Name the three checks EasyBuild can run, and say what a pass of each establishes.
Say which of them a given package actually has.
Say what a green build describes, and what it does not.
A completed build prints a single line and creates one file. The line indicates that the install succeeded. The file is a modulefile and is the only component a user ever encounters. A green line and a modulefile do not mean the software runs. The bugs that remain reach a user under the developer’s name.
Existence is the default claim¶
After the install step copies files into place, EasyBuild checks the count.
The first consulted parameter is sanity_check_paths.
Two keys exist, each a list of paths relative to the install directory.
The check asserts that three paths exist.
It does not assert executability, correct version, or start capability.
A zero-byte file at bin/vasp_std would satisfy it.
The parameter only checks that those paths exist.
The word “installed” suggests a much stronger claim than the underlying check.
Running something is a stronger claim, and it is optional¶
sanity_check_commands takes shell commands and runs them:
The import resolved and the function returned.
A zero-byte file fails this check.
The code-server easyconfig from An easyconfig is Python lacks such a line.
Its install still runs code-server --help.
That command originates from the easyblock.
The easyconfig may not mention that check.
The easyblock may still run it.
A package receives an executing check when its easyblock provides
one, or when its author writes one.
Many easyconfigs have neither.
For those, “installed” means that the files are present.
A third kind of check, for a failure the first two cannot see¶
The recorded runtime library search path, the RPATH, is a third check.
It reads installed binaries.
It resolves libraries they link against.
It reports any that are missing.
It catches something the other two checks cannot detect structurally.
A file exists. --help runs. The binary still cannot start.
Those are three claims of increasing strength.
Knowing which of the three a given package satisfies reveals what its green build signifies.
Where the check ran is part of what it proved¶
A sanity check runs on the machine that performed the build. On a cluster with multiple processor architectures, the install tree is split per architecture. A binary tuned for one architecture differs from the binary for another. A green check is a statement about one of those trees. The following shows that arrangement on a real machine.
One module name, and the tree it resolves to
$ hostname
login.example
$ readlink -f /sw/arch
/sw/arch/AMD-ZEN2
$ module load 2025 QMCPACK/4.4.0-foss-2025a
$ echo $EBROOTQMCPACK
/sw/arch/RHEL9/EB_production/2025/software/QMCPACK/4.4.0-foss-2025a
$ qmcpack --version
QMCPACK version 4.4.0 built on Sep 9 2026
Recorded: SURF Snellius, 2026-09-09, 2025 module environment
Read the third and fourth commands together.
The variable $EBROOTQMCPACK expands to /sw/arch/....
The expansion contains no architecture information.
The preceding command shows /sw/arch resolving to the directory AMD-ZEN2.
The module text is architecture‑neutral.
The target path is architecture‑specific.
Loading the same module on a node with a different architecture makes the same string resolve to a different tree with a binary compiled for that architecture.
The module does not record which tree was loaded.
The omission enables the subsequent mistake.
A problem was declared fixed after testing on an interactive node.
The binary started, resolved its libraries, and ran.
The report was incorrect in two respects.
The interactive nodes used the architecture shown above.
The user’s node did not use that architecture.
The check verified a different tree.
That tree was already correct.
“It works” is incomplete without specifying where.
On a heterogeneous cluster the location is not a minor detail.
Verify on the node type that failed.
EB-Installed-1 — What has this green build established
Three packages install without error on a cluster with two processor architectures. Their easyconfigs differ:
alphadeclaressanity_check_pathsnaming three binaries, and nothing else.betadeclares the same, plussanity_check_commandsrunningbeta --version.gammadeclares the same asbeta, and its build hadskipsteps = ['sanitycheck'].
For each, say what the green build establishes. Then say what none of the three establishes.
Solution
alpha: three paths exist, on one of the two architectures. Not that
they are executable, not that they are the right version, not that they
can start. A zero-byte file would have passed.
beta: the same, plus one command ran and returned. That is genuinely
stronger, and it is still bounded by what --version touches, which is
usually argument parsing and a string.
gamma: that the files were copied. Nothing else. skipsteps removed
the step that would have decided whether the install counted, so the
green means the build reached the end rather than that anything was
verified.
None of the three establishes anything about the other architecture. The install tree is separate per architecture, and a check runs where the build ran. On a heterogeneous cluster “it works” is incomplete until it says where, and the node to verify on is the one that failed.
And how the check is done matters as much as where¶
The tool has a related trap.
The command patchelf --set-rpath writes DT_RUNPATH, not DT_RPATH.
The dynamic linker consults DT_RPATH before LD_LIBRARY_PATH, and DT_RUNPATH after it.
Patching a binary in that way and testing it in a shell that already has the library on LD_LIBRARY_PATH makes resolution go through the environment.
No report about the patch is produced.
The test can pass while only showing that the shell already contained the library.
Test in a way that the environment cannot answer for.
LD_PRELOAD handles a single library; otherwise read the resolution instead of the exit code.
A test that can pass for a reason other than the one being tested does not qualify as a test.
That matches the shape of A versionsuffix is part of the name’s stale name: an accurate observation carrying a wrong inference.
The modulefile is the deliverable¶
Pages 1 through 5 collectively produce a single text file.
Each becomes part of a modulefile: the easyconfig parameters, the easyblock derivations and the toolchain hierarchy.
The pinned dependency versions and the suffix in the name go in too.
The generated modulefile sets environment variables and declares prerequisites.
Running module load provides the variables without exposing the underlying details.
The modulefile is generated rather than manually written.
It inherits every inference presented in this book.
The expression moduleclass = 'tools' appears in the sample from An easyconfig is Python.
It is a modulefile concern that propagated to the easyconfig.
The modulefile may be entirely correct while describing an incorrect dependency tree.
The module name still carries the CUDA string that no longer matches the GPU.
A module labeled CUDA 12.9.1 declares a dependency on CUDA 12.9.1.
Both are accurate.
It resolves into a 13.0 tree.
The modulefile contains no false information.
The module remains unusable.
The three checks, side by side¶
Check |
What a pass establishes |
What it misses |
|---|---|---|
|
These paths exist |
Whether they are executable, the right version, or able to start. A zero-byte file passes. |
|
Something ran and returned |
Anything the command does not reach. |
The runtime library search path |
Everything the binaries link resolves |
Whether the program is correct, and whether the same is true on another node |
A package includes the first check. It includes the second check only if its author or its EasyBlock supplied one.
Then there are two things that are not about parameters at all.
A pass describes the tree it ran against.
On a cluster with more than one architecture it names one architecture.
Verify on the failing node type, however far away it is.
patchelf --set-rpath writes DT_RUNPATH.
The dynamic linker consults DT_RUNPATH after LD_LIBRARY_PATH.
A test that your own shell can satisfy is not a test.
The modulefile at the end of all this is the only artifact a user meets.
It is generated from every inference in this book.
It can be accurate about a tree that does not work.
What to remember
A completed build prints a line and writes a modulefile. Neither proves the software runs.
sanity_check_pathsis a count of files, not a test of behaviour.The gap between those two claims is where user-facing bugs live.