An easyconfig is Python¶
The file is Python. EasyBuild evaluates it. The name becomes an easyblock class.¶
An easyconfig is a Python file EasyBuild evaluates.
Questions
Why is one name quoted and the other not?
When is
%(version)ssubstituted: while the file runs, or after?What is an easyconfig without an
easyblockline claiming?
What you will be able to do
Say why
SYSTEMcarries no quotes and'tools'does.Predict the easyblock class EasyBuild will look for, from a software name alone, and read one backwards to the name.
Say what an easyconfig with no
easyblockline is claiming.Read an EasyBuild error for the assumption behind it rather than the state it reports.
An easyconfig is a Python file EasyBuild evaluates. This one is a VS Code installation that runs in a browser on a supercomputer. The file contains twenty lines. Recipes of this form are merged weekly. The installed module name follows a fixed grammar, drawn as a railroad diagram.
Module name grammar. The versionsuffix is optional. Nothing on this rail is a range.¶
SYSTEM is the empty toolchain, already in the namespace. Templates resolve after the file runs. A missing easyblock line still names a class.¶
The file reads like YAML: names on the left, values on the right, no functions, no if.
Line seven breaks that reading.
SYSTEM has no quotes.
moduleclass has quotes.
That does not follow house style.
EasyBuild executed this file as Python.
SYSTEM was already a variable in the evaluation namespace.
Visibility of SYSTEM makes much of the rest of EasyBuild stop being folklore.
The templating uses Python’s own %.
The missing easyblock line claims a class exists.
The quoting is not inconsistent.
One of those names was supplied.
The other was typed.
Why that distinction earns its place in this page¶
EasyBuild executes an easyconfig in a prepared namespace.
The namespace carries the set of names the file is allowed to use.
The token SYSTEM on line seven is not a keyword.
The token SYSTEM on line seven is not a string.
EasyBuild puts the value {'name': 'system', 'version': 'system'} in the namespace before evaluating the file.
The value lacks quotes while moduleclass includes them.
One is a variable.
The other is a literal.
Running the sample at the top returns a model.
toolchain comes out as a structure with a name and a version.
The text SYSTEM is gone by then.
The last two lines of that output are the toolchain name and the derived easyblock class.
The file does not name an easyblock.
The model reports the EasyBuild name that will be derived anyway.
That forms the rest of this page.
The same fact explains a later common confusion.
The file is written in Python.
It can perform computation.
A list comprehension in an easyconfig is legal.
A loop that builds exts_list from a table is legal.
Almost no easyconfig does either.
The convention against it is advisable.
It is a convention.
Nothing in the format forbids computation.
The things in percent-brackets are not typed in percent-brackets¶
Look at sources:
%(version)s is old‑style Python string formatting applied after the file runs.
EasyBuild assembles a dictionary from the easyconfig.
The file stores the literal.
%(version)s works even when version is set below sources.
EasyBuild adds that second pass.
EasyBuild also adds names such as SOURCE_TAR_GZ that are in the eval namespace.
Run the sample.
Observe %(version)s resolve.
Change the version.
Observe it move.
Every value derived from it moves with it.
That is the whole of templating.
The one that does not resolve is %(mapped_arch)s.
Nothing in the file sets mapped_arch.
On a cluster it resolves anyway.
It resolves to amd64 on x86-64.
It resolves.
The easyblock provides that behavior.
The remainder of this page focuses on the easyblock.
EasyBuild derives the easyblock from the name, and the derivation is a string transformation¶
The easyconfig above lacks installation instructions.
It contains no easyblock line, no configure flags, no make invocation, and no install prefix.
It lists the download sources and their checksums, then stops.
Installation steps are not described.
EasyBuild searches for an easyblock named after the software, applying name mangling.
The package name is code-server.
The module searched for is code_server, and the class inside it follows.
EB_code_minus_server.
The hyphen became the literal word minus.
That is not a joke at EasyBuild’s expense.
It answers a real problem.
Class names must be valid Python identifiers.
Package names are not valid Python identifiers.
A bridge is required between the two.
The bridge must be reversible.
Without reversibility, two differently-named packages could collide on one class.
Spelling the punctuation out gives both.
The framework’s own justification names c++ and C# appear in the sample above.
Running it shows c++ becoming EB_c_plus__plus_ .
The doubled separator indicates that the substitution runs per character and collapses nothing.
The consequence people trip over stays invisible in code-server.
Try Python-bundle-PyPI.
Then try a name with _ in it.
The character is itself encoded as _underscore_ .
That is what keeps the mapping reversible.
Left alone, _minus_ would be ambiguous.
It could represent an encoded hyphen or a name that spells out minus between two of them.
The class name is longer and stranger than expected.
That moment leads people to decide the lookup is broken rather than mechanical.
It surprises exactly once.
It then explains a whole category of “why is the easyblock not being found”.
EB-Python-1 — Read a class name backwards
A traceback names a class you have never seen:
EB_Python_minus_bundle_minus_PyPI
What software is it?
Two different packages are called
a-banda_minus_b. Work out both class names. Does the encoding keep them apart?
Use the sample above for the second one rather than doing it in your head.
Solution
Python-bundle-PyPI. Each _minus_ is a hyphen, and nothing else in the
name was transformed.
a-b gives EB_a_minus_b. a_minus_b gives
EB_a_underscore_minus_underscore_b
too. So they stay apart, and that is the entire reason the low line is
not left alone: without encoding it, both names would collide on
EB_a_minus_b and one package would silently get the other’s easyblock.
The class for code-server exists.
Three actions are performed.
mapped_arch is computed as the source of that template value.
An error is raised for any architecture that is neither x86-64 nor aarch64.
The install step defaults to copying the unpacked tree into place.
code-server --help is run as a sanity check.
The install is incomplete until the binary has run.
The easyconfig does not contain any of that.
The name implies all of it.
What happens when the class does not exist¶
Guess before reading on.
The first answer is wrong, and it is the wrong answer that gets carried around.
That answer: EasyBuild falls back to something generic, ConfigureMake being the usual guess, and tries ./configure && make && make install.
It does not.
EasyBuild has no default.
If no easyblock is given and no software-specific class can be imported, the build stops:
No software-specific easyblock 'EB_example' found for example
The function get_easyblock_class in easybuild/framework/easyconfig/easyconfig.py raises the exit code MISSING_EASYBLOCK.
The parameters error_on_missing_easyblock and error_on_failed_import default to true.
The parameters error_on_missing_easyblock and error_on_failed_import are not overridden by the build path.
The only caller that passes false is the extensions machinery.
Falling through to a generic extension handler is the correct behaviour in that context.
Reversing that description yields a tool.
A missing easyblock line does not indicate a shrug.
It positively claims that a software-specific easyblock exists.
In the sample at the top of this page, the missing line reports that code_server.py is present before any search occurs.
An easyconfig that does specify easyblock = 'ConfigureMake' is not verbose.
Naming a generic easyblock is the only way to obtain one.
A second, more severe failure is hidden behind the same code.
An existing easyblock module with a broken internal import also raises ImportError.
EasyBuild separates the two cases by regex‑matching the error message against the requested module name.
A match indicates “no such easyblock”.
Any other case is reported as Failed to import... easyblock.
The second message indicates that the easyblock is present but its own dependencies cause the problem.
Editing the easyconfig will not help.
An aside on reading the error¶
All material in this page covers inferences generated by EasyBuild. An incorrect inference from a real run on a real cluster is presented. The package QMCPACK is a quantum Monte Carlo code; the easyconfig moved from a pinned development commit to the 4.4.0 release.
A build that got everything right and failed anyway
== processing EasyBuild easyconfig
/sw/eb/easyconfigs-surf/q/QMCPACK/QMCPACK-4.4.0-foss-2025a.eb
== building and installing QMCPACK/4.4.0-foss-2025a...
== fetching files and verifying checksums...
>> download succeeded:
https://github.com/QMCPACK/QMCPACK/archive/v4.4.0.tar.gz
>> sources:
>> qmcpack-4.4.0.tar.gz
[SHA256: 72accd955b59f9040ac8643059bc26c031e94b355299eecd293d10a872b6fc15]
== unpacking...
>> running shell command:
tar xzf qmcpack-4.4.0.tar.gz
>> command completed: exit 0, ran in 00h00m02s
== patching...
== preparing...
>> loading toolchain module: foss/2025a
>> loading modules for build dependencies:
>> * CMake/3.31.3-GCCcore-14.2.0
>> * Ninja/1.12.1-GCCcore-14.2.0
>> * pkgconf/2.3.0-GCCcore-14.2.0
== FAILED: Installation ended unsuccessfully: Specified start dir
/tmp/jenkins/build/QMCPACK/4.4.0/foss-2025a/qmcpack-4.4.0/qmcpack does not exist
Recorded: SURF Snellius, production run 20260909_063530, snel-zen4_h100, EasyBuild 5.3.1
Earlier steps were read until they stopped.
The correct URL was used.
The checksum was verified against the archive.
tar xzf returned exit code 0.
Patching, the toolchain, and every build dependency were fine.
Every expected check had already succeeded.
A required path does not exist.
The error names the consequence.
Something looked for qmcpack-4.4.0/qmcpack and did not find it.
The message says that and nothing else.
The identity of the lookup and the expectation are missing.
Answering those two questions is the work.
The lever is already provided in this page.
The easyconfig is a Python file; the expected path is a value assigned in it.
The value was start_dir, and it was set to qmcpack.
Five minutes earlier that value had been correct.
The easyconfig was built from a git checkout, and EasyBuild archives a clone under the repository’s name, so the sources resided one level down inside qmcpack/. A release tarball unpacks to qmcpack-4.4.0/ with the sources at the top level.
The line was correct for the old source and incorrect for the new one.
The change from one to the other says nothing about start_dir.
A complete recipe contains one hole.
The lint sample names the hole; it does not fill it.
EB-Python-fade-1 — Fill the hole the linter names
This recipe is otherwise complete. The listing below has no
checksums list. What is the smallest addition that makes that list
present, and what does that addition still not prove?
Solution
A checksums list with one sha256, aligned with sources. The
in-page linter then stops reporting the missing list. That does not
prove the hash is the hash of the archive: verification is a later
step, on a machine that can fetch the file.
EB-Python-2 — The smallest change that fixes it
The QMCPACK easyconfig in the previous section fails with:
Specified start dir /tmp/.../qmcpack-4.4.0/qmcpack does not exist
You have the released tarball, and tar tzf on it shows every path
beginning qmcpack-4.4.0/.
What is the smallest change to the easyconfig that fixes this, and what would you have to check before making it?
Solution
Delete the start_dir line.
start_dir is relative to the unpacked source directory. The tarball
already puts the sources at the top of qmcpack-4.4.0/, which is where
EasyBuild starts by default, so the parameter has nothing left to say.
What to check first: that no other parameter assumes the old layout. Patch strip levels and any path naming a subdirectory were written against a git checkout archived under the repository’s name, and they are quiet in exactly the same way. A diff against the previous recipe will report them unchanged, which is the wrong question.
Two observations are relevant.
Neither concerns QMCPACK.
First: An EasyBuild error reports the state it discovered.
The assumption underlying that state is absent from the message.
“Does not exist” is accurate.
The interesting fact is the expectation that the path exists.
That expectation resides in the easyconfig or in the EasyBlock selected by the easyconfig’s name.
Second: The parameters that break on a version bump describe the source layout.
The software’s own requirements remain unchanged.
The archive’s layout changes.
A checksum change is prominent.
A checksum change cannot be ignored.
start_dir, patch strip levels, and any subdirectory name are quiet.
They were correct previously.
A diff against the prior easyconfig shows them unchanged.
Unchanged is not the relevant question.
The relevant question is whether they still apply to the archive being unpacked on this run.
Three consequences¶
An easyconfig is a Python file evaluated in a prepared namespace.
All three of these appear constantly.
Unquoted names like SYSTEM are variables supplied by EasyBuild.
The quoting appears inconsistent, but it is not.
%(...)s is Python’s string formatting applied to a dictionary built from the easyconfig and the easyblock.
A template can resolve to a value not mentioned in the file.
The easyblock comes from transforming the software’s name.
If no specific easyblock is available, the build stops instead of guessing.
A missing easyblock line implies that an easyblock exists.
The next page is about toolchain.
The only unread line in the sample is toolchain.
That line causes more confusion than the other nineteen lines combined.
What to remember
An easyconfig is a Python file evaluated in a namespace EasyBuild prepared.
Unquoted names are variables EasyBuild supplied, not free identifiers.
If no software-specific easyblock exists, the framework raises
MISSING_EASYBLOCK.