4402c363b6
runExecline is a primitive that just does not care. It’s similar to `runCommand`, but instead of concatenating bash scripts left and right, it actually *uses* the features of `derivation`, passing things to `args` and making it possible to overwrite the `builder` in a sensible manner. Additionally, it provides a way to pass a nix string to `stdin` of the build script. Similar to `writeExecline`, the passed script is not a string, but a nested list of nix lists representing execline blocks. Escaping is done by the implementation, the user can just use normal nix strings. Change-Id: I890d9e5d921207751cdc8cc4309381395d92742f Reviewed-on: https://cl.tvl.fyi/c/depot/+/701 Reviewed-by: BuildkiteCI Reviewed-by: isomer <isomer@tvl.fyi> Reviewed-by: tazjin <mail@tazj.in> Tested-by: BuildkiteCI
121 lines
3.4 KiB
Nix
121 lines
3.4 KiB
Nix
{ pkgs, stdenv, lib, getBins, escapeExecline }:
|
||
|
||
# runExecline is a primitive building block
|
||
# for writing non-kitchen sink builders.
|
||
#
|
||
# It’s conceptually similar to `runCommand`,
|
||
# but instead of concatenating bash scripts left
|
||
# and right, it actually *uses* the features of
|
||
# `derivation`, passing things to `args`
|
||
# and making it possible to overwrite the `builder`
|
||
# in a sensible manner.
|
||
#
|
||
# Additionally, it provides a way to pass a nix string
|
||
# to `stdin` of the build script.
|
||
#
|
||
# Similar to //nix/writeExecline, the passed script is
|
||
# not a string, but a nested list of nix lists
|
||
# representing execline blocks. Escaping is
|
||
# done by the implementation, the user can just use
|
||
# normal nix strings.
|
||
#
|
||
# Example:
|
||
#
|
||
# runExecline "my-drv" { stdin = "hi!"; } [
|
||
# "importas" "out" "out"
|
||
# # this pipes stdout of s6-cat to $out
|
||
# # and s6-cat redirects from stdin to stdout
|
||
# "redirfd" "-w" "1" "$out" bins.s6-cat
|
||
# ]
|
||
#
|
||
# which creates a derivation with "hi!" in $out.
|
||
#
|
||
# See ./tests.nix for more examples.
|
||
|
||
|
||
let
|
||
bins = getBins pkgs.execline [
|
||
"execlineb"
|
||
{ use = "if"; as = "execlineIf"; }
|
||
"redirfd"
|
||
"importas"
|
||
"exec"
|
||
]
|
||
// getBins pkgs.s6-portable-utils [
|
||
"s6-cat"
|
||
"s6-grep"
|
||
"s6-touch"
|
||
"s6-test"
|
||
"s6-chmod"
|
||
];
|
||
|
||
in
|
||
|
||
name:
|
||
{
|
||
# a string to pass as stdin to the execline script
|
||
stdin ? ""
|
||
# a program wrapping the acutal execline invocation;
|
||
# should be in Bernstein-chaining style
|
||
, builderWrapper ? bins.exec
|
||
# additional arguments to pass to the derivation
|
||
, derivationArgs ? {}
|
||
}:
|
||
# the execline script as a nested list of string,
|
||
# representing the blocks;
|
||
# see docs of `escapeExecline`.
|
||
execline:
|
||
|
||
# those arguments can’t be overwritten
|
||
assert !derivationArgs ? system;
|
||
assert !derivationArgs ? name;
|
||
assert !derivationArgs ? builder;
|
||
assert !derivationArgs ? args;
|
||
|
||
derivation (derivationArgs // {
|
||
# TODO(Profpatsch): what about cross?
|
||
inherit (stdenv) system;
|
||
inherit name;
|
||
|
||
# okay, `builtins.toFile` does not accept strings
|
||
# that reference drv outputs. This means we need
|
||
# to pass the script and stdin as envvar;
|
||
# this might clash with another passed envar,
|
||
# so we give it a long & unique name
|
||
_runExeclineScript =
|
||
let
|
||
in escapeExecline execline;
|
||
_runExeclineStdin = stdin;
|
||
passAsFile = [
|
||
"_runExeclineScript"
|
||
"_runExeclineStdin"
|
||
] ++ derivationArgs.passAsFile or [];
|
||
|
||
# the default, exec acts as identity executable
|
||
builder = builderWrapper;
|
||
|
||
args = [
|
||
bins.importas # import script file as $script
|
||
"-ui" # drop the envvar afterwards
|
||
"script" # substitution name
|
||
"_runExeclineScriptPath" # passed script file
|
||
|
||
bins.importas # do the same for $stdin
|
||
"-ui"
|
||
"stdin"
|
||
"_runExeclineStdinPath"
|
||
|
||
bins.redirfd # now we
|
||
"-r" # read the file
|
||
"0" # into the stdin of execlineb
|
||
"$stdin" # that was given via stdin
|
||
|
||
bins.execlineb # the actual invocation
|
||
# TODO(Profpatsch): depending on the use-case, -S0 might not be enough
|
||
# in all use-cases, then a wrapper for execlineb arguments
|
||
# should be added (-P, -S, -s).
|
||
"-S0" # set $@ inside the execline script
|
||
"-W" # die on syntax error
|
||
"$script" # substituted by importas
|
||
];
|
||
})
|