Output formats#
1 Stack lock (stack.lock.json)#
{
"schema_version": 1,
"toolchain": {"name": "foss", "version": "2025b"},
"generation_label": "foss-2025b",
"packages": [
{
"name": "GROMACS",
"version": "2025.0",
"toolchain": {"name": "foss", "version": "2025b"},
"versionsuffix": null,
"easyconfig_path": "foss-2025b/GROMACS-2025.0-foss-2025b.eb"
}
],
"solver": {
"engine": "resolvo_cdcl_sat",
"engine_version": "resolvo+eb_stack-0.3.0",
"timestamp": "2026-01-01T00:00:00Z"
}
}
schema_version1as of eb-stack 0.2.0.toolchainThe policy’s target toolchain (not necessarily every package’s own toolchain field, though in practice they match).
generation_labelOptional human label, e.g.
"foss-2025b"when produced from an easyconfig tree. Library callers may omit it for hand-built universes.packagesOne entry per co-selected package, sorted by name. Each entry is a
LockPackage:name,version, its owntoolchain, optionalversionsuffix, and theeasyconfig_pathit came from.solverengineis always"resolvo_cdcl_sat".engine_versionembeds the crate version.timestampis RFC 3339, UTC, second precision.
2 Internal candidate universe#
{
"toolchain": {"name": "foss", "version": "2025b"},
"generation_label": "foss-2025b",
"candidates": [
{
"name": "GROMACS",
"version": "2025.0",
"toolchain": {"name": "foss", "version": "2025b"},
"versionsuffix": null,
"easyconfig_path": "foss-2025b/GROMACS-2025.0-foss-2025b.eb",
"dependencies": [
{"name": "OpenMPI", "version_req": ">=4.1.6"}
],
"builddependencies": []
}
]
}
dependencies and builddependencies use the same DepReq shape and the
same version_req grammar as policy pins (see
policy reference). This structure is a Rust/library input;
the version-one CLI builds it from repeatable --easyconfigs paths.
3 Build list (--build-list-out)#
Plain text, one easyconfig path per line, trailing newline, no blank lines:
foss-2025b/OpenBLAS-0.3.27-foss-2025b.eb
foss-2025b/OpenMPI-4.1.6-foss-2025b.eb
foss-2025b/FFTW-3.3.10-foss-2025b.eb
foss-2025b/GROMACS-2025.0-foss-2025b.eb
Order is a topological sort over the co-selected stack: a dependency’s path
always appears before every co-selected package that needs it (runtime or
build-time). Ties are broken by package name for a deterministic order
independent of input list order or HashMap iteration. An empty lock
produces an empty file.
4 Stack diff (--stack-diff-out)#
Markdown, meant to be pasted directly into a pull request:
# Stack diff
Baseline (`foss-2025a`) -> solved (`foss-2025b`).
## Summary
- **unchanged**: 1
- **added**: 0
- **removed**: 0
- **version-bumped**: 3
## Packages
### FFTW -- unchanged
- Baseline: `3.3.10` -- `foss-2025a/FFTW-3.3.10-foss-2025a.eb`
- Solved: `3.3.10` -- `foss-2025b/FFTW-3.3.10-foss-2025b.eb`
### GROMACS -- version-bumped
- Baseline: `2024.1` -- `foss-2025a/GROMACS-2024.1-foss-2025a.eb`
- Solved: `2025.0` -- `foss-2025b/GROMACS-2025.0-foss-2025b.eb`
- Change: `2024.1` -> `2025.0`
Every package present in either the baseline or the solved lock is
classified once: unchanged (same version both sides), added (solved
only), removed (baseline only), or version-bumped (present both sides,
different version). Sections are sorted by package name.
5 Planned SBOM (--sbom-out, CycloneDX 1.5)#
Documents are built with the official cyclonedx-bom crate (same models as cargo-cyclonedx), serialized as CycloneDX 1.5 JSON.
Opt-in: --sbom-out has no default filename. Omit it and stack solve writes
only the lock (and any requested build list / stack diff). eb-stack stack sbom --lock LOCK defaults to stack.cdx.json because that command’s sole
purpose is SBOM emission.
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:…",
"version": 1,
"metadata": {
"timestamp": "2026-01-01T00:00:00Z",
"tools": [{"vendor": "SURF", "name": "eb-stack", "version": "resolvo+eb_stack-0.3.0"}],
"component": {
"type": "application",
"name": "easybuild-stack-foss-2025b",
"version": "foss-2025b",
"description": "Planned EasyBuild stack inventory from eb-stack lock (pre-install; not a post-build compliance scan)"
},
"lifecycles": [{"phase": "pre-build"}],
"properties": [
{"name": "eb_stack:document_kind", "value": "planned-sbom-from-lock"},
{"name": "eb_stack:solver_engine", "value": "resolvo_cdcl_sat"},
{"name": "eb_stack:toolchain", "value": "foss-2025b"}
]
},
"components": [
{
"type": "library",
"bom-ref": "pkg:generic/GROMACS@2025.0?toolchain=foss-2025b",
"name": "GROMACS",
"version": "2025.0",
"purl": "pkg:generic/GROMACS@2025.0?toolchain=foss-2025b",
"properties": [
{"name": "easybuild:toolchain", "value": "foss-2025b"},
{"name": "easybuild:easyconfig_path", "value": "foss-2025b/GROMACS-2025.0-foss-2025b.eb"},
{"name": "eb_stack:lifecycle", "value": "pre-install-plan"}
]
}
],
"dependencies": [
{"ref": "pkg:generic/GROMACS@2025.0?toolchain=foss-2025b", "dependsOn": ["pkg:generic/OpenMPI@4.1.6?toolchain=foss-2025b"]}
]
}
Each component’s bom-ref / purl is a synthetic pkg:generic/ reference
(EasyBuild has no dedicated PURL type) embedding name, version, and
toolchain. Metadata lifecycles: pre-build and
eb_stack:lifecycle: pre-install-plan mark a planned inventory from the
solver selection, not a scan of installed software.
dependsOn edges:
From
stack solve(universe in hand): each package’s declared runtimedependencies, intersected with co-selected lock members. Build-time deps (builddependencies) go on the component propertyeb_stack:buildDependsOn(comma-separated refs), not the runtime graph.From
eb-stack stack sbom --lock LOCK(lock only):dependsOnis empty (unknown edges). The document never invents all-to-all co-stack cycles. EmptydependsOnarrays may be omitted by cyclonedx-bom serialization.