Standing where the build stood¶
What you will be able to do
Find the four artifacts EasyBuild already left inside every installation, and say what each one reproduces.
Load a build’s own environment from its devel module, and say what that module contains beyond the environment.
Choose between a devel module, a dumped script, a
BuildEnvmodule and a container, on stated grounds.
What a build is allowed to see measured the awkward fact that loading foss/2025a sets EBROOTFOSS but does not set $CC.
The build environment is constructed inside EasyBuild during prepare and is expected to evaporate when the run ends.
However, it does not evaporate, and this chapter primarily discusses where it went.
Look inside the installation first¶
Each successful installation includes a subdirectory that is rarely opened.
What ends up in <installdir>/easybuild/
$ find ebtest/software/wobble/1.0 -type f | sed 's#.*/1.0/#1.0/#'
1.0/wobble
1.0/easybuild/easybuild-wobble-1.0-20260910.072006_test_report.md
1.0/easybuild/wobble-1.0.eb
1.0/easybuild/easybuild-wobble-1.0-20260910.072006.log
1.0/easybuild/wobble-1.0-easybuild-devel
1.0/easybuild/reprod/wobble-1.0.env
1.0/easybuild/reprod/wobble-1.0.eb
1.0/easybuild/reprod/easyblocks/tarball.py
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10; a Tarball install of a one-file test package
One binary, and seven files about how it got there. Taking them in the order they become useful:
wobble-1.0.eb is the easyconfig as used, archived beside the software.
So the recipe for anything installed is on the machine, whatever happened to the repository it came from.
easybuild-...log is the full build log, the one Reading a failed build teaches to read, kept.
..._test_report.md is a test report, written whether or not one was asked for one, which is Testing, and the four things it can mean’s claim confirmed on disk.
reprod/ is the interesting one, and it is three separate answers to “how would I do this again”:
reprod/wobble-1.0.eb, the easyconfig, dumped from the parsed configuration rather than copied;reprod/wobble-1.0.env, a shell script that recreates the build environment;reprod/easyblocks/tarball.py, the source of the easyblock that ran.
That last file is the one nothing else gives.
An easyconfig plus a toolchain does not determine a build.
Archiving it turns the installation into a self-describing artifact: recipe, environment, and the program that interpreted them.
The framework does this before the steps run, in main.py:
reproduce_build creates the directory, dump_env_easyblock writes the environment script into it, and copy_easyblocks_for_reprod adds the easyblocks, including the ones used by components of a bundle and by extensions.
The devel module¶
What is in it
Every install carries a devel module whose purpose is to reproduce the build’s environment. Before reading the next block: name three variables expected in it, and one that would be a problem to find.
What happens
$CC, $CXX and the toolchain’s flags are there, which is the point.
The one to be unhappy about is $PATH, and it is there in full. On the run in the next transcript that also captured two
variables named _ModuleTable001_ and _ModuleTable002_, which are Lmod’s
own base64 bookkeeping.
So a devel module is a faithful record rather than a curated one, and loading it into a working shell is a decision rather than a habit.
The fourth artifact is a modulefile. It directly answers What a build is allowed to see’s problem.
The generated devel module, first lines
$ head -3 ebtest/software/wobble/1.0/easybuild/wobble-1.0-easybuild-devel
setenv("CC", "gcc")
setenv("CXX", "g++")
setenv("PATH", "/scratch/ebvenv/bin:$HOME/.local/bin:...<the whole of it>")
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10; system toolchain, so the compiler variables are the host’s
The docstring in easybuild/framework/easyblock.py describes both its usage and its limitation.
Create a develop module file which sets environment based on the build.
Usage: module load name, which loads the module you want to use.
$EBDEVELNAME should then be the full path to the devel module file. So now
you can module load $EBDEVELNAME.
WARNING: you cannot unload using $EBDEVELNAME (for now: use module unload
\`basename $EBDEVELNAME\`)
So the loop is two commands:
module load QMCPACK/4.4.0-foss-2025a
module load $EBDEVELQMCPACK
After the second step, the shell contains the compilers, the flags, and the $LIB* link lines that What a toolchain injects tabulates for that package’s build on this machine.
Two aspects are documented by the code but not mentioned in the documentation.
It records every environment change, not a curated set. The implementation iterates env.get_changes() and emits a setenv for each non‑empty value.
In that run, it included $PATH in full and two variables named _ModuleTable001_ and _ModuleTable002_, which are Lmod’s own base64 bookkeeping.
No filtering is applied to them.
a devel module provides a faithful record rather than a tidy one, and a setenv of $PATH should be examined before loading it into a working environment.
It loads the devel modules of its dependencies. The load lines are generated by scanning the environment for other $EBDEVEL* variables and emitting a load for each, so loading one devel module imports the build environments of its dependencies.
When it is needed before the build¶
The command eb --dump-env-script <easyconfig> writes <basename>.env into the current directory.
The file contains module load lines for the toolchain and its dependencies.
It also contains export lines for the build environment.
No build actions are performed.
Existing files are not overwritten.
A dumped environment for a system-toolchain recipe, and the refusal to overwrite it
$ eb wobble-1.0.eb --dump-env-script
Script to set up build environment for wobble-1.0.eb dumped to
wobble-1.0.env
$ cat wobble-1.0.env
#!/bin/bash
# script to set up build environment as defined by EasyBuild v5.3.1 for
# wobble-1.0.eb
# usage: source wobble-1.0.env
# toolchain & dependency modules
# (no modules loaded)
# build environment
# (no build environment defined)
$ eb wobble-1.0.eb --dump-env-script
ERROR: Script(s) already exists, not overwriting them (unless --force is
used): wobble-1.0.env
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10
Both sections are empty there, which is the correct answer rather than a failure.
wobble uses the system toolchain, which loads no modules and sets no compiler variables.
When run on a foss easyconfig, both sections fill up.
The two comments indicate that the tool declines to write an empty file and calls it an environment.
EB-DevEnv-2 — Archaeology on one install
You have shell access to a machine with an EasyBuild stack and one module
name: Foo/2.1-foss-2023a. Nobody who built it is reachable.
Using only what is on that machine, say how you would answer each of these, and which artifact answers it:
Which EasyBuild version built it?
Which patches were applied, and what did they change?
Which compiler flags did the build get?
Would rebuilding it today produce the same thing?
Solution
The build log in
<installdir>/easybuild/, which records the framework version in its header. The easyconfig does not carry it.The archived easyconfig names the patches, and
reprod/carries the easyconfig as parsed. The patch files are not necessarily there, which is a real gap: if they came from the site’s repository and that repository moved, the names are all you have.reprod/<ec>.env, which is the environment script, or the devel module, which records what the build’s environment actually became. Both are on disk. Loading the toolchain module would not tell you, for What a build is allowed to see’s reason.Probably not, and the artifacts say why rather than hiding it.
reprod/easyblocks/pins the code that ran, so that much is settled. What is not pinned: the framework version (in the log, and installing that exact version is on you), the sources (upstream, and possibly moved), the dependency installations (modules that may have been removed), and the operating system. An honest answer to a user asking for a rebuild names those four.
When somebody else needs it¶
A dumped script belongs to one person.
In a team setting, the mechanism is a module, and EasyBuild provides the generic easyblock BuildEnv, whose docstring reads “EasyBuild support for creating a module that loads the build environment flags for the current toolchain”.
No software is installed.
The output is a modulefile that sets $CC, $CXX, $FC, $CFLAGS and other variables for the named toolchain. The compiler environment consistently.
The RPATH wrapper directory is copied when the site builds with --rpath, causing manually compiled binaries to be linked like the stack’s.
For machines where a modules tool cannot be installed, EasyBuild generates container recipes via --container-* options.
An image is built with --container-build-image when privileges exist, and both are stored under --containerpath.
Doing this on top of EESSI¶
When the stack originates from /cvmfs instead of a personal build host, the issue is addressed by an EESSI‑specific module EESSI-extend.
It is loaded by setting EESSI_SITE_INSTALL or EESSI_USER_INSTALL, and EasyBuild is configured to install into that location atop the mounted stack, which is the subject of EESSI, somebody else’s stack.
EESSI_EASYBUILD_HOOKS_OVERRIDE replaces the hooks it uses, thereby replacing the hooks that give an EESSI build its EESSI shape; overriding them enables obtaining a non‑EESSI build.
EB-DevEnv-1 — Four people who need an environment
Pick a mechanism from Five ways to get a build environment for each, and say what makes the others worse.
A user asks how a module on your site was built, three years after the person who built it left.
You are debugging why one package’s
cmakepicks the wrong MPI, and you want to runcmakeby hand twenty times.A PhD student needs to compile their own code against the toolchain the site’s VASP was built with, for the next six months.
Somebody needs to rebuild a 2023 installation exactly, on a new machine, and has the module tree but not the repository.
Solution
Nothing new:
<installdir>/easybuild/already has the easyconfig, the log andreprod/. The log names the framework version, the easyconfig names the toolchain and the dependencies, andreprod/easyblocks/holds the code that ran. This is the answer to most software archaeology on an EasyBuild site, and it needs no tooling.eb --dump-env-script, sourced once. The devel module would also work if the package is installed, and is one module load rather than a file. Either beats aBuildEnvmodule, which is more machinery than a twenty-minute investigation needs.BuildEnv, named with the toolchain in theversionsuffix. Six months and a second person means the environment should be discoverable and maintained. A dumped script in your home directory is neither, and a devel module is tied to one package’s build rather than to the toolchain.reprod/, and then a careful reading of what What reprod/ contains, and what is missing from it says is missing from it. You have the recipe, the environment script and the easyblock source. You do not have the framework version except in the log, the source archives except upstream, or the operating system. On a new machine the last one is the hard part, which is the argument for building on top of a compatibility layer rather than on top of a distribution.
What to remember
Every install already carries the easyconfig, the log, a test report, a devel module and a
reprod/directory holding the easyblock source.module load $EBDEVEL<NAME>reproduces the environment that build had, including$PATHand the modules tool’s own bookkeeping.eb --dump-env-scriptwrites the toolchain’s environment for a recipe before any build, and does not overwrite.BuildEnvturns a toolchain’s environment into a module other people can load, and copies in the RPATH wrappers.What
reprod/does not carry: the framework version, the sources, the dependency installations, and the operating system.