Languages that bring their own package manager

pip, cargo, and npm run inside an easyconfig; the pins stay exact

The language package manager runs inside the build. It does not become the resolver. Pins stay exact.

language package managers run inside the easyconfig

pip, cargo, and npm run inside the build. They do not replace the pin.

PythonBundle and RPackage install through exts_list; a standalone does not

Language ecosystems bring their own installer. The easyblock is the join to a prefix.

What you will be able to do

  1. Say how EasyBuild installs a Python, R, Perl, Rust or Julia package, and what it forbids the language’s own tool from doing.

  2. Say why the dependency set has to be enumerated rather than resolved.

  3. Say what to do about an ecosystem EasyBuild has no easyblock for.

Most of the software on a cluster is written in C, C++, or Fortran. The steps described in How a build executes correspond directly to it. Other software is written in languages that include their own package managers. A package manager provides an additional perspective on which components should be installed. EasyBuild’s answer is identical for all such cases. The answer precedes the specifics. The language’s tool may perform the build. The tool must not perform dependency resolution.

Python

PythonPackage is the easyblock. Its defaults follow the policy.

PythonPackage defaults, abridged from thirty-one

$ python3 -c 'from easybuild.easyblocks.generic.pythonpackage \
> import PythonPackage
> ex = PythonPackage.extra_options()
> for k in sorted(ex): print(" ", k, "=", repr(ex[k][0])[:34])'
 download_dep_fail = None
 fix_python_shebang_for = ['bin/*']
 pip_ignore_installed = True
 pip_no_build_isolation = True
 pip_no_index = None
 sanity_pip_check = True
 source_urls = ['https://pypi.python.org/packages
 use_pip = True
 use_pip_for_deps = False

Recorded: SURF Snellius, 2026-09-10, EasyBuild 5.3.1

Read use_pip_for_deps = False first. pip runs; it cannot determine additional requirements. Every dependency is declared in the easyconfig, resolved by the robot from The robot, and installed as its own module or its own exts_list entry. The rest of that list enforces it. download_dep_fail turns a dependency download into a build failure rather than a quiet success. pip_no_index forbids the index entirely. pip_ignore_installed stops pip from treating an existing package as sufficient. sanity_pip_check runs pip check at the end. It asks pip whether the environment it now sees is internally consistent. A declared version set that does not satisfy the packages’ own metadata is caught here. It is What “installed” means’s executing check, in a form specific to Python. fix_python_shebang_for = ['bin/*'] is the small detail that saves a support case. A wheel’s scripts carry a #! line pointing at whatever interpreter built them; EasyBuild rewrites it to the module’s Python. Without that, an installed script runs under the system Python and fails in a way that mentions neither. For a set of packages rather than one, PythonBundle is the exts_list pattern from One easyconfig, many installs. Two turn up constantly: SciPy-bundle and Python-bundle-PyPI. They exist to install the fifty required packages once instead of fifty times.

R, Perl, Julia, Go, Rust

The RPackage and the R-bundle-CRAN collection share this form. The PerlModule and PerlBundle follow the same pattern. The JuliaPackage and JuliaBundle are analogous. The GoPackage conforms as well. Rust is the exception; its defaults explicitly state the policy.

The Cargo easyblock, in full

$ python3 -c 'from easybuild.easyblocks.generic.cargo import Cargo
> ex = Cargo.extra_options()
> for k in sorted(ex): print(" ", k, "=", repr(ex[k][0])[:30])'
 crates = []
 enable_tests = True
 lto = None
 offline = True
 options = {}

Recorded: SURF Snellius, 2026-09-10, EasyBuild 5.3.1

By default, offline = True. The crates variable is a list populated with each crate and its version required by the build, analogous to the exts_list job for Python. A Rust easyconfig lists its full dependency tree, vendors the dependencies, and performs the build with the network disabled. Pinning crates in the easyconfig requires more effort than cargo build and is the only method to obtain a reproducible build for future years. A Rust build often compiles C as well. Crates named \*-sys wrap C libraries, and many compile the C sources themselves through the cc crate. That step runs the C compiler and the assembler from the build environment. So a Cargo easyconfig on GCCcore lists binutils beside Rust in builddependencies. Build dependencies are not dependencies shows zstd-sys failing without it.

The ecosystem with no easyblock

EasyBuild 5.3.1 has 82 generic easyblock classes, in 47 modules, and 282 software-specific ones. None of them is for npm. That absence is informative rather than an oversight. npm’s model is a resolver that runs at install time against a live registry, producing a tree that depends on when the install ran. Nothing in that is compatible with enumerate-and-vendor. A generic easyblock would have to either forbid what npm is for, or accept a build nobody can reproduce. Node packages get handled one of three ways. Which one is in view changes what can be promised about it. Vendored by hand. The easyconfig carries a package-lock.json as a source or a patch. The build runs npm ci --offline against a pre-populated cache. It is reproducible. Someone has to maintain the lock. Installed as a black box. A Binary or Tarball easyblock unpacks a release that already contains node_modules. It is reproducible in the sense that the bytes are fixed. It is opaque; nothing in the easyconfig says what is inside. Not EasyBuild’s problem. The user gets a Node module and installs their own packages into their own directory. That is the afterword’s argument, applied to one ecosystem. None of the three is wrong. Choosing the first and not maintaining the lock is wrong. Choosing the second while telling people it is reproducible is also wrong.

The rule underneath all of it

An ecosystem matches EasyBuild when its dependency set can be written down. Python, R, Perl, Rust and Julia each can provide a version list, a lock file, or a crates array. The language’s tool then compiles instead of deciding. An ecosystem resists EasyBuild when resolution is performed at install time. That does not judge the language. It states the purpose of a stack. Everything in A pin is for one generation about exact pins applies here, one layer down. A package manager that cannot be pinned cannot belong to a pinned stack.

EB-Languages-1 — Three requests, three answers

Three users ask for software on the same day.

  1. A Python tool with eleven dependencies, all on PyPI, all pure Python.

  2. A Rust command-line tool with a Cargo.lock listing 180 crates.

  3. A JavaScript build tool whose install instructions are npm install -g thing.

For each: which easyblock or approach, what has to be enumerated, and what would go wrong if you let the language’s own tool resolve.

Then say which of the three you would push back on, and what you would offer instead.

Solution

1 is PythonBundle with eleven exts_list entries, or PythonPackage plus dependencies if some are already modules. Enumerate the eleven with versions. Letting pip resolve would give you whatever PyPI served that afternoon, so the same easyconfig would install different software next month and nobody could say which.

2 is Cargo, with all 180 crates in the crates list, taken from the lock file. offline = True is already the default, so the build will fail rather than fetch. That failure is the feature: it is what makes the easyconfig a complete statement of what gets built.

3 has no easyblock, and npm install -g is the shape that cannot be made reproducible as written. Push back on this one. Offer a vendored build if there is a committed lock file, a Binary install of an official release if there is one, or a Node module and their own prefix if neither, with the trade stated plainly: the third option is theirs to maintain and it is available today.

What to remember

  • A language ecosystem is a second package manager living inside an easyconfig.

  • exts_list is how that manager’s packages become EasyBuild extensions.

  • An ecosystem with no easyblock is a residual, not a guess.