Writing an easyblock

an easyblock is a class that owns configure, build, and install

An easyblock is a class. The easyconfig fills parameters. The class owns the steps the file does not.

an easyblock class owns the steps

An easyblock is a class. Write one when the file cannot name the steps.

EasyBlock to ConfigureMake to a site class that overrides one method

Write an easyblock when a method on ConfigureMake is the wrong shape. Inherit, then override that method.

What you will be able to do

  1. Decide whether a build needs an easyblock or a parameter.

  2. Write one: which class to derive from, which methods to override, and how to add a parameter of your own.

  3. Load it without installing it anywhere, and test it without building anything.

Before this page

An easyblock is Python code executed by EasyBuild. The most powerful element in this book is an easyblock. The element most frequently used prematurely is an easyblock.

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. The first two normally resolve the issue. Can ``configopts`` say it? An unusual flag, an environment variable, or a command before configure or after install needs no additional code. preconfigopts, configopts, prebuildopts, buildopts, preinstallopts, installopts and postinstallcmds are strings inserted into the commands described in How a build executes. 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 far less effort than building from scratch. EasyBuild documentation recommends deriving from that class. 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 resides in easybuild.framework.easyblock and is abstract in the framework, not 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. Create an easyblock that inherits from ConfigureMake and is only twenty lines long. Mechanical naming rules must be applied correctly. EasyBuild locates an easyblock by computing its name instead of performing a lookup.

A name starting EB_ is software-specific. Everything else is generic. That is also what eb --list-easyblocks prints. The widget in An easyconfig is Python solves the encoding (see ).

The methods that actually get overridden

easyblock = 'EB_example'

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

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 make up the idiom. ``self.cfg[…]`` is the easyconfig. Each parameter is readable. self.cfg.update appends to a string or list parameter rather than replacing it. This matters when the easyconfig also sets the parameter. self.name, self.version, self.toolchain and self.all_dependencies are shortcuts to frequently used values. ``extra_options`` is how a parameter is born. A @staticmethod returns a dict of name: [default, help, category]. The dict merges with the parent’s. The category is CUSTOM for an optional parameter. The category is MANDATORY for a parameter the easyconfig must set. After declaration, it becomes a real parameter. eb -a -e EB_example lists it. 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 runs the subclass preparation, then configures normally. First configures, then fixes the produced output. Wrong order produces a build that works on the local machine. The same build fails on a clean machine. Use ``setvar``, not ``os.environ``. EasyBuild tracks environment changes. They can be logged and reported under Reading a failed build’s extended dry run. A direct write to os.environ is invisible to the dry run.

Loading one without installing it

Try an easyblock without installing into EasyBuild.

eb example-1.0.eb --include-easyblocks=~/myeb/*.py,~/myeb/generic/*.py

--include-easyblocks accepts a comma‑separated list of paths. Paths may be absolute, relative, or globbed. It expects generic easyblocks in a subdirectory named generic.

The option builds a temporary directory with the correct Python package layout. It symlinks the modules into that directory. It places the directory first on the Python search path. This establishes precedence.

An included easyblock overrides the one EasyBuild provides. That is useful for testing. It must not remain in a site configuration.

eb --list-easyblocks=detailed reports the exact file that was loaded. The report includes 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 effective. ``eb -x`` performs an extended dry run that traverses the entire procedure and reports the actions each step would perform, including commands and environment changes. When no errors occur, the run concludes with (no ignored errors during dry run). That line is the indicator to look for instead of the absence of a traceback. eb --module-only --force skips fetch, extract, patch, configure, build, test, install and sanitycheck. The modulefile is written without running sanity_check_step. eb --module-only --rebuild still runs prepare, ready, postiter, sanitycheck and module. That is the command that hits a None if configure_step set state that sanity_check_step reads. The fix provides a helper method invoked by both steps and stops state from being 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.

  1. A package whose configure needs --with-hdf5 pointing at the HDF5 module’s root.

  2. A package that ships a Makefile.in with CC = gcc hardcoded, and has done for six releases.

  3. A package that installs correctly but leaves its binaries in libexec/, so the module exports nothing on $PATH.

  4. A package whose build must be run twice, with different flags, and the second run needs a file the first one produced.

Solution
  1. No easyblock. configopts = '--with-hdf5=$EBROOTHDF5', using the variable from What a module exports that the dependency’s module set during prepare.

  2. An easyblock, and this is the case that justifies one. Six releases means the substitution will be needed again, so apply_regex_substitutions in configure_step is 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.

  3. No easyblock. modextrapaths = {'PATH': 'libexec'}, which is What a module exports. Nothing about the build changed; only what the module says about it.

  4. Probably no easyblock. A list-valued configopts makes EasyBuild iterate the whole ready-to-extensions block once per entry, which is exactly two builds with different flags, and it is How a build executes’s postiter step 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. Otherwise the next person will assume the iteration would have worked.

What to remember

  • Reach for configopts first, a generic easyblock second, and your own code only when more than one easyconfig will need it.

  • configure_step, build_step and install_step are the three the abstract class does not implement.

  • extra_options is how a parameter is born; setvar is how an environment variable is set so the dry run can see it.

  • --include-easyblocks shadows the shipped easyblock, and --list-easyblocks=detailed is how to confirm which file loaded.

  • Under --module-only fetch, extract, patch, configure, build, test and install do not run, so state carried between steps arrives as None.