Writing an easyblock¶
What you will be able to do
Decide whether a build needs an easyblock or a parameter.
Write one: which class to derive from, which methods to override, and how to add a parameter of your own.
Load it without installing it anywhere, and test it without building anything.
Before this chapter
How a build executes organised around the steps.
Writing one from nothing in this chapter is whether an easyconfig was enough.
Enough Python to read a class and call a parent method. Nothing more.
An easyblock is Python code executed by EasyBuild. Therefore it is the most powerful element in this book and the one most frequently used prematurely.
The decision, before the code¶
EasyBuild 5.3.1 provides 82 generic easyblock classes across 47 modules and 282 software‑specific classes.
The generic classes exist.
Three questions are posed in sequence, and the first two normally resolve the issue.
Can ``configopts`` say it? A build that requires an unusual flag, an environment variable, or a command before configure or after install does not need additional code.
preconfigopts, configopts, prebuildopts, buildopts, preinstallopts, installopts and postinstallcmds are strings inserted into the commands described in How a build executes, and they handle more cases than expected.
Is there a generic easyblock for this build system? CMakeMake, MesonNinja, PythonPackage, Cargo, Bundle, Tarball, Binary, MakeCp, PerlModule are available.
Deriving from the appropriate class and overriding a single method requires only a fraction of the effort of building from scratch, and this approach is recommended in EasyBuild documentation.
Will more than one easyconfig need it? This question determines the true threshold.
An easyblock represents code maintained by a site across EasyBuild versions.
A single package with one unusual step can be addressed with a postinstallcmds line and a comment.
Multiple versions of the same package each requiring an unusual step justify creating an easyblock.
What to derive from¶
EasyBlock, located in easybuild.framework.easyblock, is abstract.
It is not abstract by convention:
Three methods every easyblock must supply
$ python3 -c 'import inspect
> from easybuild.framework.easyblock import EasyBlock
> print(inspect.getsource(EasyBlock.install_step))'
def install_step(self):
"""Dummy install step, should be implemented in derived classes."""
raise NotImplementedError
Recorded: EasyBuild 5.3.1 from PyPI, local workstation, 2026-09-10
The base class raises NotImplementedError for configure_step, build_step and install_step.
All other steps provide a functional default. An easyblock that inherits from ConfigureMake to be only twenty lines long.
Mechanical naming rules must be applied correctly, since EasyBuild locates an easyblock by computing its name instead of performing a lookup.
That encoding (see How an easyblock's name and file are computed) is what An easyconfig is Python’s widget solves. The book has one.
The methods that actually get overridden¶
And beside it, the easyblock:
import os
from easybuild.easyblocks.generic.configuremake import ConfigureMake
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.environment import setvar
from easybuild.tools.filetools import apply_regex_substitutions
class EB_example(ConfigureMake):
"""Support for building/installing example."""
@staticmethod
def extra_options(extra_vars=None):
"""Custom easyconfig parameters for example."""
extra_vars = ConfigureMake.extra_options(extra_vars)
extra_vars.update({
'with_widgets': [False, "Build the widget backend", CUSTOM],
})
return extra_vars
def configure_step(self):
"""Patch the makefile template, then configure as usual."""
apply_regex_substitutions('Makefile.in',
[(r'^CC\s*=.*', 'CC = %s' % os.getenv('CC'))])
if self.cfg['with_widgets']:
self.cfg.update('configopts', '--enable-widgets')
super(EB_example, self).configure_step()
Four things in that are the whole idiom.
``self.cfg[…]`` is the easyconfig. Every parameter is readable, and self.cfg.update appends to a string or list parameter rather than replacing it, which matters when the easyconfig also set it.
self.name, self.version, self.toolchain and self.all_dependencies are shortcuts to the ones used constantly.
``extra_options`` is how a parameter is born. A @staticmethod returning a dict of name: [default, help, category], merged with the parent’s.
The category is CUSTOM for an optional parameter and MANDATORY for one the easyconfig must set.
Once declared, it is a real parameter: eb -a -e EB_example lists it, and an easyconfig that misspells it gets the unknown-parameter warning from Writing one from nothing.
Call the parent, and know where. super().configure_step() last means “run the subclass preparation, then configure normally”.
First means “configure, then fix up what it produced”.
Getting that order wrong produces a build that works on your machine and fails on a clean one.
Use ``setvar``, not ``os.environ``. EasyBuild tracks environment changes so they can be logged and reported under Reading a failed build’s extended dry run.
A direct write to os.environ happens invisibly and does not appear in -x output at all.
One correction to make before copying an example from the upstream documentation: that page still documents run_cmd and run_cmd_qa, which EasyBuild 5 replaced with run_shell_cmd.
The framework’s own step methods use run_shell_cmd, and there is a migration page for the change.
An easyblock written from that page will import a function that is no longer there.
Loading one without installing it¶
An easyblock does not need to be installed into EasyBuild to try it.
eb example-1.0.eb --include-easyblocks=$HOME/myeb/*.py
--include-easyblocks accepts a comma‑separated list of paths, which may be absolute, relative, or globbed, and expects generic easyblocks in a subdirectory named generic.
The option builds a temporary directory with the correct Python package layout, symlinks the modules into it, and places that directory first on the Python search path, thereby defining precedence.
an included easyblock overrides the shipped one, which is desirable for testing but must not remain in a site configuration.
eb --list-easyblocks=detailed reports the exact file that was loaded, including its path.
This check is useful when an easyblock seems ineffective.
The mutually exclusive options --include-easyblocks-from-pr and --include-easyblocks-from-commit provide the same functionality for upstream work in progress.
Testing it without building anything¶
The documented order is a good one.
``eb -x``, the extended dry run, walks the whole procedure and reports what each step would do, including commands and environment changes.
It ends with (no ignored errors during dry run) when nothing went wrong, and that line is the indicator to look for instead of the absence of a traceback.
``eb –module-only –force``, skips to the module step and generates the modulefile. The easyblock’s exports are readable before any compilation.
A trap associated with the second command is that under --module-only, most \*_step methods do not run.
an easyblock whose configure_step sets self.foo and whose sanity_check_step reads it will encounter None.
The fix is a helper method that both steps call, avoiding state carried between steps.
A real build follows, then Testing, and the four things it can mean.
EB-Easyblock-1 — Four requests, and only one of them is code
For each, say whether you would write an easyblock, and what you would do instead if not.
A package whose
configureneeds--with-hdf5pointing at the HDF5 module’s root.A package that ships a
Makefile.inwithCC = gcchardcoded, and has done for six releases.A package that installs correctly but leaves its binaries in
libexec/, so the module exports nothing on$PATH.A package whose build must be run twice, with different flags, and the second run needs a file the first one produced.
Solution
No easyblock.
configopts = '--with-hdf5=$EBROOTHDF5', using the variable from What a module exports that the dependency’s module set duringprepare.An easyblock, and this is the case that justifies one. Six releases means the substitution will be needed again, so
apply_regex_substitutionsinconfigure_stepis worth maintaining. A patch would work for one version and conflict on the next, which is Patches, and what a patch is relative to’s argument.No easyblock.
modextrapaths = {'PATH': 'libexec'}, which is What a module exports. Nothing about the build changed; only what the module says about it.Probably no easyblock. A list-valued
configoptsmakes EasyBuild iterate the wholeready-to-extensionsblock once per entry, which is exactly two builds with different flags, and it is How a build executes’spostiterstep putting the environment back between them. Reach for an easyblock only if the second run needs to find the first one’s output in a way no flag can express, and then say so in a comment person will assume the iteration would have worked.
What to remember
Reach for
configoptsfirst, a generic easyblock second, and your own code only when more than one easyconfig will need it.configure_step,build_stepandinstall_stepare the three the abstract class does not implement.extra_optionsis how a parameter is born;setvaris how an environment variable is set so the dry run can see it.--include-easyblocksshadows the shipped easyblock, and--list-easyblocks=detailedis how to confirm which file loaded.Under
--module-onlymost steps do not run, so state carried between steps arrives asNone.