Writing one from nothing¶
What you will be able to do
Write a working easyconfig for software that has none, starting from the five parameters that are actually required.
Get checksums into it without typing one, and know what the injection changes besides the checksums.
Run the two checks that decide whether it is fit to send upstream.
Each previous chapter read an existing easyconfig. The current chapter writes an easyconfig. The steps follow the order that minimizes wasted time.
Look for it first¶
Two searches before writing anything
$ eb --search '^QMCPACK'
$ eb -S 'qmcpack.*foss-2025a'
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10
--search accepts a regular expression, performs a case‑insensitive match, and scans the robot search path.
-S performs the same search but collapses shared path prefixes to $CFGS1, which is appropriate when the answer comprises forty files.
When a result is returned, the most economical correct action is typically to copy it and modify the version, which corresponds to the subject of A pin is for one generation rather than this chapter.
If a conda‑forge recipe or a Spack package is available, eb-stack ports a recipe and bumps one’s package plan (and Inspect writes a work queue, not a recipe) is the generator path.
This chapter provides the hand‑written file for cases where neither a robot hit nor a foreign recipe initiates the process.
The following material addresses situations where no result is returned.
The five parameters¶
eb -a enumerates all easyconfig parameters across eight categories, with a total of 110 in EasyBuild 5.3.1.
The initial category, named MANDATORY, contains eight parameters.
Hoste et al. [2012] previously identified these five as mandatory.
Enforcement follows the identical list.
Enforcement applies to only five parameters:
What is required, as opposed to what is filed under MANDATORY
$ python3 -c 'from easybuild.framework.easyconfig.easyconfig \
> import MANDATORY_PARAMS; print(MANDATORY_PARAMS)'
['name', 'version', 'homepage', 'description', 'toolchain']
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10
docurls, software_license and software_license_urls appear in that category and are not validated.
Fill them in anyway for anything that will be shared: an unrecorded licence will require someone to research it later under time pressure when a user asks whether they may run the software.
Thus, the smallest thing that runs:
Four decisions are hiding in that. Each one has a chapter behind it.
Which easyblock. Start with a generic one.
Start with ConfigureMake unless the build system is known to be something else.
Writing an easyblock is about when a generic easyblock stops being enough.
The answer is “later than you think”.
Which toolchain. Use the one your site already has, at the newest generation that applies, for A toolchain is a hierarchy’s reasons. Introducing a toolchain to install one package is a decision about the whole stack.
What the sanity check should say. Do not use “something got installed” but specify the exact file a user will run.
What “installed” means is the argument.
The practical form is that sanity_check_paths needs both keys, files and dirs, and at least one non-empty.
The easyconfigs test suite enforces exactly that.
The filename. Use the pattern <name>-<version>[-<toolchain>][<versionsuffix>].eb, with the toolchain omitted for the system toolchain.
It matters for dependency resolution.
The robot from The robot finds dependencies by constructing that filename and looking for it.
Getting the checksums right without typing them¶
Injection, and its refusal to overwrite
$ eb wobble-1.0.eb --inject-checksums
== injecting sha256 checksums in .../wobble-1.0.eb
== fetching sources & patches for wobble-1.0.eb...
>> sources:
>> .../wobble-1.0.tar.gz
ERROR: Found existing checksums, use --force to overwrite them
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10
The refusal is intentional.
--inject-checksums does not silently replace a checksum, since a checksum represents a claim made by someone and overwriting it silently allows an unnoticed source change to become verified.
When --force is used, it overwrites and does not verify the previous checksums first.
Four additional actions occur before processing a relevant file: The fetch step runs first, downloading any missing files.
The default algorithm is SHA256, but an optional argument can specify a different one, causing eb --inject-checksums foo.eb to interpret foo.eb as an algorithm name and fail; the filename must be placed first.
A backup is created with a .bak_<timestamp> suffix.
Parameters are reordered, placing source_urls, sources and patches in that sequence followed by checksums.
This deliberate change enforces a uniform style across thousands of files and results in a larger diff after injection than the added checksums alone.
It also handles extensions by processing every source and patch listed in each exts_list entry, which for a bundle contains most checksums.
The grammar (see What a checksums entry may contain) matters most when reading somebody else’s easyconfig. The written form does not mark whether two checksums mean “either” or “both”.
The two checks before a recipe leaves the machine¶
The style check, on a file with two faults planted in it
$ eb --check-style ugly-1.0.eb
WARNING: Use of 1 unknown easyconfig parameters detected in ugly-1.0.eb:
unused_local
If these are just local variables please rename them to start with
'local_', or try using --fix-deprecated-easyconfigs to do this
automatically.
Running style check on 1 easyconfig(s)...
ugly-1.0.eb:3:5: E221 multiple spaces before operator
ugly-1.0.eb:23:17: W299 trailing whitespace
ERROR: One or more style checks FAILED!
[FAIL] ugly-1.0.eb
Recorded: EasyBuild 5.3.1 with pycodestyle, local workstation, 2026-09-10; paths elided
Two things in that output are specific to EasyBuild rather than to pycodestyle.
W299 is EasyBuild’s own trailing-whitespace check, standing in for pycodestyle’s W291, and the difference is that it exempts comments and the description, examples and citing parameters.
Prose is allowed to end a line with a space; code is not.
The warning above the check is a different mechanism.
An easyconfig is Python, so any name can be assigned, and EasyBuild reads every top-level assignment as a parameter.
A name it does not recognise is reported, with the fix: local variables must be named ``local_*``.
--fix-deprecated-easyconfigs renames them.
This is An easyconfig is Python’s “it is Python” with the bill attached: the file has no separate namespace for your own scratch values.
--check-style runs pycodestyle with EasyBuild’s own settings.
The line limit is 120 rather than 79.
E741 is ignored.
W291, trailing whitespace, is replaced by a custom check that exempts comments and the description, examples and citing parameters, since those are prose and prose sometimes ends a line with a space.
--check-contrib runs that style check and then checks that SHA256 checksums are present for every source and patch.
Those two together are what the easyconfigs test suite runs on every file in the repository, so a file that fails either will fail upstream CI without a human looking at it.
Neither check knows whether the software works.
That is a separate question, and it is Testing, and the four things it can mean’s.
EB-Write-2 — Read a checksums block
Three easyconfigs, three checksum blocks. Say what each one asserts, and which of the three you would question.
# A
checksums = ['e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855']
# B
checksums = [(
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
'9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
)]
# C
checksums = [{
'thing-1.0.tar.gz': 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
'thing-1.0-fix.patch': '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
}]
Solution
A is one SHA256 for one file, recognised as SHA256 by its length.
B is a tuple, which means logical OR: either archive satisfies the check. It exists for an upstream that re-cut a release without changing the version, and it is the one to question archives is fine” is a claim somebody has to have actually checked byte for byte. Ask which two archives, and why they are equivalent.
C is a dict mapping filename to checksum, which is what injection writes
when there are two or more files, and the form that survives somebody
reordering sources. A list of two bare strings would have been
positional, and positional is the form that breaks silently when a patch
is inserted at the top.
Sending it upstream¶
The command eb --new-pr <file> does more than git push.
It creates a working copy of the easyconfigs repository.
It branches from an up‑to‑date develop branch.
It renames the file to match its own name, version, toolchain and versionsuffix.
It moves the file to the appropriate letter directory.
It commits the changes.
It pushes the commit to the user’s fork.
It opens a pull request.
The user’s own working copy remains unchanged.
Two defaults are important.
The target branch for a new pull request is develop.
A pull request aimed at master or main will be closed automatically.
The pull‑request title is generated as <{moduleclass}>[<toolchain>] <name> v<version>.
Therefore setting moduleclass correctly is advisable even though it is not enforced.
The option --update-pr <number> <file> updates an existing pull request.
It requires --pr-commit-msg, as does any --new-pr that modifies, deletes or adds patches to existing files.
A requirement that often surprises users is that a successful test report must be submitted for an easyconfig pull request.
The author of the pull request may not merge their own changes.
Upstream CI parses and style‑checks every easyconfig but does not build them.
Human verification provides the build evidence, which is the purpose of --upload-test-report and is covered in Testing, and the four things it can mean.
EB-Write-1 — From a tarball and a README
A user hands you https://example.org/thing-2.4.tar.gz and a README
saying ./configure --prefix=$HOME/thing && make && make install, and
that it needs HDF5 and a C++17 compiler.
Write the easyconfig. Name every parameter you would set and say why, including the filename you would save it under.
You have not computed a checksum. What is the exact command, and what will it change in the file besides adding one?
The build works. Name the two checks you run before opening a pull request, and one thing neither of them can tell you.
Solution
easyblock = 'ConfigureMake'exactly what that easyblock does.name’thing’, =version’2.4’, =homepage,description, and a toolchain your site has, sayfoss/2025a, whose compiler is new enough for C++17.source_urlsandsources = [SOURCE_TAR_GZ].dependencies = [('HDF5', '1.14.6')], which the robot will resolve against the toolchain from A toolchain is a hierarchy. Asanity_check_pathsnaming the binary the user will run, with both keys present.moduleclassthe title. Saved asthing-2.4-foss-2025a.ebdependencies by constructing that filename.Not
configopts = '--prefix…’=. EasyBuild passes the prefix itself, and hardcoding one is how an install lands outside the tree the site manages.eb thing-2.4-foss-2025a.eb --inject-checksums, with the filename first. It downloads the source if it is not already there, writes a.bak_<timestamp>copy, adds a SHA256, and reorderssource_urls,sourcesandpatchesbefore insertingchecksumsafter them. So expect a diff that touches lines you did not write.eb --check-styleandeb --check-contrib. Neither runs the software. They cannot tell you thatbin/thingactually works, which is why upstream requires a test report from a human who built it, and why the author of a pull request is not allowed to merge it.
What to remember
Five parameters are enforced:
name,version,homepage,description,toolchain. The MANDATORY category lists eight.Search before writing; a copy of an existing easyconfig with a new version is almost always the cheaper correct answer.
--inject-checksumsdownloads, backs the file up, reorders parameters and does not overwrite an existing checksum without--force.A local variable in an easyconfig has to be named
local_*.--check-styleand--check-contribare what upstream CI runs; neither runs the software, which is why a test report from a human is required.