do not edit — generated by btf.
git.druid.rocksindexdruid520mpsrc/mplib/hooks.pm

src/mplib/hooks.pm


use v5.16;
package mplib::hooks;
use strict;
use warnings FATAL => 'all';
use Exporter 'import';
use FindBin;
use mplib::config qw($CFG read_conf);
use mplib::util qw(note fail pconf_lookup pconf_section order_manifest delta_or_literal);
use mplib::portformat qw(enabled_use parse_pkg_use);
 
our @EXPORT_OK = qw(port_env_args run_hook);
 
# $pkg_use, if passed, is the CURRENT port's own raw pkg_use string (e.g.
# $pc->{pkg_use}) -- lets a "+flag"-default-on USE flag (see
# mplib::portformat::enabled_use) still get its USE_<flag>=1 exported to
# the phase script even with no per-package or global USE= configured at
# all. omitted (every call site that predates default-on flags), nothing
# changes: enabled_use falls back to its pre-existing empty-string
# default exactly as before.
sub port_env_args {
	my ($cur, $pkg_use) = @_;
	my @args;
	# pconf_section (mplib::util): merges the bare name's section under
	# the canon-specific one (specific wins key-by-key) -- a
	# pkg_slot_use-composed canon (e.g. "combodep:foo") doesn't exist yet
	# when a user writes a per-package section, so the bare name is the
	# only key they could plausibly have used. shared with
	# mplib::resolve::effective_deps' identical DEPS/DEPS+/DEPS- need,
	# instead of two copies of the same merge.
	my %sec = %{pconf_section($cur)};
 
	# lets mpx look up a "<name>:" section of its own (mpx.conf) for a
	# per-package SHELL/TARGET_*/JOBS/ARCH/MICROARCH/SANDBOX override,
	# mirroring mp.conf's per-package sections.
	push @args, "MP_PKG=$cur" if $cur;
	# lets a phase script fire an arbitrary, port-author-chosen hook point
	# mid-phase ("$MP_HOOK my-custom-step") -- not just the fixed set of
	# pre_/post_<phase> boundaries run_hook is otherwise only ever called
	# for -- by exec'ing mp.hook (src/hook.pl), a thin wrapper around the
	# exact same run_hook() this file already implements, so a custom
	# event name gets the identical hook_<name>=/HOOKS_DIR/<name>/*
	# (with the same hooks.conf pipeline) as any built-in phase.
	push @args, "MP_HOOK=$FindBin::Bin/mp.hook";
	# mp.hook (a SEPARATE process invocation, re-running
	# mplib::config::load from scratch) has no other way to learn that
	# the phase script calling it is running inside a with_sysroot_scope
	# callback -- without this, a custom hook fired via $MP_HOOK from a
	# sysroot-scoped phase would silently resolve against the DEFAULT
	# root's mp.conf/HOOKS_DIR instead of the active sysroot's.
	push @args, "MP_SYSROOT=$CFG->{SYSROOT}" if defined $CFG->{SYSROOT} && $CFG->{SYSROOT} ne '';
	# lets mpx (runsh.c's cmd_runsh) read a per-sysroot mpx.conf instead
	# of the single global /etc/mpx.conf -- every other WD-derived
	# concept (db/manifest/HOOKSDIR) already gets this same isolation,
	# but mpx.conf's TARGET_CC/JOBS/MICROARCH/SANDBOX overrides were
	# still silently shared by every sysroot and the default root alike.
	# $CFG->{WD} is already the swapped, sysroot-specific path by the
	# time with_sysroot_scope's callback runs, so this needs no extra
	# path construction -- a sysroot with no mpx.conf override of its
	# own just falls back to the global file, same as always.
	push @args, "MP_MPXCONF=$CFG->{WD}/mpx.conf" if defined $CFG->{SYSROOT} && $CFG->{SYSROOT} ne '';
 
	# a global (unindented) "ENV=" line in mp.conf -- mirrors the per-
	# package "ENV=" handling just below, but applies to every port build
	# instead of one. pushed BEFORE the per-package section's own args
	# (including its own ENV=, if any) so a same-named per-package
	# override still wins: runsh.c's generic "KEY=VALUE" trailing-arg
	# handling is a plain setenv(..., 1) replace, not an append, so
	# whichever entry for a given name comes LAST in @args is the one
	# that sticks. added because this system's own static-pie build
	# policy (CFLAGS/CXXFLAGS/LDFLAGS/CPATH/CONFIG_SITE/GIT_EXEC_PATH,
	# see /etc/mpx.env) had been carried as ad hoc "export ..." lines
	# retyped by hand before nearly every "mp update"/"mp reinstall"
	# invocation instead of persisted anywhere -- fragile, and easy to
	# forget on any one invocation (both hit for real this session).
	# /etc/mpx.env (sourced directly by runsh.c's own wrapper shell,
	# see its own comment) already covers "the same env for literally
	# every build, no per-package awareness needed, no dependency on
	# mp.conf parsing at all" -- this is for the complementary case: a
	# global default that still needs to compose with mp.conf's own
	# per-package sections/PROFILE=/OVERLAYS= precedence chain, the way
	# every other mp.conf key already does.
	if(defined $CFG->{conf}{ENV} && $CFG->{conf}{ENV} ne '')
	{
		push @args, grep { /\S/ } split ' ', $CFG->{conf}{ENV};
	}
 
	if(%sec)
	{
		for my $k (sort keys %sec)
		{
			next if $k =~ /^(?:TAG|TARGET)_/;
			next if $k =~ /^(?:DEPS|HOLD|GLOBAL_DEPS|hook_)/;
			next if $k eq 'USE';
			next if $k eq 'SYSROOT';
			if($k eq 'ENV')
			{
				push @args, grep { /\S/ } split ' ', $sec{$k};
				next;
			}
			push @args, "$k=$sec{$k}";
		}
	}
	# export one USE_<flag>=1 per enabled flag so build scripts can branch,
	# plus the whole space-separated USE set in one variable.
	my $en = enabled_use($cur, $pkg_use ? parse_pkg_use($pkg_use) : undef);
	if(%$en)
	{
		push @args, "USE=" . join(' ', sort keys %$en);
		for my $f (sort keys %$en)
		{
			push @args, "USE_$f=1";
		}
	}
	return @args;
}
 
