do not edit — generated by btf.
git.druid.rocksindexdruid520mpdocs/reference/hooks.btft

docs/reference/hooks.btft


[table class="topnav"]
[tr]
[td class="logotab"]mp[e]
[td][see name="index"]index[e][e]
[td][see name="guide/index"]guide[e][e]
[td][see name="reference/index"]reference[e][e]
[e]
[e]
 
[h1]hooks[e]
 
run_hook() (mplib::hooks.pm) is the single code path behind every hook mp ever fires -- a plain hook_<phase>= config key, every executable file in a phase's directory, and a port-author's own custom $MP_HOOK event, all end up here.
 
[h2]what fires, and when[e]
 
every phase (fetch, patch, build, check, install) gets a pre_ and post_ pair. install and remove get their own whole-operation pre_install/post_install and pre_remove/post_remove (fired once per package, not once per phase -- install_pkg_body explicitly skips firing pre_install/post_install a second time around the install: phase itself, since that would double-fire the same boundary). sync gets pre_sync/post_sync, with no $canon (there's no one package), so a canon-less hook checks the global USE= instead of any per-package state. on_fail fires, best-effort, when an install is rejected for conflicting with another installed package's files -- specifically scoped to that one rejection path, not a catch-all for every possible failure, since most failures already terminate through mplib::util::fail() with no single choke point left to hook without a much larger restructuring.
 
[quote class="note"]
note: on_fail itself runs inside try_soft (mplib::util) -- a broken on_fail hook can never mask the real error that triggered it, it just fails silently and the original rejection message still reaches the user.
[e]
 
[h2]three ways to hook one phase, freely combined[e]
 
[code]
# /etc/mp.conf:
hook_post_install='logger installing $MP_PKG'
 
# $WD/hooks/post_install/10-notify (any executable file):
#!/bin/sh
echo "installed: $MP_PKG"
[e]
 
a plain hook_<phase>= config key (global, or per-package inside a "<name>:" section) runs once, through $SHELL -c. every executable file in HOOKS_DIR/<phase>/ (HOOKS_DIR defaults to $WD/hooks) runs too, in filename order unless a sibling hooks.conf reorders/filters them (see reference/config.html's own hooks.conf summary). both mechanisms fire together for the same phase if both are configured -- there's no exclusivity between them.
 
[h2]custom events via $MP_HOOK[e]
 
a phase script isn't limited to the fixed pre_/post_<phase> set above. $MP_HOOK, exported to every phase script, is the path to a small helper (src/hook.pl) that fires any event name a port author picks:
 
[code]
# from a build: phase --
$MP_HOOK post-configure
[e]
 
this runs through the EXACT same run_hook() machinery as any built-in phase: hook_post-configure= in mp.conf, HOOKS_DIR/post-configure/*, all of it. $MP_HOOK is a separate process invocation (it reloads config from scratch, since it has no other way to receive it), which is also why it needs to be told explicitly when it's running inside a sysroot-scoped phase -- see the MP_SYSROOT env var passed alongside it, next.
 
one restriction: a custom name colliding with a real built-in lifecycle phase (pre_install, on_fail, etc.) is refused. that's almost always a typo, not something a port author actually wants -- it would otherwise silently re-fire that built-in hook a second time mid-build instead of running a genuinely new one.
 
a $MP_HOOK fired from inside a phase script inherits that phase's own sandbox (mpx's default for a new-format non-fetch phase: root read-only except the stage dir and $MP_PREFIX, see reference/architecture.btft) -- a hook script triggered this way can only write under one of those two, same as the phase script that fired it. a plain pre_/post_<phase> lifecycle hook is different: run_hook() invokes it directly (a Perl-side call, never through mpx), so it's never sandboxed regardless of which phase it's attached to.
 
[h2]sysroot isolation[e]
 
a sysroot-scoped package gets its own, fully independent hooks tree at $WD/sysroots/<name>/hooks -- a hook configured for the default root's HOOKS_DIR never fires for a sysroot-scoped install, and vice versa. this required its own fix: HOOKSDIR is derived from WD at config-load time, and with_sysroot_scope's whole-config swap re-derives it fresh for the sysroot the same way it re-derives DBFILE/MANDIR, rather than leaving it silently pinned to the default root's value for every sysroot-scoped install.
 
[h2]what a hook actually receives[e]
 
port_env_args() builds the environment every hook (and every phase script) sees: MP_PKG (the full, possibly slot-qualified canon), MP_HOOK (the path described above), MP_SYSROOT (when scoped), plus every key from the port's own per-package config section (pconf_section -- same bare-name fallback as everywhere else a slotted canon's config is looked up, since "curl:ssl" doesn't exist as a section header until curl has actually been resolved with ssl on).
 
[h2]writing a hook that only runs conditionally[e]
 
a plain hook_<phase>= key is unconditional -- if it's configured, it runs. for a hook that should only fire when a USE flag is on (or in a specific order relative to other hooks in the same phase), use HOOKS_DIR/<phase>/hooks.conf instead: same "<name>:" section grammar as everything else in this tree, with IF_USE=<flag> and AFTER=<hookfile> <hookfile>. reference/config.html covers the grammar itself; [see name="reference/patches-conf-example"]patches.conf.example[e] (repo root)'s closing example walks through a hook, a patch, and a dependency all keyed off the same USE flag together.
powered by btf.