mod eb_parse#
- module eb_parse#
Parse EasyBuild easyconfig (
.eb) files into structured candidates.Easyconfigs are a restricted Python DSL. This module evaluates that subset (assignments, lists/tuples/dicts,
SYSTEM,local_*and other name refs) and resolves EasyBuild-style%(…)stemplates derived from name / version / versionsuffix / toolchain — matching EasyBuild’sEasyConfigParserplus the core template set used for fixture goldens underfixtures/parser_hardcases/.Functions
- fn candidate_matches_dep(c: &Candidate, dep: &ResolvedDep) -> bool#
Whether a universe candidate satisfies a resolved dep (name + version + optional versionsuffix + optional per-dep toolchain). Cross-toolchain deps (e.g. eOn/gfbf depending on quill/GCCcore) are first-class here — unlike
filter_toolchainwhich keeps only the policy toolchain.When
dep.toolchainis None, any toolchain with a matching name/version/suffix is accepted (legacy identity match). Prefercandidate_matches_dep_for_recipeso unpinned deps must live in the recipe generation hierarchy (closer to EasyBuild robot behaviour).
- fn candidate_matches_dep_for_recipe(c: &Candidate, dep: &ResolvedDep, hierarchy: &crate::hierarchy::ToolchainHierarchy) -> bool#
Like
candidate_matches_dep, but when the dep has no explicit toolchain pin, only candidates whose toolchain is a member ofhierarchycount (e.g. CapnProto on GCCcore-14.x does not satisfy foss-2026.1 which needs GCCcore-15.2.0). Explicit fourth-tuple pins still match exactly, including cross-generation residuals (xtb/gfbf-2024a).
- fn check_recipe_deps(recipe: &ResolvedEasyconfig, universe: &[Candidate]) -> RecipeDepCheck#
Check that every runtime/build dep of
recipeappears as a candidate inuniverse(any tree layer already merged). Does not run the SAT solver — this is the packaging/robot completeness gate used beforeeb.Unpinned deps must match a hierarchy member of the recipe toolchain (derived from the robot universe when possible), so an older-generation GCCcore candidate does not false-pass a newer foss recipe.
- fn checksum_structure_findings(recipe: &ResolvedEasyconfig) -> Vec<String>#
Structural findings for the
checksumslist (EasyBuild convention: positional, allsourcesentries first, thenpatches). Catches the class of failure where a patch checksum is inserted in a source slot, which otherwise only surfaces as an eb “Missing checksum for X” abort after a build cycle has already been spent.
- fn easyconfig_basename(name: &str, version: &str, tc: &Toolchain, versionsuffix: Option<&str>) -> String#
Conventional basename:
Name-version-Toolchain-tcver.eb(+ versionsuffix).
- fn easyconfig_letter_dir(name: &str) -> String#
EasyBuild-style letter directory for
name(GROMACS→g,xtb→x).
- fn lock_from_candidates(cands: &[Candidate], generation_label: Option<String>, engine: &str) -> StackLock#
- fn merge_candidates_with_precedence(layers: &[Vec<Candidate>]) -> Vec<Candidate>#
Merge candidate layers with later-layer precedence: when two candidates share the same name + version + toolchain + versionsuffix, the later layer wins (overlay). Distinct installable variants remain separate candidates.
Used for site overlays on top of an upstream easyconfigs tree.
- fn packaging_gate(recipe: &ResolvedEasyconfig, required_configopts: &[&str]) -> Result<(), Vec<String>>#
Packaging gate: checksums present, moduleclass set, and optional required configopts substrings (e.g.
-Dwith_tests=false).A missing
easyblockis not an error: EasyBuild derives the easyblock from the software name when the recipe omits it (OpenMPI->EB_OpenMPI,GCC->EB_GCC, …), which is how the majority of upstream recipes are written. Only recipes whose name does not map to a software-specific easyblock need to declare one, and the recipe author — not this gate — makes that call.
- fn parse_easyconfig_file(path: &Path) -> Result<Candidate, ParseError>#
Parse one
.ebfile into a solver-facingCandidate.
- fn parse_easyconfig_tree(root: &Path) -> Result<ParseTreeResult, ParseError>#
Walk a directory tree for
*.eband parse all easyconfigs.Unparseable files are skipped (not fatal): they appear in
ParseTreeResult::skippedso callers can report coverage without aborting a real multi-thousand-file tree on the first bad recipe.
- fn parse_easyconfig_trees(roots: &[&Path]) -> Result<ParseTreeResult, ParseError>#
Parse multiple easyconfig trees and merge with later-path precedence. Skipped paths from every tree are retained.
- fn resolve_easyconfig_file(path: &Path) -> Result<ResolvedEasyconfig, ParseError>#
Resolve one
.ebfile to fully expanded fields.
- fn resolve_easyconfig_str(src: &str) -> Result<ResolvedEasyconfig, ParseError>#
Resolve easyconfig source text to fully expanded fields (no filesystem path).
- fn version_field_to_req(version: &str) -> String#
Map EasyBuild dependency version field to a solver requirement string.
Enums
Structs and Unions
- struct MissingDep#
One missing (or unmatched) dependency from a packaging/robot check.
- name: String#
- version: String#
- versionsuffix: Option<String>#
- role: String#
Runtime vs build-time role in the recipe.
- reason: String#
- struct ParseTreeResult#
Result of walking an easyconfig tree: successes + skipped unparseable files.
- skipped: Vec<SkippedEasyconfig>#
Implementations
- impl ParseTreeResult#
Functions
- fn coverage(&self) -> f64#
Coverage fraction
parsed / (parsed + skipped); 1.0 when the tree is empty.
- fn merge_with_precedence(mut self, other: ParseTreeResult) -> ParseTreeResult#
Merge another result (later candidates override on identity; skips append).
- fn parsed_count(&self) -> usize#
- fn skip_count(&self) -> usize#
- struct RecipeDepCheck#
Result of checking that a recipe’s deps exist somewhere in a robot universe.
- recipe: String#
- name: String#
- version: String#
- easyblock: Option<String>#
- configopts: Option<String>#
- moduleclass: Option<String>#
- checksum_count: usize#
- missing: Vec<MissingDep>#
- found: Vec<String>#
Implementations
- impl RecipeDepCheck#
Functions
- fn ok(&self) -> bool#
- struct ResolvedDep#
One resolved dependency entry (2–4 element EasyBuild dependency tuple).
- name: String#
- version: String#
Raw version field after template/local resolution (may be
1.2.3or >=1.2).
- versionsuffix: Option<String>#
- struct ResolvedEasyconfig#
Fully resolved easyconfig fields (templates and locals applied).
Solver-facing co-selection uses
Self::to_candidate. Packaging / contribution checks also use the optional metadata fields below (easyblock,configopts,moduleclass, …).- name: String#
- version: String#
- versionsuffix: Option<String>#
- dependencies: Vec<ResolvedDep>#
- builddependencies: Vec<ResolvedDep>#
- exts_list: Vec<ResolvedExt>#
- easyconfig_path: String#
Path of the source
.ebwhen parsed from disk (empty for in-memory text).
- easyblock: Option<String>#
EasyBuild easyblock class name (
MesonNinja,CMakeMake, …).
- configopts: Option<String>#
Meson/CMake/configure flags string after template expansion.
- moduleclass: Option<String>#
EasyBuild moduleclass (
chem,lib,tools, …).
- homepage: Option<String>#
Homepage URL when set.
- checksums: Vec<String>#
Source checksums list (strings), when present — used for packaging gates.
- sources_count: usize#
Number of entries in
sources(0 when the field is absent).
- patch_names: Vec<String>#
Patch file names from
patches(tuple/dict entries reduced to the name).
- checksum_entry_keys: Vec<Vec<String>>#
Per-
checksums-LIST-ENTRY dict keys (empty inner vec for plain-string entries). Parallel to the checksums list entries, not the flattened values, so a multi-arch dict entry stays one entry.
Implementations
- impl ResolvedEasyconfig#
Functions