Skip to content

Commit

Permalink
{makeSetupHook',sourceGuard}: init
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorBaker committed Mar 10, 2025
1 parent 6af4687 commit 995f763
Show file tree
Hide file tree
Showing 11 changed files with 389 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/build-helpers/special.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This chapter describes several special build helpers.
special/fakenss.section.md
special/fhs-environments.section.md
special/makesetuphook.section.md
special/makeSetupHookPrime.section.md
special/mkshell.section.md
special/vm-tools.section.md
special/checkpoint-build.section.md
Expand Down
83 changes: 83 additions & 0 deletions doc/build-helpers/special/makeSetupHookPrime.section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# makeSetupHook' {#build-helpers-special-makeSetupHookPrime}

`makeSetupHook'` is a build helper which produces hooks which may be added to a derivation's `nativeBuildInputs`.

This helper differs from `makeSetupHook`[#sec-pkgs.makesetuphook] in several ways:

- it uses `pkgs.replaceVars` instead of the bash function `substituteAll`
- it uses `sourceGuard`[#setup-hook-sourceGuard] to source the script
- `strictDeps` is set to `true`
- script dependencies are provided using the `nativeBuildInputs` and `buildInputs` arguments rather than `propagatedBuildInputs` and `depsTargetTargetPropagated`
- the script argument must be a path and is interpolated into a string, causing Nix to create a store path for only it, enforcing isolation


:::{.example #ex-makeSetupHookPrime-doc-example}

# Usage example of makeSetupHook'

Re-using the example from [`makeSetupHook`](#sec-pkgs.makeSetupHook-usage-example):

```nix
pkgs.makeSetupHook' {
name = "run-hello-hook";
script = writeScript "run-hello-hook.sh" ''
#!@shell@
# the direct path to the executable has to be here because
# this will be run when the file is sourced
# at which point '$PATH' has not yet been populated with inputs
@cowsay@ cow
_printHelloHook() {
hello
}
preConfigureHooks+=(_printHelloHook)
'';
nativeBuildInputs = [
pkgs.cowsay
pkgs.hello
];
replacements = {
cowsay = lib.getExe pkgs.cowsay;
shell = lib.getExe pkgs.bash;
};
}
```

:::

## Inputs {#build-helpers-special-makeSetupHookPrime-inputs}

`name` (string)

: The name of the hook.
When `useSourceGuard` is enabled, `name` is used as the `guardName` argument to `sourceGuard`.

`script` (path-like)

: The derivation or store path to make into a hook.
The script path is interpolated into a string, causing Nix to create a store path for only it, enforcing isolation.
Values in the script may be replaced using the `replacements` argument.

`nativeBuildInputs` (array of path-like values, optional)
: A list of derivations or store paths which should be added to the `nativeBuildInputs` of derivations which include this hook in their `nativeBuildInputs`.
When not provided, this value defaults to `[ ]`.

`buildInputs` (array of path-like values, optional)
: A list of derivations or store paths which should be added to the `buildInputs` of derivations which include this hook in their `nativeBuildInputs`.
When not provided, this value defaults to `[ ]`.

`useSourceGuard` (boolean, optional)
: Whether to use `sourceGuard`[#setup-hook-sourceGuard] to source the hook.
When not provided, this value defaults to `true`.

`replacements` (attribute set of string-like values, optional)
: A map of string-like values which are used to replace variables in `script`.
When not provided, this value defaults to `{ }`.

`passthru` (attribute set, optional)
: A map of values which are passed to the `passthru` attribute of the hook derivation.
When not provided, this value defaults to `{ }`.

`meta` (attribute set, optional)
: A map of values which are passed to the `meta` attribute of the hook derivation.
When not provided, this value defaults to `{ }`.
1 change: 1 addition & 0 deletions doc/hooks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ postgresql-test-hook.section.md
premake.section.md
python.section.md
scons.section.md
sourceGuard.section.md
tauri.section.md
tetex-tex-live.section.md
unzip.section.md
Expand Down
8 changes: 8 additions & 0 deletions doc/hooks/sourceGuard.section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# sourceGuard {#setup-hook-sourceGuard}

This hook provides the `sourceGuard` bash function.

Using `sourceGuard` to source scripts ensures two things:

- the script is only sourced if it is a build-time dependency (which is to say it has a `hostOffset` of `-1`)
- the script is only sourced once, even if `sourceGuard` is called multiple times
12 changes: 12 additions & 0 deletions doc/redirects.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"build-helpers-special-makeSetupHookPrime": [
"index.html#build-helpers-special-makeSetupHookPrime"
],
"build-helpers-special-makeSetupHookPrime-inputs": [
"index.html#build-helpers-special-makeSetupHookPrime-inputs"
],
"chap-build-helpers-finalAttrs": [
"index.html#chap-build-helpers-finalAttrs"
],
Expand All @@ -11,6 +17,9 @@
"ex-shfmt": [
"index.html#ex-shfmt"
],
"ex-makeSetupHookPrime-doc-example": [
"index.html#ex-makeSetupHookPrime-doc-example"
],
"ex-testBuildFailurePrime-doc-example": [
"index.html#ex-testBuildFailurePrime-doc-example"
],
Expand Down Expand Up @@ -317,6 +326,9 @@
"sec-tools-of-stdenv": [
"index.html#sec-tools-of-stdenv"
],
"setup-hook-sourceGuard": [
"index.html#setup-hook-sourceGuard"
],
"ssec-stdenv-dependencies": [
"index.html#ssec-stdenv-dependencies"
],
Expand Down
96 changes: 96 additions & 0 deletions pkgs/by-name/ma/makeSetupHook'/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
lib,
replaceVarsWith,
sourceGuard,
stdenvNoCC,
testers,
writeTextFile,
}:
# Docs in doc/build-helpers/special/makeSetupHookPrime.section.md
# See https://nixos.org/manual/nixpkgs/unstable/#build-helpers-special-makeSetupHookPrime
lib.makeOverridable (
{
name,
script,
nativeBuildInputs ? [ ],
buildInputs ? [ ],
useSourceGuard ? true,
replacements ? { },
passthru ? { },
meta ? { },
}:
# NOTE: To enforce isolation, interpolating the path in `script` causes Nix to copy the file to its own store path,
# containing nothing else.
assert lib.assertMsg (lib.isPath script) "makeSetupHook': script must be a path";
let
templatedScriptName = if replacements == { } then name else "templated-${name}";
templatedScript =
if replacements == { } then
"${script}"
else
replaceVarsWith {
# Boilerplate
__structuredAttrs = true;
strictDeps = true;

name = templatedScriptName;
src = "${script}";
inherit replacements;
};
in
stdenvNoCC.mkDerivation {
# Boilerplate
__structuredAttrs = true;
allowSubstitutes = false;
preferLocalBuild = true;
strictDeps = true;

inherit name meta;

src = null;
dontUnpack = true;

# Perhaps due to the order in which Nix loads dependencies (current node, then dependencies), we need to add sourceGuard
# as a dependency in with a slightly earlier dependency offset.
# Adding sourceGuard to `propagatedBuildInputs` causes our `setupHook` to fail to run with a `sourceGuard: command not found`
# error.
# See https://github.com/NixOS/nixpkgs/pull/31414.
depsHostHostPropagated = lib.optionals useSourceGuard [ sourceGuard ];

# Since we're producing a setup hook which will be used in nativeBuildInputs, all of our dependency propagation is
# understood to be shifted by one to the right -- that is, the script's nativeBuildInputs correspond to this
# derivation's propagatedBuildInputs, and the script's buildInputs correspond to this derivation's
# depsTargetTargetPropagated.
propagatedBuildInputs = nativeBuildInputs;
depsTargetTargetPropagated = buildInputs;

setupHook =
if useSourceGuard then
writeTextFile {
name = "sourceGuard-${templatedScriptName}";
text = ''
sourceGuard ${lib.escapeShellArg name} ${lib.escapeShellArg templatedScript}
'';
derivationArgs = {
# Boilerplate
__structuredAttrs = true;
strictDeps = true;
};
}
else
templatedScript;

passthru = passthru // {
tests = passthru.tests or { } // {
shellcheck = testers.shellcheck {
name = templatedScriptName;
src = templatedScript;
};
shfmt = testers.shfmt {
name = templatedScriptName;
src = templatedScript;
};
};
};
}
)
4 changes: 4 additions & 0 deletions pkgs/by-name/ma/makeSetupHook'/tests.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{ lib }:
lib.recurseIntoAttrs {
# TODO(@connorbaker)
}
17 changes: 17 additions & 0 deletions pkgs/by-name/so/sourceGuard/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
callPackages,
lib,
makeSetupHook',
}:
# Docs in doc/hooks/sourceGuard.section.md
# See https://nixos.org/manual/nixpkgs/unstable/#setup-hook-sourceGuard
makeSetupHook' {
name = "sourceGuard";
script = ./sourceGuard.bash;
useSourceGuard = false; # Avoid self-reference
passthru.tests = callPackages ./tests.nix { };
meta = {
description = "Ensure files are sourced at most once and are build-time dependencies";
maintainers = [ lib.maintainers.connorbaker ];
};
}
Loading

0 comments on commit 995f763

Please sign in to comment.