diff --git a/pkgs/build-support/setup-hooks/make-wrapper.sh b/pkgs/build-support/setup-hooks/make-wrapper.sh index 903e17c3b2e5..604096135c20 100644 --- a/pkgs/build-support/setup-hooks/make-wrapper.sh +++ b/pkgs/build-support/setup-hooks/make-wrapper.sh @@ -36,6 +36,58 @@ makeWrapper() { assertExecutable "$original" + # Write wrapper code which adds `value` to the beginning or end of + # the list variable named by `varName`, depending on the `mode` + # specified. + # + # A value which is already part of the list will not be added + # again. If this is the case and the `suffix` mode is used, the + # list won't be touched at all. The `prefix` mode will however + # move the last matching instance of the value to the beginning + # of the list. Any remaining duplicates of the value will be left + # as-is. + addValue() { + local mode="$1" # `prefix` or `suffix` to add to the beginning or end respectively + local varName="$2" # name of list variable to add to + local separator="$3" # character used to separate elements of list + local value="$4" # one value, or multiple values separated by `separator`, to add to list + if test -n "$value"; then + local old_ifs=$IFS + IFS=$separator + + if [[ "$mode" == '--prefix'* ]]; then + # Keep the order of the components as written when + # prefixing; normally, they would be added in the + # reverse order. + local tmp= + for v in $value; do + tmp=$v${tmp:+$separator}$tmp + done + value="$tmp" + fi + for v in $value; do + { + echo "$varName=\${$varName:+${separator@Q}\$$varName${separator@Q}}" # add separators on both ends unless empty + if [[ "$mode" == '--prefix'* ]]; then # -- in prefix mode -- + echo "$varName=\${$varName/${separator@Q}${v@Q}${separator@Q}/${separator@Q}}" # remove the first instance of the value (if any) + echo "$varName=${v@Q}\$$varName" # prepend the value + elif [[ "$mode" == '--suffix'* ]]; then # -- in suffix mode -- + echo "if [[ \$$varName != *${separator@Q}${v@Q}${separator@Q}* ]]; then" # if the value isn't already in the list + echo " $varName=\$$varName${v@Q}" # append the value + echo "fi" + else + echo "unknown mode $mode!" 1>&2 + exit 1 + fi + echo "$varName=\${$varName#${separator@Q}}" # remove leading separator + echo "$varName=\${$varName%${separator@Q}}" # remove trailing separator + echo "export $varName" + } >> "$wrapper" + done + IFS=$old_ifs + fi + } + mkdir -p "$(dirname "$wrapper")" echo "#! @shell@ -e" > "$wrapper" @@ -67,28 +119,14 @@ makeWrapper() { separator="${params[$((n + 2))]}" value="${params[$((n + 3))]}" n=$((n + 3)) - if test -n "$value"; then - if test "$p" = "--suffix"; then - echo "export $varName=\$$varName\${$varName:+${separator@Q}}${value@Q}" >> "$wrapper" - else - echo "export $varName=${value@Q}\${$varName:+${separator@Q}}\$$varName" >> "$wrapper" - fi - fi - elif [[ "$p" == "--prefix-each" ]]; then + addValue "$p" "$varName" "$separator" "$value" + elif [[ ("$p" == "--suffix-each") || ("$p" == "--prefix-each") ]]; then varName="${params[$((n + 1))]}" separator="${params[$((n + 2))]}" values="${params[$((n + 3))]}" n=$((n + 3)) for value in $values; do - echo "export $varName=${value@Q}\${$varName:+${separator@Q}}\$$varName" >> "$wrapper" - done - elif [[ "$p" == "--suffix-each" ]]; then - varName="${params[$((n + 1))]}" - separator="${params[$((n + 2))]}" - values="${params[$((n + 3))]}" - n=$((n + 3)) - for value in $values; do - echo "export $varName=\$$varName\${$varName:+$separator}${value@Q}" >> "$wrapper" + addValue "$p" "$varName" "$separator" "$value" done elif [[ ("$p" == "--suffix-contents") || ("$p" == "--prefix-contents") ]]; then varName="${params[$((n + 1))]}" @@ -97,11 +135,7 @@ makeWrapper() { n=$((n + 3)) for fileName in $fileNames; do contents="$(cat "$fileName")" - if test "$p" = "--suffix-contents"; then - echo "export $varName=\$$varName\${$varName:+$separator}${contents@Q}" >> "$wrapper" - else - echo "export $varName=${contents@Q}\${$varName:+$separator}\$$varName" >> "$wrapper" - fi + addValue "$p" "$varName" "$separator" "$contents" done elif [[ "$p" == "--add-flags" ]]; then flags="${params[$((n + 1))]}" diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index b7980c478c66..4110327946d9 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -67,4 +67,6 @@ with pkgs; writers = callPackage ../build-support/writers/test.nix {}; dhall = callPackage ./dhall { }; + + makeWrapper = callPackage ./make-wrapper {}; } diff --git a/pkgs/test/make-wrapper/default.nix b/pkgs/test/make-wrapper/default.nix new file mode 100644 index 000000000000..6e70945ee194 --- /dev/null +++ b/pkgs/test/make-wrapper/default.nix @@ -0,0 +1,136 @@ +{ lib +, writeText +, writeCBin +, writeShellScript +, makeWrapper +, runCommand +, which +, ... +}: + +let + # Testfiles + foofile = writeText "foofile" "foo"; + barfile = writeText "barfile" "bar"; + + # Wrapped binaries + wrappedArgv0 = writeCBin "wrapped-argv0" '' + #include + #include + + void main(int argc, char** argv) { + printf("argv0=%s", argv[0]); + exit(0); + } + ''; + wrappedBinaryVar = writeShellScript "wrapped-var" '' + echo "VAR=$VAR" + ''; + wrappedBinaryArgs = writeShellScript "wrapped-args" '' + echo "$@" + ''; + + mkWrapperBinary = { name, args, wrapped ? wrappedBinaryVar }: runCommand name + { + nativeBuildInputs = [ makeWrapper ]; + } '' + mkdir -p $out/bin + makeWrapper "${wrapped}" "$out/bin/${name}" ${lib.escapeShellArgs args} + ''; + + mkTest = cmd: toExpect: '' + output="$(${cmd})" + if [[ "$output" != '${toExpect}' ]]; then + echo "test failed: the output of ${cmd} was '$output', expected '${toExpect}'" + echo "the wrapper contents:" + for i in ${cmd}; do + if [[ $i =~ ^test- ]]; then + cat $(which $i) + fi + done + exit 1 + fi + ''; +in +runCommand "make-wrapper-test" +{ + nativeBuildInputs = [ + which + (mkWrapperBinary { name = "test-argv0"; args = [ "--argv0" "foo" ]; wrapped = "${wrappedArgv0}/bin/wrapped-argv0"; }) + (mkWrapperBinary { name = "test-set"; args = [ "--set" "VAR" "abc" ]; }) + (mkWrapperBinary { name = "test-set-default"; args = [ "--set-default" "VAR" "abc" ]; }) + (mkWrapperBinary { name = "test-unset"; args = [ "--unset" "VAR" ]; }) + (mkWrapperBinary { name = "test-run"; args = [ "--run" "echo bar" ]; }) + (mkWrapperBinary { name = "test-run-and-set"; args = [ "--run" "export VAR=foo" "--set" "VAR" "bar" ]; }) + (mkWrapperBinary { name = "test-args"; args = [ "--add-flags" "abc" ]; wrapped = wrappedBinaryArgs; }) + (mkWrapperBinary { name = "test-prefix"; args = [ "--prefix" "VAR" ":" "abc" ]; }) + (mkWrapperBinary { name = "test-suffix"; args = [ "--suffix" "VAR" ":" "abc" ]; }) + (mkWrapperBinary { name = "test-prefix-and-suffix"; args = [ "--prefix" "VAR" ":" "foo" "--suffix" "VAR" ":" "bar" ]; }) + (mkWrapperBinary { name = "test-prefix-multi"; args = [ "--prefix" "VAR" ":" "abc:foo:foo" ]; }) + (mkWrapperBinary { name = "test-suffix-each"; args = [ "--suffix-each" "VAR" ":" "foo bar:def" ]; }) + (mkWrapperBinary { name = "test-prefix-each"; args = [ "--prefix-each" "VAR" ":" "foo bar:def" ]; }) + (mkWrapperBinary { name = "test-suffix-contents"; args = [ "--suffix-contents" "VAR" ":" "${foofile} ${barfile}" ]; }) + (mkWrapperBinary { name = "test-prefix-contents"; args = [ "--prefix-contents" "VAR" ":" "${foofile} ${barfile}" ]; }) + ]; +} + ( + # --argv0 works + mkTest "test-argv0" "argv0=foo" + + # --set works + + mkTest "test-set" "VAR=abc" + # --set overwrites the variable + + mkTest "VAR=foo test-set" "VAR=abc" + # --set-default works + + mkTest "test-set-default" "VAR=abc" + # --set-default doesn"t overwrite the variable + + mkTest "VAR=foo test-set-default" "VAR=foo" + # --unset works + + mkTest "VAR=foo test-unset" "VAR=" + + # --add-flags works + + mkTest "test-args" "abc" + # given flags are appended + + mkTest "test-args foo" "abc foo" + + # --run works + + mkTest "test-run" "bar\nVAR=" + # --run & --set works + + mkTest "test-run-and-set" "VAR=bar" + + # --prefix works + + mkTest "VAR=foo test-prefix" "VAR=abc:foo" + # sets variable if not set yet + + mkTest "test-prefix" "VAR=abc" + # prepends value only once + + mkTest "VAR=abc test-prefix" "VAR=abc" + # Moves value to the front if it already existed + + mkTest "VAR=foo:abc test-prefix" "VAR=abc:foo" + + mkTest "VAR=abc:foo:bar test-prefix-multi" "VAR=abc:foo:bar" + # Doesn't overwrite parts of the string + + mkTest "VAR=test:abcde:test test-prefix" "VAR=abc:test:abcde:test" + # Only append the value once when given multiple times in a parameter + # to makeWrapper + + mkTest "test-prefix" "VAR=abc" + + + # --suffix works + + mkTest "VAR=foo test-suffix" "VAR=foo:abc" + # sets variable if not set yet + + mkTest "test-suffix" "VAR=abc" + # adds the same value only once + + mkTest "VAR=abc test-suffix" "VAR=abc" + + mkTest "VAR=abc:foo test-suffix" "VAR=abc:foo" + # --prefix in combination with --suffix + + mkTest "VAR=abc test-prefix-and-suffix" "VAR=foo:abc:bar" + + # --suffix-each works + + mkTest "VAR=abc test-suffix-each" "VAR=abc:foo:bar:def" + # --prefix-each works + + mkTest "VAR=abc test-prefix-each" "VAR=bar:def:foo:abc" + # --suffix-contents works + + mkTest "VAR=abc test-suffix-contents" "VAR=abc:foo:bar" + # --prefix-contents works + + mkTest "VAR=abc test-prefix-contents" "VAR=bar:foo:abc" + + "touch $out" + ) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index cbb6be6c25b6..067e782cc365 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -715,8 +715,14 @@ with pkgs; makeInitrd = callPackage ../build-support/kernel/make-initrd.nix; # Args intentionally left out - makeWrapper = makeSetupHook { deps = [ dieHook ]; substitutions = { shell = targetPackages.runtimeShell; }; } - ../build-support/setup-hooks/make-wrapper.sh; + makeWrapper = makeSetupHook + { deps = [ dieHook ]; + substitutions = { + shell = targetPackages.runtimeShell; + passthru.tests = tests.makeWrapper; + }; + } + ../build-support/setup-hooks/make-wrapper.sh; makeBinaryWrapper = let f = { cc, sanitizers }: let