Build dependencies are not dependencies¶
What you will be able to do
Say which list a given dependency belongs in.
Say what each list does to the generated module.
Recognise both directions of getting it wrong from the symptom.
Two lists have nearly identical syntax, and the distinction determines what a user receives upon loading the module.
During prepare, both lists are loaded, placing them on the path for the compiler.
When reading the easyconfig, they appear interchangeable.
The walk below treats both lists as items the robot must locate.
Swapping a name from dependencies into builddependencies still results in the walk listing it; the generated module marks where they diverge, and that segment cannot be emitted by a browser.
They are not; the modulefile shows where the difference is visible.
The module is the difference¶
The referenced easyconfig is valid, and its output is likewise valid.
Seven dependencies went in; four came out
$ sed -n '/^depends_on/p' \
> /sw/arch/.../modulefiles/chem/QMCPACK/4.4.0-foss-2025a.lua
depends_on("foss/2025a")
depends_on("Boost/1.88.0-GCC-14.2.0")
depends_on("HDF5/1.14.6-gompi-2025a")
depends_on("Python/3.13.1-GCCcore-14.2.0")
depends_on("libxml2/2.13.4-GCCcore-14.2.0")
Recorded: SURF Snellius, generated modulefile, 2026-09-10, EasyBuild 5.3.1
The toolchain and the four dependencies are mentioned.
CMake, Ninja and pkgconf are absent, and this absence is intentional.
That is the whole rule: ``dependencies`` go into the module, and ``builddependencies`` do not. This makes each entry a runtime question rather than a build question.
It is not a question of whether the build needs it.
The question is: when a user runs this software, does the software still need it? QMCPACK needs HDF5 at run time.
It does not need CMake.
Getting it wrong in each direction¶
The two mistakes fail differently, and neither error message names the list.
A runtime dependency in ``builddependencies``. The build succeeds, the sanity check may pass, and the module lacks a depends_on.
A user loads it and the binary cannot locate a shared library, or locates a different version that was already loaded.
The failure occurs at runtime for another user, resembling What “installed” means’s third check if that check is executed.
A build tool in ``dependencies``. Everything works, which is why this mistake never announces itself.
It incurs costs in two ways.
Every user of the module now loads CMake regardless of need.
That can result in a user having a CMake older than the one they installed themselves, without knowing its source.
It also constrains the module.
Anything that conflicts with that CMake now conflicts with the package without reason.
Neither issue is caught by a build.
Both are detected by asking the runtime question for each entry once.
The awkward cases¶
Three cases where the answer is not obvious.
Python, in the sample above, is in dependencies rather than builddependencies.
A package that merely uses Python to generate a header at build time would put it in builddependencies.
The same package name goes in a different list depending on what survives into the install.
A header-only library, Boost being the common example.
Nothing links against it, so a strict reading says build-time.
It is in dependencies here.
Parts of Boost are not header-only.
Being wrong in this direction costs a needless module load, where being wrong in the other direction is a runtime failure.
A code generator whose output is compiled in.
Build-time, always.
The generated code is in your binary; the generator is not.
When the list cannot be decided, the tie-break is ldd on the installed binaries and grep through the installed scripts.
If nothing in the install mentions it, it is a build dependency.
And the hierarchy still applies¶
One more constraint is taken from A toolchain is a hierarchy.
The modulefile lists the versions Boost/1.88.0-GCC-14.2.0, HDF5/1.14.6-gompi-2025a, and Python/3.13.1-GCCcore-14.2.0.
These three toolchains are members of foss-2025a’s hierarchy and none of them is foss.
The easyconfig specifies only ('Boost', '1.88.0').
The robot resolves each entry to the hierarchy member that actually provides it.
This resolution makes the dependency list short and the modulefile specific.
Both lists resolve in this manner, causing a build dependency to follow the same hierarchy rule and to fail to resolve under the same conditions.
EB-Builddeps-1 — Which list, and how to check
You are writing an easyconfig for a C++ application. It needs:
CMake, to configure.
Catch2, a header-only test framework, used only by its test suite.
HDF5, which it links against.
Python, which its build uses to generate a lookup table, and which it also installs three analysis scripts for.
pkgconf, so CMake can find things.
Put each in a list. Then say what single command on the finished install would confirm you got HDF5 right, and what would confirm Python.
Solution
builddependencies: CMake, Catch2, pkgconf. Catch2
during the build and are not installed; if the easyconfig installed the
test binary for users to run, the answer would change.
dependencies: HDF5, Python. Python is in both roles and the runtime one
wins
build is long gone.
For HDF5: ldd on the installed binaries, looking for
libhdf5.so. If the binary links it, the module must declare it.
For Python: read the installed scripts. A #! line naming an interpreter,
or an import of anything, means the install needs Python at run time.
If the only Python in the whole install was a build-time generator whose
output is a compiled-in table, it would be a build dependency.
Both checks look at the install rather than the easyconfig, which is the point: the lists are a claim about the finished thing, and the finished thing can be asked.