The robot

Questions

  • What is a bare .eb filename, if it is not a path?

  • Where do the defaults go in --robot-paths=/my/site:?

  • Which list is the site’s, not EasyBuild’s?

What you will be able to do

  1. Say what a bare easyconfig filename is, and what resolves it.

  2. Read a --robot-paths value and say where the site defaults go in it.

  3. Name the three lists the robot consults, and say which one is not EasyBuild’s to set.

The command is this:

eb GROMACS-2025.2-foss-2025a.eb -r

One file is named and one package is requested. Dozens of modules appear, most never named at the prompt, in a working order. The -r caused this. It is short for --robot, and this chapter explains its function and, more usefully, its search locations. The sample below is a toy robot. It walks a canned universe shipped with the page, not the site. Change Python from 3.13.1 to 3.11.3 to observe a dependency being removed.

name = 'GROMACS'
version = '2025.2'
toolchain = {'name': 'foss', 'version': '2025a'}

dependencies = [
    ('Python', '3.13.1'),
    ('FFTW', '3.3.10'),
]

builddependencies = [
    ('CMake', '3.31.4'),
]

A filename is a query

Begin with something smaller than it appears. The command passed GROMACS-2025.2-foss-2025a.eb contained no path, and the working directory likely did not contain it. That works. EasyBuild resolves it against an ordered list of directories, the robot path, and resolves every discovered dependency in the same manner. No distinction in kind separates “find the requested easyconfig” from “find a dependency of the requested easyconfig”. The same query yields the same path. The relevant question is never “why can EasyBuild not find the dependency”. The relevant question is “what is on the robot path”.

The robot path, and the empty element

The option that determines the setting is --robot-paths. Its default consists of the easyconfigs supplied with the EasyBuild installation. This default enables a fresh installation to build thousands of packages without additional configuration. The next portion may appear as a typo but is intentional. The following example shows a legal and common method to set the option:

--robot-paths=/my/site/easyconfigs:

The trailing colon is intentional, and omitting it inverts the meaning. Without the colon, the default robot path is replaced by a single directory. EasyBuild then finds none of the thousands of easyconfigs it includes. With the colon, EasyBuild searches that directory first while retaining the default paths thereafter.

The mechanism is neither a special case nor a parsing quirk. The option is declared with an action named add_flex, and its documentation in easybuild/base/generaloption.py explains its behavior:

similar to add / add\_first, but replaces the first "empty" element with
the default

A colon-separated list /my/site/easyconfigs: contains two elements, the second of which is empty. add_flex inserts the default into the empty element. Thus the colon is a position rather than punctuation. Where the defaults should be placed. Placing it at the front, :/my/site/easyconfigs, causes EasyBuild to search the site directory last. When this becomes visible, the entire option ceases to be fiddly. The value represents an order, with the blank marking the default’s position within it. A related detail, which resolves ties, is that paths supplied to --robot are placed ahead of --robot-paths. The framework states this explicitly, commenting that “paths specified to --robot have preference over --robot-paths”.

EB-Robot-1 — Where do the defaults go

Three settings, one of them wrong for almost every purpose:

A.  --robot-paths=/my/site/easyconfigs
B.  --robot-paths=/my/site/easyconfigs:
C.  --robot-paths=:/my/site/easyconfigs
  1. For each, say where EasyBuild looks and in what order.

  2. Which one leaves a fresh install unable to find any of the thousands of easyconfigs it came with?

  3. You want your directory consulted only when the defaults have nothing. Which do you pick?

Solution

The value is a colon-separated list, and the empty element is where the defaults are inserted. That is the whole rule, and everything else follows from it.

A has one element and no empty one, so the defaults have nowhere to go and your directory replaces them. That is the answer to question 2, and it is the common mistake forgotten every package it ships with.

B has two elements, the second empty, so it reads: yours, then the defaults. That is the common site layout.

C also has two, the first empty: defaults, then yours. That answers question 3, and it is the right shape when your directory holds a fallback rather than an override.

Why an overlay is the normal case

A site almost always maintains its own easyconfigs directory before the defaults. The reason is A pin is for one generation’s closing point: an EasyBuild release carries a fixed set of easyconfigs. Any easyconfig merged upstream after that release is not included. when a site requires a package newer than its EasyBuild, it must have a place for that easyconfig. That location is placed first on the robot path.

That freeze creates two statements that are simultaneously true. Remembering this point is important.

  • the easyconfig exists upstream, merged, on the development branch;

  • the site cannot build it, and its EasyBuild has never heard of it.

Both statements are true. The remedy is not to dispute the first statement. Instead, place a copy where the robot looks, or direct the robot to the commit that added it.

How it decides an order

Given the path, the robot returns an executable order: nothing gets built before what it needs.

It does not compute that from a finished graph. The graph is what it is discovering. resolve_dependencies in easybuild/tools/robot.py runs a loop, and that loop’s shape explains most of what --dry-run prints.

One pass does this:

  1. Ask which easyconfigs are fully resolved, meaning every dependency they declare is already available. Those go onto the end of the order.

  2. Treat what just went onto the order as available, since by the time it is built it will be.

  3. For anything still unresolved, search the robot path for an easyconfig that provides the missing dependency, and add it to the set being resolved.

  4. Repeat.

