Writing one from nothing

sources, easyblock, and sanity paths become a new easyconfig

A recipe from nothing is sources, a block, and a sanity check. The rest is the pages already written.

sources and sanity become a file

Writing one from nothing starts with sources and a sanity check, not with a template dump.

name version toolchain sources sanity

A new easyconfig is these fields, then an easyblock. Everything else is a later argument.

What you will be able to do

  1. Write a working easyconfig for software that has none, starting from the five parameters that are actually required.

  2. Get checksums into it without typing one, and know what the injection changes besides the checksums.

  3. Run the two checks that decide whether it is fit to send upstream.

Each earlier page reads an existing easyconfig. The current page 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. Shared path prefixes collapse to $CFGS1. The approach fits when the answer comprises forty files. When a result returns, the most economical correct action typically copies it and modifies the version. The subject matches A pin is for one generation rather than this page. When a conda‑forge recipe or a Spack package is available, the generator path follows eb-stack ports a recipe and bumps one’s package plan. Inspect writes a work queue, not a recipe is also relevant. The page provides the hand‑written file for cases where neither a robot hit nor a foreign recipe initiates the process. The following material addresses situations with no result returned.

The five parameters

eb -a enumerates all easyconfig parameters across eight categories, totaling 110 in EasyBuild 5.3.1. The first category, named MANDATORY, contains eight parameters. Hoste et al. [2012] reported five of these as mandatory. The enforcement applies the same list. The enforcement includes 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. They are not validated. Fill them in for any item that will be shared. An unrecorded licence will require later research under time pressure. A user may ask if they can run the software. The smallest recorded recipe is the one that runs.

easyblock = 'ConfigureMake'

name = 'example'
version = '1.0'

homepage = 'https://example.org/example'
description = "One sentence that reads as a sentence."

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

source_urls = ['https://example.org/releases']
sources = [SOURCE_TAR_GZ]

sanity_check_paths = {
    'files': ['bin/example'],
    'dirs': [],
}

moduleclass = 'tools'

Four decisions hide in that. Each decision has a page 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

--inject-checksums does not silently replace a checksum. A checksum is a claim made by someone. Overwriting a checksum silently permits an unnoticed source change to become verified. When --force is used, it overwrites. It does not verify 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. An optional argument can specify a different algorithm. This causes 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. It 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. For a bundle, the entry contains most checksums.

The grammar (see ) is most important for reading another’s easyconfig. The written form does not indicate whether two checksums represent 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

check-contrib after style is clean

$ eb --check-contrib clean-1.0.eb
Running contrib check on 1 easyconfig(s)...
All checks passed.

Recorded: EasyBuild 5.3.1, local workstation, 2026-09-10

Two items in that output are specific to EasyBuild instead of pycodestyle. W299 is EasyBuild’s trailing-whitespace check, replacing pycodestyle’s W291. It exempts comments and the description, examples and citing parameters. Prose may end a line with a space. Code may not end a line with a space. The warning above the check uses a different mechanism. An easyconfig is Python. Any name can be assigned. EasyBuild reads each top-level assignment as a parameter. A name it does not recognise is reported; the fix requires local variables must be named ``local_*``. --fix-deprecated-easyconfigs renames them. That is An easyconfig is Python’s “it is Python” with the bill attached. The file lacks a separate namespace for custom scratch values. --check-style runs pycodestyle using EasyBuild’s settings. The line limit is 120 instead of 79. E741 is ignored. W291, trailing whitespace, is replaced by a custom check that exempts comments and the description, examples and citing parameters. Those are prose. Prose sometimes ends a line with a trailing space.

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 form to question. “Either of these 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>. 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. The contributing docs ask for a test report on the PR: eb --from-pr <PR#> --upload-test-report --force --robot. That is a build plus a comment, not one of the four meanings of Testing.

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.

  1. Write the easyconfig. Name every parameter you would set and say why, including the filename you would save it under.

  2. You have not computed a checksum. What is the exact command, and what will it change in the file besides adding one?

  3. The build works. Name the two checks you run before opening a pull request, and one thing neither of them can tell you.

Solution
  1. easyblock = 'ConfigureMake': exactly what that easyblock does. name = 'thing', version = '2.4', homepage, description, and a toolchain your site has, say foss-2025a, whose compiler is new enough for C++17. source_urls and sources = [SOURCE_TAR_GZ]. dependencies = [('HDF5', '1.14.6')], which the robot will resolve against the toolchain from A toolchain is a hierarchy. A sanity_check_paths naming the binary the user will run, with both keys present. Set moduleclass = 'tools' so --new-pr titles as {tools}[foss/2025a] thing v2.4. Saved as thing-2.4-foss-2025a.eb by constructing that filename.

    Not configopts = '--prefix=...'. EasyBuild passes the prefix itself, and hardcoding one is how an install is written outside the tree the site manages.

  2. 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 reorders source_urls, sources and patches before inserting checksums after them. So expect a diff that touches lines you did not write.

  3. eb --check-style and eb --check-contrib. The second also requires a SHA256 for every source and patch. Neither runs the software. They cannot tell you that bin/thing actually 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-checksums downloads, 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-style and --check-contrib are what upstream CI runs; neither runs the software, which is why a test report from a human is required.