about summary refs log tree commit diff
path: root/nixpkgs/pkgs/test/stdenv
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/test/stdenv')
-rw-r--r--nixpkgs/pkgs/test/stdenv/default.nix302
-rw-r--r--nixpkgs/pkgs/test/stdenv/gcc-stageCompare.nix32
-rw-r--r--nixpkgs/pkgs/test/stdenv/hooks.nix136
-rw-r--r--nixpkgs/pkgs/test/stdenv/patch-shebangs.nix115
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
+  '';
+}