2 # nix-build -A tests.stdenv
11 # early enough not to rebuild gcc but late enough to have patchelf
12 earlyPkgs = stdenv.__bootPackages.stdenv.__bootPackages;
13 earlierPkgs = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages;
14 # use a early stdenv so when hacking on stdenv this test can be run quickly
15 bootStdenv = stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv;
16 pkgsStructured = import pkgs.path { config = { structuredAttrsByDefault = true; }; inherit (stdenv.hostPlatform) system; };
17 bootStdenvStructuredAttrsByDefault = pkgsStructured.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv;
19 runCommand = earlierPkgs.runCommand;
22 ccWrapperSubstitutionsTest = { name, stdenv', extraAttrs ? { } }:
24 stdenv'.cc.overrideAttrs (previousAttrs: ({
27 postFixup = previousAttrs.postFixup + ''
28 declare -p wrapperName
29 echo "env.wrapperName = $wrapperName"
30 [[ $wrapperName == "CC_WRAPPER" ]] || (echo "'\$wrapperName' was not 'CC_WRAPPER'" && false)
32 echo "env.suffixSalt = $suffixSalt"
33 [[ $suffixSalt == "${stdenv'.cc.suffixSalt}" ]] || (echo "'\$suffxSalt' was not '${stdenv'.cc.suffixSalt}'" && false)
35 grep -q "@out@" $out/bin/cc || echo "@out@ in $out/bin/cc was substituted"
36 grep -q "@suffixSalt@" $out/bin/cc && (echo "$out/bin/cc contains unsubstituted variables" && false)
42 testEnvAttrset = { name, stdenv', extraAttrs ? { } }:
47 string = "testing-string";
50 passAsFile = [ "buildCommand" ];
53 echo "env.string = $string"
54 [[ $string == "testing-string" ]] || (echo "'\$string' was not 'testing-string'" && false)
55 [[ "$(declare -p string)" == 'declare -x string="testing-string"' ]] || (echo "'\$string' was not exported" && false)
60 testPrependAndAppendToVar = { name, stdenv', extraAttrs ? { } }:
65 string = "testing-string";
68 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ];
71 appendToVar string hello
72 # test that quoted strings work
73 prependToVar string "world"
76 declare -A associativeArray=(["X"]="Y")
77 [[ $(appendToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "appendToVar did not throw appending to associativeArray" && false)
78 [[ $(prependToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not throw prepending associativeArray" && false)
80 [[ $string == "world testing-string hello" ]] || (echo "'\$string' was not 'world testing-string hello'" && false)
82 # test appending to a unset variable
83 appendToVar nonExistant created hello
84 declare -p nonExistant
85 if [[ -n $__structuredAttrs ]]; then
86 [[ "''${nonExistant[@]}" == "created hello" ]]
88 # there's a extra " " in front here and a extra " " in the end of prependToVar
89 # shouldn't matter because these functions will mostly be used for $*Flags and the Flag variable will in most cases already exist
90 [[ "$nonExistant" == " created hello" ]]
99 testConcatTo = { name, stdenv', extraAttrs ? { } }:
107 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ];
109 declare -A associativeArray=(["X"]="Y")
110 [[ $(concatTo nowhere associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatTo did not throw concatenating associativeArray" && false)
115 declare -a flagsArray
116 concatTo flagsArray string list notset=e=f empty_array=g empty_string=h
117 declare -p flagsArray
118 [[ "''${flagsArray[0]}" == "a" ]] || (echo "'\$flagsArray[0]' was not 'a'" && false)
119 [[ "''${flagsArray[1]}" == "*" ]] || (echo "'\$flagsArray[1]' was not '*'" && false)
120 [[ "''${flagsArray[2]}" == "c" ]] || (echo "'\$flagsArray[2]' was not 'c'" && false)
121 [[ "''${flagsArray[3]}" == "d" ]] || (echo "'\$flagsArray[3]' was not 'd'" && false)
122 [[ "''${flagsArray[4]}" == "e=f" ]] || (echo "'\$flagsArray[4]' was not 'e=f'" && false)
123 [[ "''${flagsArray[5]}" == "g" ]] || (echo "'\$flagsArray[5]' was not 'g'" && false)
124 [[ "''${flagsArray[6]}" == "h" ]] || (echo "'\$flagsArray[6]' was not 'h'" && false)
126 # test concatenating to unset variable
127 concatTo nonExistant string list notset=e=f empty_array=g empty_string=h
128 declare -p nonExistant
129 [[ "''${nonExistant[0]}" == "a" ]] || (echo "'\$nonExistant[0]' was not 'a'" && false)
130 [[ "''${nonExistant[1]}" == "*" ]] || (echo "'\$nonExistant[1]' was not '*'" && false)
131 [[ "''${nonExistant[2]}" == "c" ]] || (echo "'\$nonExistant[2]' was not 'c'" && false)
132 [[ "''${nonExistant[3]}" == "d" ]] || (echo "'\$nonExistant[3]' was not 'd'" && false)
133 [[ "''${nonExistant[4]}" == "e=f" ]] || (echo "'\$nonExistant[4]' was not 'e=f'" && false)
134 [[ "''${nonExistant[5]}" == "g" ]] || (echo "'\$nonExistant[5]' was not 'g'" && false)
135 [[ "''${nonExistant[6]}" == "h" ]] || (echo "'\$nonExistant[6]' was not 'h'" && false)
143 testConcatStringsSep = { name, stdenv' }:
148 # NOTE: Testing with "&" as separator is intentional, because unquoted
149 # "&" has a special meaning in the "${var//pattern/replacement}" syntax.
150 # Cf. https://github.com/NixOS/nixpkgs/pull/318614#discussion_r1706191919
151 passAsFile = [ "buildCommand" ];
153 declare -A associativeArray=(["X"]="Y")
154 [[ $(concatStringsSep ";" associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatStringsSep did not throw concatenating associativeArray" && false)
156 string="lorem ipsum dolor sit amet"
157 stringWithSep="$(concatStringsSep "&" string)"
158 [[ "$stringWithSep" == "lorem&ipsum&dolor&sit&amet" ]] || (echo "'\$stringWithSep' was not 'lorem&ipsum&dolor&sit&amet'" && false)
160 array=("lorem ipsum" "dolor" "sit amet")
161 arrayWithSep="$(concatStringsSep "&" array)"
162 [[ "$arrayWithSep" == "lorem ipsum&dolor&sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum&dolor&sit amet'" && false)
170 # Disable on Darwin due to assumptions with __bootPackages
171 __attrsFailEvaluation = stdenv.hostPlatform.isDarwin;
173 # tests for hooks in `stdenv.defaultNativeBuildInputs`
174 hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenv; pkgs = earlyPkgs; inherit lib; });
176 outputs-no-out = runCommand "outputs-no-out-assert" {
177 result = earlierPkgs.testers.testBuildFailure (bootStdenv.mkDerivation {
179 name = "outputs-no-out";
187 # Assumption: the first output* variable to be configured is
188 # _overrideFirst outputDev "dev" "out"
189 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";
191 grep -F "$expectedMsg" $result/testBuildFailure.log >/dev/null
195 test-env-attrset = testEnvAttrset { name = "test-env-attrset"; stdenv' = bootStdenv; };
197 # Test compatibility with derivations using `env` as a regular variable.
198 test-env-derivation = bootStdenv.mkDerivation rec {
199 name = "test-env-derivation";
200 env = bootStdenv.mkDerivation {
208 passAsFile = [ "buildCommand" ];
211 [[ $env == "${env}" ]]
216 # Check that mkDerivation rejects MD5 hashes
217 rejectedHashes = lib.recurseIntoAttrs {
219 let drv = runCommand "md5 outputHash rejected" {
220 outputHash = "md5-fPt7dxVVP7ffY3MxkQdwVw==";
222 in assert !(builtins.tryEval drv).success; {};
225 test-inputDerivation = let
226 inherit (stdenv.mkDerivation {
227 dep1 = derivation { name = "dep1"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
228 dep2 = derivation { name = "dep2"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
229 passAsFile = [ "dep2" ];
232 runCommand "test-inputDerivation" {
233 exportReferencesGraph = [ "graph" inputDerivation ];
235 grep ${inputDerivation.dep1} graph
236 grep ${inputDerivation.dep2} graph
240 test-inputDerivation-fixed-output = let
241 inherit (stdenv.mkDerivation {
242 dep1 = derivation { name = "dep1"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
243 dep2 = derivation { name = "dep2"; builder = "/bin/sh"; args = [ "-c" ": > $out" ]; system = builtins.currentSystem; };
245 outputHash = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
246 outputHashMode = "flat";
247 outputHashAlgo = "sha256";
251 passAsFile = [ "dep2" ];
254 runCommand "test-inputDerivation" {
255 exportReferencesGraph = [ "graph" inputDerivation ];
257 grep ${inputDerivation.dep1} graph
258 grep ${inputDerivation.dep2} graph
262 test-prepend-append-to-var = testPrependAndAppendToVar {
263 name = "test-prepend-append-to-var";
264 stdenv' = bootStdenv;
267 test-concat-to = testConcatTo {
268 name = "test-concat-to";
269 stdenv' = bootStdenv;
272 test-concat-strings-sep = testConcatStringsSep {
273 name = "test-concat-strings-sep";
274 stdenv' = bootStdenv;
277 test-structured-env-attrset = testEnvAttrset {
278 name = "test-structured-env-attrset";
279 stdenv' = bootStdenv;
280 extraAttrs = { __structuredAttrs = true; };
283 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
284 name = "test-cc-wrapper-substitutions";
285 stdenv' = bootStdenv;
288 structuredAttrsByDefault = lib.recurseIntoAttrs {
290 hooks = lib.recurseIntoAttrs (import ./hooks.nix { stdenv = bootStdenvStructuredAttrsByDefault; pkgs = earlyPkgs; inherit lib; });
292 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
293 name = "test-cc-wrapper-substitutions-structuredAttrsByDefault";
294 stdenv' = bootStdenvStructuredAttrsByDefault;
297 test-structured-env-attrset = testEnvAttrset {
298 name = "test-structured-env-attrset-structuredAttrsByDefault";
299 stdenv' = bootStdenvStructuredAttrsByDefault;
302 test-prepend-append-to-var = testPrependAndAppendToVar {
303 name = "test-prepend-append-to-var-structuredAttrsByDefault";
304 stdenv' = bootStdenvStructuredAttrsByDefault;
306 # will be a bash indexed array in attrs.sh
307 # declare -a list=('a' 'b' )
308 # and a json array in attrs.json
311 # will be a bash associative array(dictionary) in attrs.sh
312 # declare -A array=(['a']='1' ['b']='2' )
313 # and a json object in attrs.json
314 # {"array":{"a":"1","b":"2"}
315 array = { a = "1"; b = "2"; };
321 [[ "''${array[c]}" == "3" ]] || (echo "c element of '\$array' was not '3'" && false)
324 prependToVar list hello
325 # test that quoted strings work
326 appendToVar list "world"
329 [[ "''${list[0]}" == "hello" ]] || (echo "first element of '\$list' was not 'hello'" && false)
330 [[ "''${list[1]}" == "a" ]] || (echo "first element of '\$list' was not 'a'" && false)
331 [[ "''${list[-1]}" == "world" ]] || (echo "last element of '\$list' was not 'world'" && false)
336 test-concat-to = testConcatTo {
337 name = "test-concat-to-structuredAttrsByDefault";
338 stdenv' = bootStdenvStructuredAttrsByDefault;
340 # test that whitespace is kept in the bash array for structuredAttrs
341 listWithSpaces = [ "c c" "d d" ];
343 declare -a flagsWithSpaces
344 concatTo flagsWithSpaces string listWithSpaces
345 declare -p flagsWithSpaces
346 [[ "''${flagsWithSpaces[0]}" == "a" ]] || (echo "'\$flagsWithSpaces[0]' was not 'a'" && false)
347 [[ "''${flagsWithSpaces[1]}" == "*" ]] || (echo "'\$flagsWithSpaces[1]' was not '*'" && false)
348 [[ "''${flagsWithSpaces[2]}" == "c c" ]] || (echo "'\$flagsWithSpaces[2]' was not 'c c'" && false)
349 [[ "''${flagsWithSpaces[3]}" == "d d" ]] || (echo "'\$flagsWithSpaces[3]' was not 'd d'" && false)
354 test-concat-strings-sep = testConcatStringsSep {
355 name = "test-concat-strings-sep-structuredAttrsByDefault";
356 stdenv' = bootStdenvStructuredAttrsByDefault;
359 test-golden-example-structuredAttrs =
361 goldenSh = earlyPkgs.writeText "goldenSh" ''
362 declare -A EXAMPLE_ATTRS=(['foo']='bar' )
363 declare EXAMPLE_BOOL_FALSE=
364 declare EXAMPLE_BOOL_TRUE=1
365 declare EXAMPLE_INT=123
366 declare EXAMPLE_INT_NEG=-123
367 declare -a EXAMPLE_LIST=('foo' 'bar' )
368 declare EXAMPLE_STR='foo bar'
370 goldenJson = earlyPkgs.writeText "goldenSh" ''
375 "EXAMPLE_BOOL_FALSE": false,
376 "EXAMPLE_BOOL_TRUE": true,
378 "EXAMPLE_INT_NEG": -123,
383 "EXAMPLE_NESTED_ATTRS": {
388 "EXAMPLE_NESTED_LIST": [
397 "EXAMPLE_STR": "foo bar"
401 bootStdenvStructuredAttrsByDefault.mkDerivation {
402 name = "test-golden-example-structuredAttrsByDefault";
403 nativeBuildInputs = [ earlyPkgs.jq ];
405 EXAMPLE_BOOL_TRUE = true;
406 EXAMPLE_BOOL_FALSE = false;
408 EXAMPLE_INT_NEG = -123;
409 EXAMPLE_STR = "foo bar";
410 EXAMPLE_LIST = [ "foo" "bar" ];
411 EXAMPLE_NESTED_LIST = [ [ "foo" "bar" ] [ "baz" ] ];
412 EXAMPLE_ATTRS = { foo = "bar"; };
413 EXAMPLE_NESTED_ATTRS = { foo.bar = "baz"; };
420 cat $NIX_ATTRS_SH_FILE | grep "EXAMPLE" | grep -v -E 'installPhase|jq' > $out/sh
421 jq 'with_entries(select(.key|match("EXAMPLE")))' $NIX_ATTRS_JSON_FILE > $out/json
422 diff $out/sh $goldenSh
423 diff $out/json $goldenJson