#[non_exhaustive]pub struct ExecCtx<'a> {
pub vars: &'a VarEnv,
pub pack_root: &'a Path,
pub workspace: &'a Path,
pub pack: &'a Path,
pub platform: Platform,
pub registry: Option<&'a Arc<Registry>>,
pub pack_type_registry: Option<&'a Arc<PackTypeRegistry>>,
pub visited_meta: Option<&'a MetaVisitedSet>,
pub scheduler: Option<&'a Arc<Scheduler>>,
}Expand description
Read-only context passed to every crate::execute::ActionExecutor::execute call.
Lifetimes are carried through rather than cloning so the planner can run
over a borrowed VarEnv without incurring a copy per action. The ctx is
Copy-cheap in the sense that all fields are &-references — callers
typically pass &ctx rather than cloning.
§Why references, not owned data
Executors are stateless by contract; any “state” lives in the caller’s
driver. Owning data inside ExecCtx would either force clones per
action or require interior mutability — both violate the framework goal
of “future-proof, maximally decoupled”.
Marked #[non_exhaustive] so future slots (plugin registry handle,
scheduler token, teardown hook …) can land without breaking library
consumers who destructure the struct.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.vars: &'a VarEnvVariable lookup table used by every expand_* call.
pack_root: &'a PathPack workdir (the pack’s on-disk root). Relative src paths in
symlink/exec actions resolve against this directory.
workspace: &'a PathWorkspace root (the user’s configured grex workspace). Relative destination paths (though rare — spec encourages absolute) resolve here.
pack: &'a PathPack root sibling of Self::workspace. Additive in v1.3.0:
every constructor populates it with the same value as
Self::workspace. Plugin authors writing new code SHOULD prefer
pack; legacy readers of workspace keep working unchanged.
v2 will remove workspace once the rename has propagated.
platform: PlatformPlatform tag. Defaults to Platform::current but is overridable in
tests to exercise when.os branches deterministically.
registry: Option<&'a Arc<Registry>>Outer Registry handle for plugins that recurse into nested
actions (today: when). Populated by the concrete executors
(FsExecutor, PlanExecutor) right before plugin dispatch so
nested execute calls go through the caller’s registry instead of
a freshly bootstrapped default. None outside an executor-driven
call (e.g. direct plugin invocation in tests) — plugins must treat
absence as “no nested dispatch available” and fall back to their
own bootstrap.
pack_type_registry: Option<&'a Arc<PackTypeRegistry>>Outer PackTypeRegistry handle for pack-type plugins that recurse
across sibling pack types (today: meta dispatching into child
packs of arbitrary type). Populated by the pack-level driver before
invoking crate::plugin::PackTypePlugin methods. None outside a
driver-scoped call — see ExecCtx::registry for the same pattern
at the action level.
Stage B (M5-1B) only exposes the slot; the dispatch swap that actually threads a pack-type registry through the executor chain lands in Stage C.
visited_meta: Option<&'a MetaVisitedSet>Shared cycle-detection set owned by the outer sync driver.
M5-2c: crate::plugin::pack_type::MetaPlugin mutates this set
under a lock at every recursion boundary. Absent (None) means
no outer driver is tracking recursion — MetaPlugin treats that
as “caller promises a single-level dispatch” and skips the check.
The sync driver attaches a fresh empty set at the top of every
install / update / sync run so the first plugin call observes
an empty history. Teardown runs do NOT attach a set:
crate::sync::teardown drives every pack through the
walker’s reverse post-order, so each
crate::plugin::PackTypePlugin::teardown invocation
corresponds to a single pack and has no in-process recursion
to guard. The cycle-detection set stays defense-in-depth for
direct plugin callers (e.g. the meta_recursion integration
tests) that recurse through MetaPlugin::recurse_children.
scheduler: Option<&'a Arc<Scheduler>>Bounded parallel Scheduler handle — feat-m6-1.
Populated by crate::sync::run at the top of every sync run so
plugins that fan out can bound in-flight children via the same
permit pool used by the outer walker. None outside a sync-driven
call (e.g. direct plugin invocation in tests) — plugins that need
to respect the cap must treat absence as “no bound configured”
and fall back to unbounded/serial per their own policy.
feat-m6-1 lands the slot and CLI flag; plugin acquisition sites
land in feat-m6-2 alongside per-pack .grex-lock coordination.
Implementations§
Source§impl<'a> ExecCtx<'a>
impl<'a> ExecCtx<'a>
Sourcepub fn new(vars: &'a VarEnv, pack_root: &'a Path, workspace: &'a Path) -> Self
pub fn new(vars: &'a VarEnv, pack_root: &'a Path, workspace: &'a Path) -> Self
Build a context with platform defaulted to the current target and
no outer registry attached. Executors attach the registry via
ExecCtx::with_registry before invoking plugin dispatch.
Sourcepub fn with_platform(self, p: Platform) -> Self
pub fn with_platform(self, p: Platform) -> Self
Override the platform tag (useful for tests and dry-run overrides).
Sourcepub fn with_registry(self, reg: &'a Arc<Registry>) -> Self
pub fn with_registry(self, reg: &'a Arc<Registry>) -> Self
Attach the outer Registry so plugins that recurse (today:
when) dispatch nested actions through the caller’s registry
instead of a fresh Registry::bootstrap. Used by
crate::execute::FsExecutor and crate::execute::PlanExecutor
just before they hand control to a plugin.
Sourcepub fn with_pack_type_registry(self, reg: &'a Arc<PackTypeRegistry>) -> Self
pub fn with_pack_type_registry(self, reg: &'a Arc<PackTypeRegistry>) -> Self
Attach the outer PackTypeRegistry so pack-type plugins that
recurse across child packs (today: meta) dispatch through the
caller’s registry rather than a fresh
PackTypeRegistry::bootstrap. The dispatch swap that exercises
this slot ships in M5-1 Stage C; Stage B only lands the slot and
the builder method.
Sourcepub fn with_visited_meta(self, visited: &'a MetaVisitedSet) -> Self
pub fn with_visited_meta(self, visited: &'a MetaVisitedSet) -> Self
Attach the shared cycle-detection set used by
crate::plugin::pack_type::MetaPlugin recursion. The sync
driver builds one empty set per run() invocation and threads
it through every ExecCtx it constructs so nested install /
sync / update / teardown calls observe the same history.
Sourcepub fn with_scheduler(self, scheduler: &'a Arc<Scheduler>) -> Self
pub fn with_scheduler(self, scheduler: &'a Arc<Scheduler>) -> Self
Attach a bounded parallel Scheduler handle. The sync driver
builds one Scheduler per run() invocation (permits ==
--parallel N) and threads the same Arc through every
ExecCtx so sibling plugin dispatch shares the permit pool.
feat-m6-1 only plumbs the slot; acquisition sites land in feat-m6-2. Callers may still attach a scheduler today — it is observably inert until the per-pack lock wiring lands.
Trait Implementations§
Auto Trait Implementations§
impl<'a> Freeze for ExecCtx<'a>
impl<'a> !RefUnwindSafe for ExecCtx<'a>
impl<'a> Send for ExecCtx<'a>
impl<'a> Sync for ExecCtx<'a>
impl<'a> Unpin for ExecCtx<'a>
impl<'a> UnsafeUnpin for ExecCtx<'a>
impl<'a> !UnwindSafe for ExecCtx<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more