Step 3 is the one that surprises people. The set of things being resolved grows during resolution, which is why --dry-run lists packages never named and never asked about.

Available means what the module tool says

Step 1 enables the word available, and the function states directly its source.

avail_modules = modtool.available()

The robot queries the module system for installed modules. the plan depends on MODULEPATH as much as on the robot path. These are two distinct lists configured in separate ways. A dependency that is already installed is considered satisfied and does not appear in the order. Two consequences follow, and both are useful. A build plan is not portable. Executing the same command on a machine with a different set of installed modules yields a different, yet equally correct, plan. And --dry-run on a well‑stocked cluster is short for a reason. It does not report that the package has few dependencies. It reports that most dependencies are already present. EasyBuild 5 dropped the implication that -D includes --robot. eb pkg.eb -D dry‑runs only the named file. The resolved plan is eb pkg.eb -Dr. A wrapper is the range EasyBuild will not write is the page for that change. The recordings in this chapter already pass both letters. -D / --dry-run already retains every dependency and marks each one [x] or [ ], once -r is also on the command line. There is no eb --retain-all-deps; that name is an internal argument used by -D, --dep-graph, and --check-conflicts. -M / --missing-modules prints only the absent set.

What is being rebuilt does not satisfy anything

A detail in the loop is easy to miss and can save an afternoon.

being_installed = [p['full_mod_name'] for p in easyconfigs]
avail_modules = [m for m in avail_modules if m not in being_installed]

Anything in this run’s own list is removed from the available set, even when it is installed. Requesting a rebuild of a library and a dependent library causes both to be rebuilt, in that order. The old library does not satisfy the dependent. A matching escape hatch exists. A dependency marked as an external module has no easyconfig, so the robot does not search for one. The module must still be available. If it is not, the resolve loop ends with Missing modules for dependencies marked as external modules. This is how a site informs the robot about a module EasyBuild did not build, not how it pretends the module exists.

The order is not unique

Any topological ordering of the discovered graph is valid, and EasyBuild does not promise a particular one. The list generated by --dry-run is an answer rather than the definitive answer, so diffing two such lists across versions shows churn that lacks meaning. A toolchain is a hierarchy’s sibling toolchains convey the same fact one level lower.

And the hierarchy filters the candidates

The hierarchy from A toolchain is a hierarchy determines which items step 3 may locate. A candidate is considered valid only when its toolchain belongs to the package’s hierarchy.

That filter represents the intersection of the two chapters and generates the most frustrating class of robot failure. The module is installed. module avail lists it. The robot reports that it cannot find it.

The robot is not lying. It reports that nothing in the package’s hierarchy provides that dependency. A module built with a toolchain outside that hierarchy is invisible rather than rejected.

EB-Robot-2 — The same command, two different plans

You run the same command with -D on two machines, from the same easyconfig and the same robot path:

eb GROMACS-2025.2-foss-2025a.eb -r -D

On the cluster it lists three things to build. On your laptop it lists ninety.

  1. What differs, given that the robot path is the same?

  2. Which flag makes the cluster print the ninety as well, and when would you want that?

Solution

MODULEPATH. The robot asks the module system what is installed, through modtool.available(), and treats anything already there as satisfied. The cluster has eighty-seven of those dependencies installed and your laptop has none.

So a build plan is a property of the machine as much as of the easyconfig, and it does not travel between them. The robot path decides what can be found; MODULEPATH decides what counts as already built.

-D already retains every dependency and marks each one. There is no eb --retain-all-deps. Use -D when the question is the full closure, and -M when the question is what is absent on this machine.

Ask before the build

The robot announces its decision without performing any of it.

eb GROMACS-2025.2-foss-2025a.eb -r -D

The option -D corresponds to --dry-run. The resolved list returns with a marker beside each entry indicating if it is already installed or would be built. It should be read before each non‑trivial build for a specific reason. The command as typed hides the difference between “this will install two things” and “this will install ninety things”. That difference is comparable to that between a coffee and an afternoon. It also provides the fastest way to detect a dependency resolving to an unexpected version. This occurs most often when a site overlay shadows an upstream easyconfig with the same name.

Three lists, and a loop

The robot consults three lists, and confusing them causes most errors.

List

Set by

Decides

the robot path

--robot-paths, empty element for the defaults

which easyconfigs can be found

MODULEPATH

your module system, not EasyBuild

what counts as already built

the toolchain hierarchy

A toolchain is a hierarchy, computed

which found candidates are eligible

It iterates with a loop instead of performing a graph traversal.

It takes each fully resolved item, marks it as available, searches the path for missing items, and repeats.

The resolved set expands during execution, which explains why --dry-run lists packages that were never requested.

Two consequences deserve attention.

A plan lacks portability.

Items present in the current run’s list cease to count as available, causing both a library and its dependents to be rebuilt.

Consult -D prior to any non‑trivial build.

This option uniquely differentiates one installation from ninety.

The following chapter covers versionsuffix, which appears as a constraint and is a label.