diff options
Diffstat (limited to 'nixpkgs/pkgs/test/stdenv')
-rw-r--r-- | nixpkgs/pkgs/test/stdenv/default.nix | 302 | ||||
-rw-r--r-- | nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix | 32 | ||||
-rw-r--r-- | nixpkgs/pkgs/test/stdenv/hooks.nix | 136 | ||||
-rw-r--r-- | nixpkgs/pkgs/test/stdenv/patch-shebangs.nix | 115 |
4 files changed, 585 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/test/stdenv/default.nix b/nixpkgs/pkgs/test/stdenv/default.nix new file mode 100644 index 000000000000..3882eb2b625c --- /dev/null +++ b/nixpkgs/pkgs/test/stdenv/default.nix @@ -0,0 +1,302 @@ +# To run these tests: +# nix-build -A tests.stdenv + +{ stdenv +, pkgs +, lib +, testers +}: + +let + # early enough not to rebuild gcc but late enough to have patchelf + earlyPkgs = stdenv.__bootPackages.stdenv.__bootPackages; + earlierPkgs = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages; + # use a early stdenv so when hacking on stdenv this test can be run quickly + bootStdenv = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv; + pkgsStructured = import pkgs.path { config = { structuredAttrsByDefault = true; }; inherit (stdenv.hostPlatform) system; }; + bootStdenvStructuredAttrsByDefault = pkgsStructured.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv; + + runCommand = earlierPkgs.runCommand; + + + ccWrapperSubstitutionsTest = { name, stdenv', extraAttrs ? { } }: + + stdenv'.cc.overrideAttrs (previousAttrs: ({ + inherit name; + + postFixup = previousAttrs.postFixup + '' + declare -p wrapperName + echo "env.wrapperName = $wrapperName" + [[ $wrapperName == "CC_WRAPPER" ]] || (echo "'\$wrapperName' was not 'CC_WRAPPER'" && false) + declare -p suffixSalt + echo "env.suffixSalt = $suffixSalt" + [[ $suffixSalt == "${stdenv'.cc.suffixSalt}" ]] || (echo "'\$suffxSalt' was not '${stdenv'.cc.suffixSalt}'" && false) + + grep -q "@out@" $out/bin/cc || echo "@out@ in $out/bin/cc was substituted" + grep -q "@suffixSalt@" $out/bin/cc && (echo "$out/bin/cc contains unsubstituted variables" && false) + + touch $out + ''; + } // extraAttrs)); + + testEnvAttrset = { name, stdenv', extraAttrs ? { } }: + stdenv'.mkDerivation + ({ + inherit name; + env = { + string = "testing-string"; + }; + + passAsFile = [ "buildCommand" ]; + buildCommand = '' + declare -p string + echo "env.string = $string" + [[ $string == "testing-string" ]] || (echo "'\$string' was not 'testing-string'" && false) + [[ "$(declare -p string)" == 'declare -x string="testing-string"' ]] || (echo "'\$string' was not exported" && false) + touch $out + ''; + } // extraAttrs); + + testPrependAndAppendToVar = { name, stdenv', extraAttrs ? { } }: + stdenv'.mkDerivation + ({ + inherit name; + env = { + string = "testing-string"; + }; + + passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ]; + buildCommand = '' + declare -p string + appendToVar string hello + # test that quoted strings work + prependToVar string "world" + declare -p string + + declare -A associativeArray=(["X"]="Y") + [[ $(appendToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not catch prepending associativeArray" && false) + [[ $(prependToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not catch prepending associativeArray" && false) + + [[ $string == "world testing-string hello" ]] || (echo "'\$string' was not 'world testing-string hello'" && false) + + # test appending to a unset variable + appendToVar nonExistant created hello + typeset -p nonExistant + if [[ -n $__structuredAttrs ]]; then + [[ "''${nonExistant[@]}" == "created hello" ]] + else + # there's a extra " " in front here and a extra " " in the end of prependToVar + # shouldn't matter because these functions will mostly be used for $*Flags and the Flag variable will in most cases already exit + [[ "$nonExistant" == " created hello" ]] + fi + + eval "$extraTest" + + touch $out + ''; + } // extraAttrs); + +in + +{ + # tests for hooks in `stdenv.defaultNativeBuildInputs` + hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenv; pkgs = earlyPkgs; inherit lib; }); + + outputs-no-out = runCommand "outputs-no-out-assert" { + result = earlierPkgs.testers.testBuildFailure (bootStdenv.mkDerivation { + NIX_DEBUG = 1; + name = "outputs-no-out"; + outputs = ["foo"]; + buildPhase = ":"; + installPhase = '' + touch $foo + ''; + }); + + # Assumption: the first output* variable to be configured is + # _overrideFirst outputDev "dev" "out" + expectedMsg = "error: _assignFirst: could not find a non-empty variable whose name to assign to outputDev.\n The following variables were all unset or empty:\n dev out"; + } '' + grep -F "$expectedMsg" $result/testBuildFailure.log >/dev/null + touch $out + ''; + + test-env-attrset = testEnvAttrset { name = "test-env-attrset"; stdenv' = bootStdenv; }; + + # Test compatibility with derivations using `env` as a regular variable. + test-env-derivation = bootStdenv.mkDerivation rec { + name = "test-env-derivation"; + env = bootStdenv.mkDerivation { + name = "foo"; + buildCommand = '' + mkdir "$out" + touch "$out/bar" + ''; + }; + + passAsFile = [ "buildCommand" ]; + buildCommand = '' + declare -p env + [[ $env == "${env}" ]] + touch "$out" + ''; + }; + + # Check that mkDerivation rejects MD5 hashes + rejectedHashes = lib.recurseIntoAttrs { + md5 = + let drv = runCommand "md5 outputHash rejected" { + outputHash = "md5-fPt7dxVVP7ffY3MxkQdwVw=="; + } "true"; + in assert !(builtins.tryEval drv).success; {}; + }; + + test-inputDerivation = let + inherit (stdenv.mkDerivation { + dep1 = derivation { name = "dep1"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; }; + dep2 = derivation { name = "dep2"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; }; + passAsFile = [ "dep2" ]; + }) inputDerivation; + in + runCommand "test-inputDerivation" { + exportReferencesGraph = [ "graph" inputDerivation ]; + } '' + grep ${inputDerivation.dep1} graph + grep ${inputDerivation.dep2} graph + touch $out + ''; + + test-prepend-append-to-var = testPrependAndAppendToVar { + name = "test-prepend-append-to-var"; + stdenv' = bootStdenv; + }; + + test-structured-env-attrset = testEnvAttrset { + name = "test-structured-env-attrset"; + stdenv' = bootStdenv; + extraAttrs = { __structuredAttrs = true; }; + }; + + test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest { + name = "test-cc-wrapper-substitutions"; + stdenv' = bootStdenv; + }; + + structuredAttrsByDefault = lib.recurseIntoAttrs { + + hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenvStructuredAttrsByDefault; pkgs = earlyPkgs; inherit lib; }); + + test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest { + name = "test-cc-wrapper-substitutions-structuredAttrsByDefault"; + stdenv' = bootStdenvStructuredAttrsByDefault; + }; + + test-structured-env-attrset = testEnvAttrset { + name = "test-structured-env-attrset-structuredAttrsByDefault"; + stdenv' = bootStdenvStructuredAttrsByDefault; + }; + + test-prepend-append-to-var = testPrependAndAppendToVar { + name = "test-prepend-append-to-var-structuredAttrsByDefault"; + stdenv' = bootStdenvStructuredAttrsByDefault; + extraAttrs = { + # will be a bash indexed array in attrs.sh + # declare -a list=('a' 'b' ) + # and a json array in attrs.json + # "list":["a","b"] + list = [ "a" "b" ]; + # will be a bash associative array(dictionary) in attrs.sh + # declare -A array=(['a']='1' ['b']='2' ) + # and a json object in attrs.json + # {"array":{"a":"1","b":"2"} + array = { a = "1"; b = "2"; }; + extraTest = '' + declare -p array + array+=(["c"]="3") + declare -p array + + [[ "''${array[c]}" == "3" ]] || (echo "c element of '\$array' was not '3'" && false) + + declare -p list + prependToVar list hello + # test that quoted strings work + appendToVar list "world" + declare -p list + + [[ "''${list[0]}" == "hello" ]] || (echo "first element of '\$list' was not 'hello'" && false) + [[ "''${list[1]}" == "a" ]] || (echo "first element of '\$list' was not 'a'" && false) + [[ "''${list[-1]}" == "world" ]] || (echo "last element of '\$list' was not 'world'" && false) + ''; + }; + }; + + test-golden-example-structuredAttrs = + let + goldenSh = earlyPkgs.writeText "goldenSh" '' + declare -A EXAMPLE_ATTRS=(['foo']='bar' ) + declare EXAMPLE_BOOL_FALSE= + declare EXAMPLE_BOOL_TRUE=1 + declare EXAMPLE_INT=123 + declare EXAMPLE_INT_NEG=-123 + declare -a EXAMPLE_LIST=('foo' 'bar' ) + declare EXAMPLE_STR='foo bar' + ''; + goldenJson = earlyPkgs.writeText "goldenSh" '' + { + "EXAMPLE_ATTRS": { + "foo": "bar" + }, + "EXAMPLE_BOOL_FALSE": false, + "EXAMPLE_BOOL_TRUE": true, + "EXAMPLE_INT": 123, + "EXAMPLE_INT_NEG": -123, + "EXAMPLE_LIST": [ + "foo", + "bar" + ], + "EXAMPLE_NESTED_ATTRS": { + "foo": { + "bar": "baz" + } + }, + "EXAMPLE_NESTED_LIST": [ + [ + "foo", + "bar" + ], + [ + "baz" + ] + ], + "EXAMPLE_STR": "foo bar" + } + ''; + in + bootStdenvStructuredAttrsByDefault.mkDerivation { + name = "test-golden-example-structuredAttrsByDefault"; + nativeBuildInputs = [ earlyPkgs.jq ]; + + EXAMPLE_BOOL_TRUE = true; + EXAMPLE_BOOL_FALSE = false; + EXAMPLE_INT = 123; + EXAMPLE_INT_NEG = -123; + EXAMPLE_STR = "foo bar"; + EXAMPLE_LIST = [ "foo" "bar" ]; + EXAMPLE_NESTED_LIST = [ [ "foo" "bar" ] [ "baz" ] ]; + EXAMPLE_ATTRS = { foo = "bar"; }; + EXAMPLE_NESTED_ATTRS = { foo.bar = "baz"; }; + + inherit goldenSh; + inherit goldenJson; + + buildCommand = '' + mkdir -p $out + cat $NIX_ATTRS_SH_FILE | grep "EXAMPLE" | grep -v -E 'installPhase|jq' > $out/sh + jq 'with_entries(select(.key|match("EXAMPLE")))' $NIX_ATTRS_JSON_FILE > $out/json + diff $out/sh $goldenSh + diff $out/json $goldenJson + ''; + }; + + }; +} diff --git a/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix b/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix new file mode 100644 index 000000000000..e5c2ed5921b3 --- /dev/null +++ b/nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix @@ -0,0 +1,32 @@ +# This test *must* be run prior to releasing any build of either stdenv or the +# gcc that it exports! This check should also be part of CI for any PR that +# causes a rebuild of `stdenv.cc`. +# +# When we used gcc's internal bootstrap it did this check as part of (and +# serially with) the gcc derivation. Now that we bootstrap externally this +# check can be done in parallel with any/all of stdenv's referrers. But we +# must remember to do the check. +# + +{ stdenv +, pkgs +, lib +}: + +assert stdenv.cc.isGNU; +with pkgs; +# rebuild gcc using the "final" stdenv +let gcc-stageCompare = (gcc-unwrapped.override { + reproducibleBuild = true; + profiledCompiler = false; + stdenv = overrideCC stdenv (wrapCCWith { + cc = stdenv.cc; + }); + }).overrideAttrs(_: { + NIX_OUTPATH_USED_AS_RANDOM_SEED = stdenv.cc.cc.out; + }); +in (runCommand "gcc-stageCompare" {} '' + diff -sr ${pkgs.gcc-unwrapped.checksum}/checksums ${gcc-stageCompare.checksum}/checksums && touch $out +'').overrideAttrs (a: { + meta = (a.meta or { }) // { platforms = lib.platforms.linux; }; +}) diff --git a/nixpkgs/pkgs/test/stdenv/hooks.nix b/nixpkgs/pkgs/test/stdenv/hooks.nix new file mode 100644 index 000000000000..eb1b3f61bda6 --- /dev/null +++ b/nixpkgs/pkgs/test/stdenv/hooks.nix @@ -0,0 +1,136 @@ +{ stdenv, pkgs, lib }: + +# ordering should match defaultNativeBuildInputs + +{ + # TODO: add audit-tmpdir + compress-man-pages = + let + manFile = pkgs.writeText "small-man" '' + .TH HELLO "1" "May 2022" "hello 2.12.1" "User Commands" + .SH NAME + hello - friendly greeting program + ''; + in + stdenv.mkDerivation { + name = "test-compress-man-pages"; + buildCommand = '' + mkdir -p $out/share/man + cp ${manFile} $out/share/man/small-man.1 + compressManPages $out + [[ -e $out/share/man/small-man.1.gz ]] + ''; + }; + make-symlinks-relative = stdenv.mkDerivation { + name = "test-make-symlinks-relative"; + outputs = [ "out" "man" ]; + buildCommand = '' + mkdir -p $out/{bar,baz} + mkdir -p $man/share/{x,y} + source1="$out/bar/foo" + destination1="$out/baz/foo" + source2="$man/share/x/file1" + destination2="$man/share/y/file2" + echo foo > $source1 + echo foo > $source2 + ln -s $source1 $destination1 + ln -s $source2 $destination2 + echo "symlink before patching: $(readlink $destination1)" + echo "symlink before patching: $(readlink $destination2)" + + _makeSymlinksRelativeInAllOutputs + + echo "symlink after patching: $(readlink $destination1)" + ([[ -e $destination1 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1) + ([[ $(readlink $destination1) == "../bar/foo" ]] && echo "absolute symlink was made relative") || (echo "symlink was not made relative" && exit 1) + echo "symlink after patching: $(readlink $destination2)" + ([[ -e $destination2 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1) + ([[ $(readlink $destination2) == "../x/file1" ]] && echo "absolute symlink was made relative") || (echo "symlink was not made relative" && exit 1) + ''; + }; + move-docs = stdenv.mkDerivation { + name = "test-move-docs"; + buildCommand = '' + mkdir -p $out/{man,doc,info} + touch $out/{man,doc,info}/foo + cat $out/{man,doc,info}/foo + + _moveToShare + + (cat $out/share/{man,doc,info}/foo 2>/dev/null && echo "man,doc,info were moved") || (echo "man,doc,info were not moved" && exit 1) + ''; + }; + move-lib64 = stdenv.mkDerivation { + name = "test-move-lib64"; + buildCommand = '' + mkdir -p $out/lib64 + touch $out/lib64/foo + cat $out/lib64/foo + + _moveLib64 + + # check symlink + [[ -h $out/lib64 ]] + ([[ -e $out/lib64 ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1) + [[ -e $out/lib/foo ]] + ''; + }; + move-sbin = stdenv.mkDerivation { + name = "test-move-sbin"; + buildCommand = '' + mkdir -p $out/sbin + touch $out/sbin/foo + cat $out/sbin/foo + + _moveSbin + + # check symlink + [[ -h $out/sbin ]] + ([[ -e $out/sbin ]] && echo "symlink isn't broken") || (echo "symlink is broken" && exit 1) + [[ -e $out/bin/foo ]] + ''; + }; + # TODO: add multiple-outputs + patch-shebangs = import ./patch-shebangs.nix { inherit stdenv lib pkgs; }; + prune-libtool-files = + let + libFoo = pkgs.writeText "libFoo" '' + # Generated by libtool (GNU libtool) 2.4.6 + old_library=''' + dependency_libs=' -Lbar.la -Lbaz.la' + ''; + in + stdenv.mkDerivation { + name = "test-prune-libtool-files"; + buildCommand = '' + mkdir -p $out/lib + cp ${libFoo} $out/lib/libFoo.la + _pruneLibtoolFiles + grep "^dependency_libs=''' #pruned" $out/lib/libFoo.la + # confirm file doesn't only contain the above + grep "^old_library='''" $out/lib/libFoo.la + ''; + }; + reproducible-builds = stdenv.mkDerivation { + name = "test-reproducible-builds"; + buildCommand = '' + # can't be tested more precisely because the value of random-seed changes depending on the output + [[ $NIX_CFLAGS_COMPILE =~ "-frandom-seed=" ]] + touch $out + ''; + }; + set-source-date-epoch-to-latest = stdenv.mkDerivation { + name = "test-set-source-date-epoch-to-latest"; + buildCommand = '' + sourceRoot=$NIX_BUILD_TOP/source + mkdir -p $sourceRoot + touch --date=1/1/2015 $sourceRoot/foo + + _updateSourceDateEpochFromSourceRoot + + [[ $SOURCE_DATE_EPOCH == "1420070400" ]] + touch $out + ''; + }; + # TODO: add strip +} diff --git a/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix b/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix new file mode 100644 index 000000000000..888d4a53a273 --- /dev/null +++ b/nixpkgs/pkgs/test/stdenv/patch-shebangs.nix @@ -0,0 +1,115 @@ +{ lib, stdenv, pkgs }: + +# since the tests are using a early stdenv, the stdenv will have dontPatchShebangs=1, so it has to be unset +# https://github.com/NixOS/nixpkgs/blob/768a982bfc9d29a6bd3beb963ed4b054451ce3d0/pkgs/stdenv/linux/default.nix#L148-L153 + +# strictDeps has to be disabled because the shell isn't in buildInputs + +let + tests = { + bad-shebang = stdenv.mkDerivation { + name = "bad-shebang"; + strictDeps = false; + dontUnpack = true; + installPhase = '' + mkdir -p $out/bin + echo "#!/bin/bash" > $out/bin/test + echo "echo -n hello" >> $out/bin/test + chmod +x $out/bin/test + dontPatchShebangs= + ''; + passthru = { + assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null"; + }; + }; + + ignores-nix-store = stdenv.mkDerivation { + name = "ignores-nix-store"; + strictDeps = false; + dontUnpack = true; + installPhase = '' + mkdir -p $out/bin + echo "#!$NIX_STORE/path/to/bash" > $out/bin/test + echo "echo -n hello" >> $out/bin/test + chmod +x $out/bin/test + dontPatchShebangs= + ''; + passthru = { + assertion = "grep \"^#!$NIX_STORE/path/to/bash\" $out/bin/test > /dev/null"; + }; + }; + + updates-nix-store = stdenv.mkDerivation { + name = "updates-nix-store"; + strictDeps = false; + dontUnpack = true; + installPhase = '' + mkdir -p $out/bin + echo "#!$NIX_STORE/path/to/bash" > $out/bin/test + echo "echo -n hello" >> $out/bin/test + chmod +x $out/bin/test + patchShebangs --update $out/bin/test + dontPatchShebangs=1 + ''; + passthru = { + assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null"; + }; + }; + + split-string = stdenv.mkDerivation { + name = "split-string"; + strictDeps = false; + dontUnpack = true; + installPhase = '' + mkdir -p $out/bin + echo "#!/usr/bin/env -S bash --posix" > $out/bin/test + echo "echo -n hello" >> $out/bin/test + chmod +x $out/bin/test + dontPatchShebangs= + ''; + passthru = { + assertion = "grep -v '^#!${pkgs.coreutils}/bin/env -S ${stdenv.shell} --posix' $out/bin/test > /dev/null"; + }; + }; + + }; +in +stdenv.mkDerivation { + name = "test-patch-shebangs"; + passthru = { inherit (tests) bad-shebang ignores-nix-store updates-nix-store split-string; }; + buildCommand = '' + validate() { + local name=$1 + local testout=$2 + local assertion=$3 + + echo -n "... $name: " >&2 + + local rc=0 + (out=$testout eval "$assertion") || rc=1 + + if [ "$rc" -eq 0 ]; then + echo "yes" >&2 + else + echo "no" >&2 + fi + + return "$rc" + } + + echo "checking whether patchShebangs works properly... ">&2 + + fail= + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: '' + validate "${test.name}" "${test}" ${lib.escapeShellArg test.assertion} || fail=1 + '') tests)} + + if [ "$fail" ]; then + echo "failed" + exit 1 + else + echo "succeeded" + touch $out + fi + ''; +} |