# paludis-style hook group for one phase: every hook runs, in order, and
# any one of them exiting nonzero vetoes the whole operation (fail()).
# four sources, all optional and all usable together:
#   hook_<phase>=  in mp.conf (global, or inside a "<name>:" section) -- one
#                  inline shell command, quick and simple.
#   HOOKS_DIR/<phase>/*  -- any executable file there runs directly, sorted
#                  by name, letting several independent hooks share a phase
#                  (e.g. HOOKS_DIR/post_install/10-logger, 20-notify).
#   HOOKS_DIR/<phase>/hooks.conf -- optional declarative pipeline over that
#                  same file group, same section grammar (and the same
#                  order_manifest engine, mplib::util) as patches/
#                  patches.conf: "IF_USE=flag !otherflag" (mplib::portformat::
#                  enabled_use -- $canon's enabled USE flags, or the global
#                  USE= for a canon-less hook like pre_sync/post_sync) skips
#                  a hook file entirely, "AFTER=<file> <file>" reorders among
#                  the files actually running. filename sort order (today's
#                  only ordering) is still the default/tiebreak with no
#                  hooks.conf, or for any file it doesn't mention.
#   HOOKS_<phase>= in mp.conf (global, or per-package, same fallback as
#                  hook_<phase>) -- a manual override on top of the
#                  IF_USE-computed set above, same +/- delta convention
#                  as USE=/"mp use": a bare list is the complete, explicit
#                  set of filenames to run (regardless of IF_USE); a
#                  +name/-name delta is applied atop the IF_USE baseline,
#                  so "-20-notify" disables one hook file without
#                  deleting it or touching hooks.conf, "+30-extra-check"
#                  forces one back on despite its own IF_USE. no dedicated
#                  command persists this today -- hand-edit mp.conf.
sub run_hook {
	my ($phase, $canon) = @_;
	my $key = "hook_$phase";
	my @runs;
 
	push @runs, ["config: $CFG->{conf}{$key}", $CFG->{SHELL}, '-c', $CFG->{conf}{$key}] if $CFG->{conf}{$key};
	if($canon)
	{
		# bare-name fallback (pconf_lookup, mplib::util): a
		# pkg_slot_use-composed canon doesn't exist yet when a hook_<phase>
		# is written under a port's bare name, so that's the only key it
		# could plausibly be configured under.
		my $val = pconf_lookup($canon, $key);
		push @runs, ["config[$canon]: $val", $CFG->{SHELL}, '-c', $val] if defined $val;
	}
	my $dir = "$CFG->{HOOKSDIR}/$phase";
	if(opendir(my $dh, $dir))
	{
		my @cand;
		for my $f (sort readdir($dh))
		{
			next if $f =~ /^\./ || $f eq 'hooks.conf';
			my $path = "$dir/$f";
			push @cand, $f if -f $path && -x _;
		}
		closedir($dh);
		if(@cand)
		{
			# no $declared here (run_hook only ever gets a bare $canon, not
			# the port's own pkg_use -- fixing that would need this file to
			# resolve the port itself, risking a real circular import with
			# mplib::resolve, which already imports FROM here): a "+flag"
			# default-on USE flag (mplib::portformat::enabled_use) won't
			# gate a hook's own IF_USE correctly unless a real per-package
			# or global USE= has been set at least once. every OTHER
			# default-on consumer (phase-script env export, dep/patch/slot
			# resolution) doesn't have this gap.
			my $enabled = enabled_use($canon);
			my $ifuse_ok = sub {
				my ($sec) = @_;
				return 1 unless $sec;
				for my $tok (split ' ', $sec->{IF_USE} || '')
				{
					my $wantit = ($tok =~ s/^!//) ? 0 : 1;
					return 0 if (($enabled->{$tok} ? 1 : 0) != $wantit);
				}
				return 1;
			};
			# HOOKS_<phase>: read straight from @cand + hooks.conf's own
			# sections -- a second, small read (order_manifest reads the
			# same file again below for its own AFTER-ordering purposes),
			# same precedent as apply_patch_manifest's DEPS+= pass
			# (mplib::resolve): hooks.conf is small and this runs once per
			# phase, not per file, and avoids changing order_manifest's
			# shared $want contract for its other caller.
			my $hooksconf = "$dir/hooks.conf";
			my $hsec = {};
			(undef, $hsec) = read_conf($hooksconf) if -f $hooksconf;
			my $baseline = join(' ', grep { $ifuse_ok->($hsec->{$_}) } @cand);
			my $hkey = "HOOKS_$phase";
			my $hraw = $canon ? pconf_lookup($canon, $hkey) : undef;
			$hraw = $CFG->{conf}{$hkey} unless defined $hraw;
			my %active = map { $_ => 1 } grep { /\S/ } split ' ', (defined $hraw
			    ? delta_or_literal($hraw, $baseline, $canon ? "$hkey for '$canon'" : "global $hkey")
			    : $baseline);
			my @filtered = grep { $active{$_} } @cand;
			push @runs, ["$dir/$_", "$dir/$_"] for order_manifest(\@filtered, $hooksconf, sub { 1 });
		}
	}
	return unless @runs;
 
	my %saved;
	my $savedpkg = $ENV{MP_PKG};
	$ENV{MP_PKG} = $canon if $canon;
	if($canon)
	{
		for my $arg (port_env_args($canon))
		{
			my ($k, $v) = split /=/, $arg, 2;
			$saved{$k} = $ENV{$k};
			$ENV{$k} = $v;
		}
	}
	for my $r (@runs)
	{
		my ($desc, @exec) = @$r;
		note("hook $phase" . (defined $canon ? "[$canon]" : '') . ": $desc");
		system(@exec) == 0 or fail("hook failed ($phase): $desc");
	}
	for my $k (keys %saved)
	{
		defined $saved{$k} ? ($ENV{$k} = $saved{$k}) : delete $ENV{$k};
	}
	defined $savedpkg ? ($ENV{MP_PKG} = $savedpkg) : delete $ENV{MP_PKG};
	return;
}
 
1;
powered by btf.