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 dependencies crossing from a parent toolchain to a core toolchain 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 implicit deps follow EasyBuild’s minimal-toolchain search within the recipe generation.
- 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 non-SYSTEM candidates inhierarchycount for a non-SYSTEM recipe. EasyBuild may minimize an implicit dependency to a generation member such as GCCcore, but it only selects SYSTEM when the dependency tuple says so explicitly. Explicit fourth-tuple pins still match exactly, including explicitly selected cross-generation dependencies.
- 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.Implicit deps must match a non-SYSTEM hierarchy member of the recipe toolchain (derived from the robot universe when possible), so neither an older-generation candidate nor a SYSTEM fallback can false-pass.
- 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(ExamplePkg→e).
- fn existing_versions(query: &ExistingVersionsQuery<'_>) -> Vec<String>¶
Versions of
query.namealready present atquery.generation, sorted newest first by the same comparison the miner uses.This is the producer for
crate::miner::is_backfill’sexisting_versions: without it a caller has to hand-build that list, which is why backfill detection had no production call site.
- fn filter_toolchain(cands: &[Candidate], tc: &Toolchain) -> Vec<Candidate>¶
Keep only candidates built at exactly this toolchain.
The narrow form, for callers that mean one level and nothing under it. A solve wants
filter_toolchain_hierarchyinstead, since a recipe’s own build dependencies routinely sit one level down.
- fn filter_toolchain_hierarchy(cands: &[Candidate], tc: &Toolchain, members: &[Toolchain]) -> Vec<Candidate>¶
Candidates built against exactly this toolchain. Keep the policy toolchain and everything below it in its hierarchy.
A stack is not built at one toolchain. A recipe at
GCC-15.2.0takes its CMake fromGCCcore-15.2.0and its licence file from SYSTEM, exactly as EasyBuild’s minimal-toolchain search does, so a universe filtered to the policy toolchain alone cannot satisfy the dependencies of its own members and reports them as missing packages.membersis the hierarchy for the policy toolchain, lowest first. Passing an empty slice reduces this tofilter_toolchain.
- fn lock_from_candidates(cands: &[Candidate], generation_label: Option<String>, engine: &str) -> StackLock¶
Build a lock from an already-selected candidate set.
The caller has done the choosing; this records it.
- 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_file_reporting(path: &Path) -> Result<(ResolvedEasyconfig, Vec<SkippedStatement>), ParseError>¶
As
resolve_easyconfig_file, also returning the skipped statements.
- fn resolve_easyconfig_str(src: &str) -> Result<ResolvedEasyconfig, ParseError>¶
Resolve easyconfig source text to fully expanded fields (no filesystem path).
Tolerant: a statement the parser cannot model is skipped so the rest of the recipe still resolves. Use
resolve_easyconfig_str_reportingwhen you need to know what was skipped.
- fn resolve_easyconfig_str_reporting(src: &str) -> Result<(ResolvedEasyconfig, Vec<SkippedStatement>), ParseError>¶
Resolve easyconfig text, also returning the statements the parser skipped.
resolve_easyconfig_stris this without the second value; the parse is identical, so asking for the report costs nothing and changes nothing.
- fn validate_lock_deps(lock: &StackLock, cands: &[Candidate]) -> Result<(), String>¶
Check that every dependency of every locked package is itself locked.
A lock that omits a transitive dependency installs a stack that cannot build, so this is worth checking before one is trusted.
- fn version_field_to_req(version: &str) -> String¶
Map EasyBuild dependency version field to a solver requirement string.
Enums
- enum ParseError¶
Why an easyconfig could not be read.
- Io(String, std::io::Error)¶
The file could not be read.
- Parse(String, String)¶
The file could not be understood. Carries the path and the reason, including a line number where the failure was positional.
Structs and Unions
- struct ExistingVersionsQuery<'a>¶
Which versions of one package a universe already carries at one toolchain generation.
Struct-shaped like the other request types in this crate so the three fields cannot be transposed at a call site.
- universe: &'a [Candidate]¶
Universe from
parse_easyconfig_treeorparse_easyconfig_trees, live checkout or historical merge-base tree alike.
- name: &'a str¶
Package name to match, exactly.
- struct MissingDep¶
One missing (or unmatched) dependency from a packaging/robot check.
- name: String¶
Dependency that could not be matched.
- version: String¶
Version the recipe asked for.
- versionsuffix: Option<String>¶
Versionsuffix required, when one was.
- role: String¶
Runtime vs build-time role in the recipe.
- reason: String¶
Why nothing matched: absent entirely, or present at the wrong version or toolchain.
- struct ParseTreeResult¶
Result of walking an easyconfig tree: successes + skipped unparseable files.
- skipped: Vec<SkippedEasyconfig>¶
Everything that did not, kept so a tree is never silently partial.
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¶
How many parsed successfully.
- fn skip_count(&self) -> usize¶
How many easyconfigs failed to parse.
- struct RecipeDepCheck¶
Result of checking that a recipe’s deps exist somewhere in a robot universe.
- recipe: String¶
Easyconfig that was checked.
- name: String¶
Package it declares.
- version: String¶
Version it declares.
- easyblock: Option<String>¶
Easyblock it uses, when it sets one.
- configopts: Option<String>¶
Configure flags, after template expansion.
- moduleclass: Option<String>¶
Moduleclass, when it sets one.
- checksum_count: usize¶
How many checksum entries it carries.
- missing: Vec<MissingDep>¶
Dependencies the robot tree does not provide.
- found: Vec<String>¶
Dependencies that were matched.
- unverified_toolchain: Option<String>¶
Set when the recipe’s toolchain generation could not be resolved, so matching ran without a hierarchy to filter by.
Every entry in
foundis then a name-and-version match that ignores toolchain, which can pair a recipe with a dependency from an unrelated generation (theEB_MAINT_CROSS_GENshape).missingstaying empty is not evidence the recipe resolves in EasyBuild; callers must say so rather than report a clean result.
Implementations
- impl RecipeDepCheck¶
Functions
- fn ok(&self) -> bool¶
Whether every dependency was found.
- fn toolchain_verified(&self) -> bool¶
Whether the matches carry toolchain evidence, i.e. a hierarchy was known.
Distinct from
Self::okon purpose:okanswers “was anything left unmatched”, this answers “is a match worth anything”.
- struct ResolvedDep¶
One resolved dependency entry (2–4 element EasyBuild dependency tuple).
- name: String¶
Dependency name.
- version: String¶
Raw version field after template/local resolution (may be
1.2.3or >=1.2).
- versionsuffix: Option<String>¶
Versionsuffix required, when the entry names one.
- 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¶
Package name.
- version: String¶
Package version after template expansion.
- versionsuffix: Option<String>¶
Versionsuffix, when the easyconfig sets one.
- dependencies: Vec<ResolvedDep>¶
Runtime dependencies.
- builddependencies: Vec<ResolvedDep>¶
Build-time-only dependencies.
- exts_list: Vec<ResolvedExt>¶
Bundled extensions.
- 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).
- source_urls: Vec<String>¶
Expanded
source_urlsentries, used to classify which artifact the recipe actually downloads.
- 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.
- checksums_by_filename: BTreeMap<String, String>¶
Checksums the recipe states by filename, for the entries written as
{'name': 'sha…'}. Position says nothing once one entry carries several hashes.
Implementations
- impl ResolvedEasyconfig¶
Functions
- struct ResolvedExt¶
One
exts_listentry after resolution.- name: String¶
Extension name.
- version: String¶
Extension version.
- struct SkippedEasyconfig¶
One easyconfig path that could not be parsed into a candidate.
- path: String¶
Easyconfig that was skipped.
- error: String¶
Why it could not be parsed.
- struct SkippedStatement¶
Resolve easyconfig source text to fully expanded fields (no filesystem path). One statement the tolerant parser could not model, and why.
The parser skips an unrepresentable statement so a single oddity does not cost the whole easyconfig. That is the right default, but it means a recipe can parse “successfully” while a line the author cared about was dropped. These records are what
--strictreports.- line: usize¶
1-based line the statement starts on.
- message: String¶
Why it could not be parsed, already carrying its own line prefix.
- text: String¶
The statement text as written, trimmed.
Implementations
- impl SkippedStatement¶
Functions
- fn reason(&self) -> &str¶
The reason with the parser’s own
line N:position prefix stripped, so a caller that prints its own position does not repeat it.
Traits implemented
- impl std::fmt::Display for SkippedStatement¶