mod hierarchy#
- module hierarchy#
Toolchain generation hierarchy: sub-toolchain membership for dependency resolve.
EasyBuild applications built with a composite toolchain (e.g.
foss-2024a) pull dependencies from several sub-toolchain levels of the same generation (GCCcore,GCC,gfbf,gompi, …). Exact top-level toolchain string matching therefore finds none of them.Hierarchy ground truth is EasyBuild’s
get_toolchain_hierarchy(framework). Checked-in JSON fixtures underfixtures/toolchain_hierarchy/capture that output so unit tests and runtime resolution do not require EasyBuild.Functions
- fn count_generation_dep_versions(name: &str, cands: &[Candidate], hierarchy: &ToolchainHierarchy) -> HashMap<String, usize>#
Count exact version pins of dependency
nameamong recipes whose own toolchain is inhierarchy(generation-scoped reverse-deps).Both
dependenciesandbuilddependenciesare counted. Only exact version pins are tallied (3.29.3or solver form==3.29.3); empty/range reqs skip.Pure function for unit tests: pass a synthetic
[Candidate]universe.
- fn derive_hierarchy_from_candidates(parent: &Toolchain, cands: &[Candidate]) -> Option<ToolchainHierarchy>#
Derive a GCC-family generation hierarchy from the parsed easyconfig universe.
The robot tree itself defines each generation: the foss-<gen> (or gompi-<gen>` /
gfbf-<gen>`) toolchain-definition easyconfig pins the generation's ``GCCversion, and the intermediate composite definitions exist as sibling recipes. Deriving from the tree makes any generation present in the robot tree work with no fixture (the annual-bump case: a brand-newfoss-2026.1must not require shipping a new fixture).Members mirror EasyBuild’s
get_toolchain_hierarchyorder for foss-family generations: system < GCCcore < GCC < gompi < gfbf < parent. Intermediate composites are included only when their definition recipe is in the tree. GCCcore is assumed version-paired with GCC (true for all modern generations, 2020a+). Non-GCC-family parents returnNone.
- fn filter_candidates_in_hierarchy(cands: &[Candidate], hierarchy: &ToolchainHierarchy) -> Vec<Candidate>#
Keep candidates whose toolchain is any member of
hierarchy.
- fn hierarchy_for(parent: &Toolchain, fixture_path: Option<&Path>) -> Result<ToolchainHierarchy, HierarchyError>#
Resolve hierarchy for
parent: optional fixture path, else built-in known map.
- fn hierarchy_for_with_tree(parent: &Toolchain, fixture_path: Option<&Path>, cands: &[Candidate]) -> Result<ToolchainHierarchy, HierarchyError>#
Like
hierarchy_for, with the parsed easyconfig universe as a final fallback: fixture path, else built-in, else derived from the tree (derive_hierarchy_from_candidates).
- fn hierarchy_member_rank(hierarchy: &ToolchainHierarchy, tc: &Toolchain) -> Option<usize>#
Rank of
tcinhierarchy(parent last = highest). Usestoolchains_matchso SYSTEM empty/systemlabels compare equal. Out-of-hierarchy →None.
- fn is_system_toolchain(tc: &Toolchain) -> bool#
EasyBuild / parser SYSTEM toolchains: name
system(any case), version empty orsystem.
- fn known_hierarchy(parent: &Toolchain) -> Option<ToolchainHierarchy>#
Built-in hierarchy fixtures embedded at compile time (no EasyBuild at test/runtime).
- fn load_hierarchy_fixture(path: &Path) -> Result<ToolchainHierarchy, HierarchyError>#
Load a hierarchy fixture JSON file.
- fn nearest_candidates_hint(name: &str, cands: &[Candidate]) -> String#
Short “what DOES exist” suffix for a missing dependency: up to four name-matching candidates across all generations, so the failure reads as a work item (bump/author from one of these) instead of a dead end. Returns an empty-or-parenthesised string safe to append to an error message.
- fn pick_consensus_version(counts: &HashMap<String, usize>, eligible: &[String]) -> Option<String>#
Pick a generation-consensus version of
nameamongeligibleinstall versions.Clear majority (at least 80 percent of generation pins of name that land in eligible) yields that modal version. Else pick the newest among eligible versions that appear in the pin counts (weak signal / no unique consensus). If no pin counts hit eligible, pick the newest eligible (true no-signal fallback).
eligible must already be floor/suffix/hierarchy filtered; empty yields None. Callers that hold full Candidate values should first run prefer_non_system_candidates so SYSTEM does not beat GCCcore on version alone.
- fn prefer_non_system_candidates<'a>(eligible: &[&'a Candidate]) -> Vec<&'a Candidate>#
Prefer generation-compiler / composite toolchains over SYSTEM among already hierarchy-eligible install candidates.
SYSTEM remains a hierarchy member (for rare binary-only build tools), but when a GCCcore/GCC/gfbf/… candidate exists it must win empty-consensus “newest” fallbacks (e.g. CMake 3.29.3 @ GCCcore-13.3.0 over CMake 3.31.8 @ SYSTEM).
Returns
eligibleunchanged when every candidate is SYSTEM (or the list is empty).
- fn resolve_dep_version_in_hierarchy(name: &str, cands: &[Candidate], hierarchy: &ToolchainHierarchy) -> Option<String>#
Among hierarchy members for
name, pick a safe version for the target generation.Among hierarchy members for name, pick a safe version for the target generation.
Selection rules, in order: (1) candidate must be in the hierarchy and match name (strict name+version membership; out-of-generation GCCcore/GCC never qualify); (2) optional versionsuffix must match (empty/None matches candidates with no suffix); (3) optional floor_version never picks a version older than the floor; (4) prefer candidates on the hierarchy parent toolchain when present, otherwise the highest hierarchy member that has a candidate; (5) among the same hierarchy rank (or when use_consensus / floor is set), use generation consensus when available: if a version has a clear majority (at least 80 percent of pins) and is eligible, pick it, else the newest eligible version that has at least one generation pin (else newest eligible). Without floor and without consensus, pick the newest version by cmp_version. Returns None when no candidate satisfies the filters.
- fn resolve_dep_version_in_hierarchy_opts(name: &str, cands: &[Candidate], hierarchy: &ToolchainHierarchy, opts: &ResolveDepOpts<'_>) -> Option<String>#
Like
resolve_dep_version_in_hierarchywith floor / versionsuffix filters.Strict hierarchy: only candidates whose toolchain name and version are exact members of
hierarchy(viaToolchainHierarchy::contains/toolchains_match) are eligible. A newer package on GCCcore-14.x is not valid for foss-2024a (GCCcore-13.3.0 only), even if the name matches.
- fn resolve_dep_versions_for_specs(specs: &[SourceDepSpec], cands: &[Candidate], hierarchy: &ToolchainHierarchy, keep_old: bool) -> Result<(HashMap<String, String>, Vec<String>), HierarchyError>#
Resolve many source dependency specifications with floor and versionsuffix safety.
Deps with a non-empty versionsuffix are not bumped (caller keeps source).
Deps with SYSTEM 4th-tuple toolchain are not bumped (keep source pin).
Deps marked optional (
# optionalon that line) resolve/bump like any other dep when hierarchy candidates exist (# optional= optional to include, not frozen). If unresolved they soft-keep the source pin (no hard ERROR). The emitter preserves the comment text; only the version token changes.Resolved versions are never older than the source version.
Missing non-optional candidates yield
HierarchyError::MissingDepunlesskeep_oldis true (then the source version is kept and listed inkept_old).
- fn resolve_dep_versions_in_hierarchy(names: impl IntoIterator<Item = impl AsRef<str>>, cands: &[Candidate], hierarchy: &ToolchainHierarchy) -> HashMap<String, String>#
Resolve versions for many dependency names. Names missing from the universe are omitted from the map (callers may leave source versions unchanged).
- fn resolve_dep_versions_in_hierarchy_strict(names: impl IntoIterator<Item = impl AsRef<str>>, cands: &[Candidate], hierarchy: &ToolchainHierarchy) -> Result<HashMap<String, String>, HierarchyError>#
Like
resolve_dep_versions_in_hierarchybut errors if any name is unresolved.
- fn toolchains_match(a: &Toolchain, b: &Toolchain) -> bool#
Equality for hierarchy membership, with SYSTEM normalization.
Enums
- enum HierarchyError#
- Io(String, std::io::Error)#
- Parse(String, String)#
- UnknownToolchain(String, String)#
- MissingDep(String, String, String, String)#
Structs and Unions
- struct ResolveDepOpts<'a>#
Options controlling safe hierarchy dependency resolution.
- floor_version: Option<&'a str>#
Never return a version older than this floor (source recipe version).
- versionsuffix: Option<&'a str>#
When set, only candidates with this versionsuffix match. When the source pins a non-empty versionsuffix, callers should typically not bump the dep at all (see
resolve_dep_versions_for_specs).
- use_consensus: bool#
When true (generation bump), prefer generation-consensus version selection among eligible install candidates (see
resolve_dep_version_in_hierarchy_opts). Default false preserves prefer_newer among ranks when no floor is set.
- struct SourceDepSpec#
One dependency as scraped from a source recipe (name + version + optional suffix).
- name: String#
- version: String#
- versionsuffix: Option<String>#
- system_toolchain: bool#
4th tuple element is EasyBuild
SYSTEM(pseudo-external / binary pin).
- optional: bool#
Trailing same-line comment marks an optional extra (e.g.
# optional). Soft-unresolved only — does not freeze the pin when candidates exist.
Implementations
- impl SourceDepSpec#
Functions
- fn is_preserve_pin(&self) -> bool#
Whether package bump planning must leave the source version untouched: SYSTEM 4th-tuple or non-empty versionsuffix.
A
# optionalcomment marks the dependency optional to include, not frozen. Optional deps still bump when hierarchy candidates exist (soft-keep the source pin only if unresolved — no hard ERROR).
- fn plain(name: impl Into<String>, version: impl Into<String>) -> Self#
Convenience constructor for tests / simple name+version pins.
- struct ToolchainHierarchy#
Ordered sub-toolchain hierarchy for one parent toolchain generation.
Member order matches EasyBuild: most minimal first, parent last (e.g.
system,GCCcore,GCC,gfbf,gompi,foss).Implementations
- impl ToolchainHierarchy#
Functions
- fn member_labels(&self) -> Vec<String>#
Labels
name-versionfor each member (system empty version →system).