makeWrapper: --add-flag and --append-flag arguments (#400649)

Changed files
+97 -41
doc
pkgs
build-support
setup-hooks
by-name
ma
makeBinaryWrapper
test
make-binary-wrapper
make-wrapper
+4 -1
doc/stdenv/stdenv.chapter.md
···
If dependencies should be resolved at runtime, use `--suffix` to append fallback values to `PATH`.
-
There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
+
There’s many more kinds of arguments, they are documented in `nixpkgs/pkgs/build-support/setup-hooks/make-wrapper.sh` for the `makeWrapper` implementation and in `nixpkgs/pkgs/by-name/ma/makeBinaryWrapper/make-binary-wrapper.sh` for the `makeBinaryWrapper` implementation.
`wrapProgram` is a convenience function you probably want to use most of the time, implemented by both `makeWrapper` and `makeBinaryWrapper`.
Using the `makeBinaryWrapper` implementation is usually preferred, as it creates a tiny _compiled_ wrapper executable, that can be used as a shebang interpreter. This is needed mostly on Darwin, where shebangs cannot point to scripts, [due to a limitation with the `execve`-syscall](https://stackoverflow.com/questions/67100831/macos-shebang-with-absolute-path-not-working). Compiled wrappers generated by `makeBinaryWrapper` can be inspected with `less <path-to-wrapper>` - by scrolling past the binary data you should be able to see the shell command that generated the executable and there see the environment variables that were injected into the wrapper.
+
+
However, `makeWrapper` is more flexible and implements more arguments.
+
Use `makeWrapper` if you need the wrapper to use shell features (e.g. look up environment variables) at runtime.
### `remove-references-to -t` \<storepath\> [ `-t` \<storepath\> ... ] \<file\> ... {#fun-remove-references-to}
+12 -2
pkgs/build-support/setup-hooks/make-wrapper.sh
···
# --unset VAR : remove VAR from the environment
# --chdir DIR : change working directory (use instead of --run "cd DIR")
# --run COMMAND : run command before the executable
-
# --add-flags ARGS : prepend ARGS to the invocation of the executable
+
# --add-flag ARG : prepend the single argument ARG to the invocation of the executable
# (that is, *before* any arguments passed on the command line)
-
# --append-flags ARGS : append ARGS to the invocation of the executable
+
# --append-flag ARG : append the single argument ARG to the invocation of the executable
# (that is, *after* any arguments passed on the command line)
+
# --add-flags ARGS : prepend ARGS verbatim to the Bash-interpreted invocation of the executable
+
# --append-flags ARGS : append ARGS verbatim to the Bash-interpreted invocation of the executable
# --prefix ENV SEP VAL : suffix/prefix ENV with VAL, separated by SEP
# --suffix
···
contents="$(cat "$fileName")"
addValue "$p" "$varName" "$separator" "$contents"
done
+
elif [[ "$p" == "--add-flag" ]]; then
+
flags=${params[n + 1]@Q}
+
n=$((n + 1))
+
flagsBefore="${flagsBefore-} $flags"
+
elif [[ "$p" == "--append-flag" ]]; then
+
flags=${params[n + 1]@Q}
+
n=$((n + 1))
+
flagsAfter="${flagsAfter-} $flags"
elif [[ "$p" == "--add-flags" ]]; then
flags="${params[$((n + 1))]}"
n=$((n + 1))
+34 -25
pkgs/by-name/ma/makeBinaryWrapper/make-binary-wrapper.sh
···
# the environment
# --unset VAR : remove VAR from the environment
# --chdir DIR : change working directory (use instead of --run "cd DIR")
-
# --add-flags ARGS : prepend ARGS to the invocation of the executable
+
# --add-flag ARG : prepend the single argument ARG to the invocation of the executable
# (that is, *before* any arguments passed on the command line)
-
# --append-flags ARGS : append ARGS to the invocation of the executable
+
# --append-flag ARG : append the single argument ARG to the invocation of the executable
# (that is, *after* any arguments passed on the command line)
+
# --add-flags ARGS : prepend the whitespace-separated list of arguments ARGS to the invocation of the executable
+
# --append-flags ARGS : append the whitespace-separated list of arguments ARGS to the invocation of the executable
# --prefix ENV SEP VAL : suffix/prefix ENV with VAL, separated by SEP
# --suffix
···
# makeCWrapper EXECUTABLE ARGS
# ARGS: same as makeWrapper
makeCWrapper() {
-
local argv0 inherit_argv0 n params cmd main flagsBefore flagsAfter flags executable length
+
local argv0 inherit_argv0 n params cmd main flags executable length
local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf
-
local resolve_path
+
local flagsBefore=() flagsAfter=()
executable=$(escapeStringLiteral "$1")
params=("$@")
length=${#params[*]}
···
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
+
--add-flag)
+
flagsBefore+=("${params[n + 1]}")
+
uses_assert=1
+
n=$((n + 1))
+
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
+
;;
+
--append-flag)
+
flagsAfter+=("${params[n + 1]}")
+
uses_assert=1
+
n=$((n + 1))
+
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
+
;;
--add-flags)
-
flags="${params[n + 1]}"
-
flagsBefore="$flagsBefore $flags"
+
read -ra flags <<< "${params[n + 1]}"
+
flagsBefore+=("${flags[@]}")
uses_assert=1
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
;;
--append-flags)
-
flags="${params[n + 1]}"
-
flagsAfter="$flagsAfter $flags"
+
read -ra flags <<< "${params[n + 1]}"
+
flagsAfter+=("${flags[@]}")
uses_assert=1
n=$((n + 1))
[ $n -ge "$length" ] && main="$main#error makeCWrapper: $p takes 1 argument"$'\n'
···
;;
esac
done
-
[[ -z "$flagsBefore" && -z "$flagsAfter" ]] || main="$main"${main:+$'\n'}$(addFlags "$flagsBefore" "$flagsAfter")$'\n'$'\n'
+
(( ${#flagsBefore[@]} + ${#flagsAfter[@]} > 0 )) && main="$main"${main:+$'\n'}$(addFlags flagsBefore flagsAfter)$'\n'$'\n'
[ -z "$inherit_argv0" ] && main="${main}argv[0] = \"${argv0:-${executable}}\";"$'\n'
[ -z "$resolve_argv0" ] || main="${main}argv[0] = resolve_argv0(argv[0]);"$'\n'
main="${main}return execv(\"${executable}\", argv);"$'\n'
···
}
addFlags() {
-
local n flag before after var
+
local n flag var
-
# Disable file globbing, since bash will otherwise try to find
-
# filenames matching the the value to be prefixed/suffixed if
-
# it contains characters considered wildcards, such as `?` and
-
# `*`. We want the value as is, except we also want to split
-
# it on on the separator; hence we can't quote it.
-
local reenableGlob=0
-
if [[ ! -o noglob ]]; then
-
reenableGlob=1
-
fi
-
set -o noglob
-
# shellcheck disable=SC2086
-
before=($1) after=($2)
-
if (( reenableGlob )); then
-
set +o noglob
-
fi
+
local -n before=$1
+
local -n after=$2
var="argv_tmp"
printf '%s\n' "char **$var = calloc(${#before[@]} + argc + ${#after[@]} + 1, sizeof(*$var));"
···
shift 3
;;
--chdir)
+
formatArgsLine 1 "$@"
+
shift 1
+
;;
+
--add-flag)
+
formatArgsLine 1 "$@"
+
shift 1
+
;;
+
--append-flag)
formatArgsLine 1 "$@"
shift 1
;;
+11 -7
pkgs/test/make-binary-wrapper/add-flags/add-flags.c
···
#include <assert.h>
int main(int argc, char **argv) {
-
char **argv_tmp = calloc(6 + argc + 2 + 1, sizeof(*argv_tmp));
+
char **argv_tmp = calloc(9 + argc + 3 + 1, sizeof(*argv_tmp));
assert(argv_tmp != NULL);
argv_tmp[0] = argv[0];
argv_tmp[1] = "-x";
argv_tmp[2] = "-y";
argv_tmp[3] = "-z";
argv_tmp[4] = "-abc";
-
argv_tmp[5] = "-g";
-
argv_tmp[6] = "*.txt";
+
argv_tmp[5] = "test var here";
+
argv_tmp[6] = "-g";
+
argv_tmp[7] = "*.txt";
+
argv_tmp[8] = "-a";
+
argv_tmp[9] = "*";
for (int i = 1; i < argc; ++i) {
-
argv_tmp[6 + i] = argv[i];
+
argv_tmp[9 + i] = argv[i];
}
-
argv_tmp[6 + argc + 0] = "-foo";
-
argv_tmp[6 + argc + 1] = "-bar";
-
argv_tmp[6 + argc + 2] = NULL;
+
argv_tmp[9 + argc + 0] = "-foo";
+
argv_tmp[9 + argc + 1] = "-bar";
+
argv_tmp[9 + argc + 2] = "test var 2 here";
+
argv_tmp[9 + argc + 3] = NULL;
argv = argv_tmp;
argv[0] = "/send/me/flags";
+4 -1
pkgs/test/make-binary-wrapper/add-flags/add-flags.cmdline
···
--append-flags "-foo -bar" \
--add-flags "-x -y -z" \
--add-flags -abc \
-
--add-flags "-g *.txt"
+
--add-flag 'test var here' \
+
--add-flags "-g *.txt" \
+
--add-flags "-a *" \
+
--append-flag 'test var 2 here'
+4
pkgs/test/make-binary-wrapper/add-flags/add-flags.env
···
-y
-z
-abc
+
test var here
-g
*.txt
+
-a
+
*
-foo
-bar
+
test var 2 here
+28 -5
pkgs/test/make-wrapper/default.nix
···
echo "VAR=$VAR"
'';
wrappedBinaryArgs = writeShellScript "wrapped-args" ''
-
echo "$@"
+
printf '%s\n' "$@"
'';
mkWrapperBinary =
···
makeWrapper "${wrapped}" "$out/bin/${name}" ${lib.escapeShellArgs args}
'';
+
mkArgTest = cmd: toExpect: mkTest cmd (builtins.concatStringsSep "\n" toExpect);
mkTest = cmd: toExpect: ''
output="$(${cmd})"
-
if [[ "$output" != '${toExpect}' ]]; then
-
echo "test failed: the output of ${cmd} was '$output', expected '${toExpect}'"
+
if [[ "$output" != ${lib.escapeShellArg toExpect} ]]; then
+
echo "test failed: the output of ${cmd} was '$output', expected ${lib.escapeShellArg toExpect}"
echo "the wrapper contents:"
for i in ${cmd}; do
if [[ $i =~ ^test- ]]; then
···
"abc"
"--append-flags"
"xyz"
+
];
+
wrapped = wrappedBinaryArgs;
+
})
+
(mkWrapperBinary {
+
name = "test-arg";
+
args = [
+
"--add-flag"
+
"abc 'aaaaa' jkhhjk"
+
"--append-flag"
+
"xyz ggg"
];
wrapped = wrappedBinaryArgs;
})
···
+ mkTest "VAR=foo test-unset" "VAR="
# --add-flags and --append-flags work
-
+ mkTest "test-args" "abc xyz"
+
+ mkArgTest "test-args" [
+
"abc"
+
"xyz"
+
]
+
# --add-flag and --append-flag work
+
+ mkArgTest "test-arg" [
+
"abc 'aaaaa' jkhhjk"
+
"xyz ggg"
+
]
# given flags are kept
-
+ mkTest "test-args foo" "abc foo xyz"
+
+ mkArgTest "test-args foo" [
+
"abc"
+
"foo"
+
"xyz"
+
]
# --run works
+ mkTest "test-run" "bar\nVAR="