A policy is the input that turns a universe of parsed easyconfig candidates
into one selection. It is a JSON object matching the Policy struct in
src/domain.rs.
{
"toolchain": {"name": "foss", "version": "2025b"},
"roots": ["GROMACS"],
"root_priority": ["GROMACS"],
"pins": [
{"name": "OpenBLAS", "version_req": "==0.3.24"}
],
"forbid": ["OpenMPI-4.1.5-foss-2025b.eb"],
"objective": "prefer_newer",
"require_upgrade": {"name": "GROMACS", "relative_to_baseline": true}
}Only toolchain and roots are required; every other field defaults as
noted below.
toolchain{"name": string, "version": string}. Required. Selects which candidates from the parsed tree participate: the policy toolchain, every member of its hierarchy, andSYSTEM. A recipe atGCCtakes CMake fromGCCcoreand a licence file fromSYSTEM, so an exact match on the policy toolchain alone cannot satisfy its own dependencies.roots- Array of package names. Required, non-empty. Every dependency (runtime and build-time) a root needs co-selects automatically; the solve fails if any named root has no candidate at all.
root_priority- Optional array of package names. Declares which root
wins the newest-version tie-break when two or more roots share a
dependency with conflicting requirements. Defaults to
rootslist order when omitted or empty. Any root missing from an explicitroot_priorityis appended inrootsorder, so every root always participates. Reorderingrootsdoes not change the effective priority onceroot_priorityis set explicitly. See the how-to and why this exists. pins- Optional array of
{"name": string, "version_req": string}. Restricts a package to candidates whose version matches the requirement string (grammar below). A pin on a package with no matching candidate fails the solve at policy-construction time, before any Boolean satisfiability (SAT) solving happens. forbid- Optional array of strings. Each entry matches either a full
easyconfig_pathor a bare packagename; matching candidates are excluded from the universe entirely before solving. prefer_installed- Optional bool, default false. When true, a package
present in the baseline stays at that version unless a pin, a forbid, or
require_upgradesays otherwise. The derived criteria are then["-changed", "-notuptodate"]: staying put outranks moving to newest. objective- Optional string, default
"prefer_newer". The only objective the solver implements: among jointly feasible assignments, prefer the newest version of each root (in priority order), then the newest version of every other co-selected package. criteria- Optional array of Common Upgradeability Description Format (CUDF) criterion names, in precedence
order. Empty means derive them:
["-notuptodate"]by default, or["-changed", "-notuptodate"]whenprefer_installedis true. An explicit list wins. Known names are-changed(minimise packages moved off the baseline version) and-notuptodate(minimise packages held below the newest candidate). Any other name fails the solve. The applied list is recorded on the lock assolver.criteria. require_upgrade- Optional
{"name": string, "relative_to_baseline": bool}, or an array of those objects. Whenrelative_to_baselineis true, the named package’s solved version must be strictly newer than its version in the baseline lock (--baseline-easyconfigsor--baseline-easystack). No baseline, or no baseline entry for that package, fails the solve with an explicit error instead of skipping the requirement without a warning.
Used by both pins[].version_req and parsed easyconfig dependency
requirements (==X, >=X, <=X, >X, <X, or a bare X for an exact
match). Version comparison is EasyBuild-shaped, not semver: digit runs
compare numerically, letter runs compare lexicographically, and a trailing
letter run with nothing on the other side sorts as a pre-release
(2025a < 2025b, 1.0rc1 < 1.0).
def effective_root_priority(policy):
order = policy.root_priority or policy.roots
order = [r for r in order if r in policy.roots] # only real roots
for r in policy.roots:
if r not in order:
order.append(r) # never drop a root
return orderThis is a description of Policy::effective_root_priority in
src/domain.rs, not a separate implementation; it is shown here because the
outcome of a multi-root solve depends entirely on this order.
eb-stack stack solve --stack-policy POLICY.json