Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# To run these tests: 2# nix-build -A tests.stdenv 3 4{ 5 stdenv, 6 pkgs, 7 lib, 8 testers, 9}: 10 11let 12 # early enough not to rebuild gcc but late enough to have patchelf 13 earlyPkgs = stdenv.__bootPackages.stdenv.__bootPackages; 14 earlierPkgs = 15 stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages; 16 # use a early stdenv so when hacking on stdenv this test can be run quickly 17 bootStdenv = 18 stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv; 19 pkgsStructured = import pkgs.path { 20 config = { 21 structuredAttrsByDefault = true; 22 }; 23 inherit (stdenv.hostPlatform) system; 24 }; 25 bootStdenvStructuredAttrsByDefault = 26 pkgsStructured.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv; 27 28 runCommand = earlierPkgs.runCommand; 29 30 ccWrapperSubstitutionsTest = 31 { 32 name, 33 stdenv', 34 extraAttrs ? { }, 35 }: 36 37 stdenv'.cc.overrideAttrs ( 38 previousAttrs: 39 ( 40 { 41 inherit name; 42 43 postFixup = previousAttrs.postFixup + '' 44 declare -p wrapperName 45 echo "env.wrapperName = $wrapperName" 46 [[ $wrapperName == "CC_WRAPPER" ]] || (echo "'\$wrapperName' was not 'CC_WRAPPER'" && false) 47 declare -p suffixSalt 48 echo "env.suffixSalt = $suffixSalt" 49 [[ $suffixSalt == "${stdenv'.cc.suffixSalt}" ]] || (echo "'\$suffxSalt' was not '${stdenv'.cc.suffixSalt}'" && false) 50 51 grep -q "@out@" $out/bin/cc || echo "@out@ in $out/bin/cc was substituted" 52 grep -q "@suffixSalt@" $out/bin/cc && (echo "$out/bin/cc contains unsubstituted variables" && false) 53 54 touch $out 55 ''; 56 } 57 // extraAttrs 58 ) 59 ); 60 61 testEnvAttrset = 62 { 63 name, 64 stdenv', 65 extraAttrs ? { }, 66 }: 67 stdenv'.mkDerivation ( 68 { 69 inherit name; 70 env = { 71 string = "testing-string"; 72 }; 73 74 passAsFile = [ "buildCommand" ]; 75 buildCommand = '' 76 declare -p string 77 echo "env.string = $string" 78 [[ $string == "testing-string" ]] || (echo "'\$string' was not 'testing-string'" && false) 79 [[ "$(declare -p string)" == 'declare -x string="testing-string"' ]] || (echo "'\$string' was not exported" && false) 80 touch $out 81 ''; 82 } 83 // extraAttrs 84 ); 85 86 testPrependAndAppendToVar = 87 { 88 name, 89 stdenv', 90 extraAttrs ? { }, 91 }: 92 stdenv'.mkDerivation ( 93 { 94 inherit name; 95 env = { 96 string = "testing-string"; 97 }; 98 99 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ]; 100 buildCommand = '' 101 declare -p string 102 appendToVar string hello 103 # test that quoted strings work 104 prependToVar string "world" 105 declare -p string 106 107 declare -A associativeArray=(["X"]="Y") 108 [[ $(appendToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "appendToVar did not throw appending to associativeArray" && false) 109 [[ $(prependToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not throw prepending associativeArray" && false) 110 111 [[ $string == "world testing-string hello" ]] || (echo "'\$string' was not 'world testing-string hello'" && false) 112 113 # test appending to a unset variable 114 appendToVar nonExistant created hello 115 declare -p nonExistant 116 if [[ -n $__structuredAttrs ]]; then 117 [[ "''${nonExistant[@]}" == "created hello" ]] 118 else 119 # there's a extra " " in front here and a extra " " in the end of prependToVar 120 # shouldn't matter because these functions will mostly be used for $*Flags and the Flag variable will in most cases already exist 121 [[ "$nonExistant" == " created hello" ]] 122 fi 123 124 eval "$extraTest" 125 126 touch $out 127 ''; 128 } 129 // extraAttrs 130 ); 131 132 testConcatTo = 133 { 134 name, 135 stdenv', 136 extraAttrs ? { }, 137 }: 138 stdenv'.mkDerivation ( 139 { 140 inherit name; 141 142 string = "a *"; 143 list = [ 144 "c" 145 "d" 146 ]; 147 148 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ]; 149 buildCommand = '' 150 declare -A associativeArray=(["X"]="Y") 151 [[ $(concatTo nowhere associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatTo did not throw concatenating associativeArray" && false) 152 153 empty_array=() 154 empty_string="" 155 156 declare -a flagsArray 157 concatTo flagsArray string list notset=e=f empty_array=g empty_string=h 158 declare -p flagsArray 159 [[ "''${flagsArray[0]}" == "a" ]] || (echo "'\$flagsArray[0]' was not 'a'" && false) 160 [[ "''${flagsArray[1]}" == "*" ]] || (echo "'\$flagsArray[1]' was not '*'" && false) 161 [[ "''${flagsArray[2]}" == "c" ]] || (echo "'\$flagsArray[2]' was not 'c'" && false) 162 [[ "''${flagsArray[3]}" == "d" ]] || (echo "'\$flagsArray[3]' was not 'd'" && false) 163 [[ "''${flagsArray[4]}" == "e=f" ]] || (echo "'\$flagsArray[4]' was not 'e=f'" && false) 164 [[ "''${flagsArray[5]}" == "g" ]] || (echo "'\$flagsArray[5]' was not 'g'" && false) 165 [[ "''${flagsArray[6]}" == "h" ]] || (echo "'\$flagsArray[6]' was not 'h'" && false) 166 167 # test concatenating to unset variable 168 concatTo nonExistant string list notset=e=f empty_array=g empty_string=h 169 declare -p nonExistant 170 [[ "''${nonExistant[0]}" == "a" ]] || (echo "'\$nonExistant[0]' was not 'a'" && false) 171 [[ "''${nonExistant[1]}" == "*" ]] || (echo "'\$nonExistant[1]' was not '*'" && false) 172 [[ "''${nonExistant[2]}" == "c" ]] || (echo "'\$nonExistant[2]' was not 'c'" && false) 173 [[ "''${nonExistant[3]}" == "d" ]] || (echo "'\$nonExistant[3]' was not 'd'" && false) 174 [[ "''${nonExistant[4]}" == "e=f" ]] || (echo "'\$nonExistant[4]' was not 'e=f'" && false) 175 [[ "''${nonExistant[5]}" == "g" ]] || (echo "'\$nonExistant[5]' was not 'g'" && false) 176 [[ "''${nonExistant[6]}" == "h" ]] || (echo "'\$nonExistant[6]' was not 'h'" && false) 177 178 eval "$extraTest" 179 180 touch $out 181 ''; 182 } 183 // extraAttrs 184 ); 185 186 testConcatStringsSep = 187 { name, stdenv' }: 188 stdenv'.mkDerivation { 189 inherit name; 190 191 # NOTE: Testing with "&" as separator is intentional, because unquoted 192 # "&" has a special meaning in the "${var//pattern/replacement}" syntax. 193 # Cf. https://github.com/NixOS/nixpkgs/pull/318614#discussion_r1706191919 194 passAsFile = [ "buildCommand" ]; 195 buildCommand = '' 196 declare -A associativeArray=(["X"]="Y") 197 [[ $(concatStringsSep ";" associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatStringsSep did not throw concatenating associativeArray" && false) 198 199 string="lorem ipsum dolor sit amet" 200 stringWithSep="$(concatStringsSep "&" string)" 201 [[ "$stringWithSep" == "lorem&ipsum&dolor&sit&amet" ]] || (echo "'\$stringWithSep' was not 'lorem&ipsum&dolor&sit&amet'" && false) 202 203 array=("lorem ipsum" "dolor" "sit amet") 204 arrayWithSep="$(concatStringsSep "&" array)" 205 [[ "$arrayWithSep" == "lorem ipsum&dolor&sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum&dolor&sit amet'" && false) 206 207 array=("lorem ipsum" "dolor" "sit amet") 208 arrayWithSep="$(concatStringsSep "++" array)" 209 [[ "$arrayWithSep" == "lorem ipsum++dolor++sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum++dolor++sit amet'" && false) 210 211 array=("lorem ipsum" "dolor" "sit amet") 212 arrayWithSep="$(concatStringsSep " and " array)" 213 [[ "$arrayWithSep" == "lorem ipsum and dolor and sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum and dolor and sit amet'" && false) 214 215 touch $out 216 ''; 217 }; 218in 219 220{ 221 # tests for hooks in `stdenv.defaultNativeBuildInputs` 222 hooks = lib.recurseIntoAttrs ( 223 import ./hooks.nix { 224 stdenv = bootStdenv; 225 pkgs = earlyPkgs; 226 inherit lib; 227 } 228 ); 229 230 outputs-no-out = 231 runCommand "outputs-no-out-assert" 232 { 233 result = earlierPkgs.testers.testBuildFailure ( 234 bootStdenv.mkDerivation { 235 NIX_DEBUG = 1; 236 name = "outputs-no-out"; 237 outputs = [ "foo" ]; 238 buildPhase = ":"; 239 installPhase = '' 240 touch $foo 241 ''; 242 } 243 ); 244 245 # Assumption: the first output* variable to be configured is 246 # _overrideFirst outputDev "dev" "out" 247 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"; 248 } 249 '' 250 grep -F "$expectedMsg" $result/testBuildFailure.log >/dev/null 251 touch $out 252 ''; 253 254 test-env-attrset = testEnvAttrset { 255 name = "test-env-attrset"; 256 stdenv' = bootStdenv; 257 }; 258 259 # Check that mkDerivation rejects MD5 hashes 260 rejectedHashes = lib.recurseIntoAttrs { 261 md5 = 262 let 263 drv = runCommand "md5 outputHash rejected" { 264 outputHash = "md5-fPt7dxVVP7ffY3MxkQdwVw=="; 265 } "true"; 266 in 267 assert !(builtins.tryEval drv).success; 268 { }; 269 }; 270 271 test-inputDerivation = 272 let 273 inherit 274 (stdenv.mkDerivation { 275 dep1 = derivation { 276 name = "dep1"; 277 builder = "/bin/sh"; 278 args = [ 279 "-c" 280 ": > $out" 281 ]; 282 system = builtins.currentSystem; 283 }; 284 dep2 = derivation { 285 name = "dep2"; 286 builder = "/bin/sh"; 287 args = [ 288 "-c" 289 ": > $out" 290 ]; 291 system = builtins.currentSystem; 292 }; 293 passAsFile = [ "dep2" ]; 294 }) 295 inputDerivation 296 ; 297 in 298 runCommand "test-inputDerivation" 299 { 300 exportReferencesGraph = [ 301 "graph" 302 inputDerivation 303 ]; 304 } 305 '' 306 grep ${inputDerivation.dep1} graph 307 grep ${inputDerivation.dep2} graph 308 touch $out 309 ''; 310 311 test-inputDerivation-fixed-output = 312 let 313 inherit 314 (stdenv.mkDerivation { 315 dep1 = derivation { 316 name = "dep1"; 317 builder = "/bin/sh"; 318 args = [ 319 "-c" 320 ": > $out" 321 ]; 322 system = builtins.currentSystem; 323 }; 324 dep2 = derivation { 325 name = "dep2"; 326 builder = "/bin/sh"; 327 args = [ 328 "-c" 329 ": > $out" 330 ]; 331 system = builtins.currentSystem; 332 }; 333 name = "meow"; 334 outputHash = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="; 335 outputHashMode = "flat"; 336 outputHashAlgo = "sha256"; 337 buildCommand = '' 338 touch $out 339 ''; 340 passAsFile = [ "dep2" ]; 341 }) 342 inputDerivation 343 ; 344 in 345 runCommand "test-inputDerivation" 346 { 347 exportReferencesGraph = [ 348 "graph" 349 inputDerivation 350 ]; 351 } 352 '' 353 grep ${inputDerivation.dep1} graph 354 grep ${inputDerivation.dep2} graph 355 touch $out 356 ''; 357 358 test-prepend-append-to-var = testPrependAndAppendToVar { 359 name = "test-prepend-append-to-var"; 360 stdenv' = bootStdenv; 361 }; 362 363 test-concat-to = testConcatTo { 364 name = "test-concat-to"; 365 stdenv' = bootStdenv; 366 }; 367 368 test-concat-strings-sep = testConcatStringsSep { 369 name = "test-concat-strings-sep"; 370 stdenv' = bootStdenv; 371 }; 372 373 test-structured-env-attrset = testEnvAttrset { 374 name = "test-structured-env-attrset"; 375 stdenv' = bootStdenv; 376 extraAttrs = { 377 __structuredAttrs = true; 378 }; 379 }; 380 381 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest { 382 name = "test-cc-wrapper-substitutions"; 383 stdenv' = bootStdenv; 384 }; 385 386 ensure-no-execve-in-setup-sh = 387 derivation { 388 name = "ensure-no-execve-in-setup-sh"; 389 system = stdenv.system; 390 builder = "${stdenv.bootstrapTools}/bin/bash"; 391 PATH = "${pkgs.strace}/bin:${stdenv.bootstrapTools}/bin"; 392 initialPath = [ 393 stdenv.bootstrapTools 394 pkgs.strace 395 ]; 396 args = [ 397 "-c" 398 '' 399 countCall() { 400 echo "$stats" | tr -s ' ' | grep "$1" | cut -d ' ' -f5 401 } 402 403 # prevent setup.sh from running `nproc` when cores=0 404 # (this would mess up the syscall stats) 405 export NIX_BUILD_CORES=1 406 407 echo "Analyzing setup.sh with strace" 408 stats=$(strace -fc bash -c ". ${../../stdenv/generic/setup.sh}" 2>&1) 409 echo "$stats" | head -n15 410 411 # fail if execve calls is > 1 412 stats=$(strace -fc bash -c ". ${../../stdenv/generic/setup.sh}" 2>&1) 413 execveCalls=$(countCall execve) 414 if [ "$execveCalls" -gt 1 ]; then 415 echo "execve calls: $execveCalls; expected: 1" 416 echo "ERROR: setup.sh should not launch additional processes when being sourced" 417 exit 1 418 else 419 echo "setup.sh doesn't launch extra processes when sourcing, as expected" 420 fi 421 422 touch $out 423 '' 424 ]; 425 } 426 // { 427 meta = { }; 428 }; 429 430 structuredAttrsByDefault = lib.recurseIntoAttrs { 431 432 hooks = lib.recurseIntoAttrs ( 433 import ./hooks.nix { 434 stdenv = bootStdenvStructuredAttrsByDefault; 435 pkgs = earlyPkgs; 436 inherit lib; 437 } 438 ); 439 440 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest { 441 name = "test-cc-wrapper-substitutions-structuredAttrsByDefault"; 442 stdenv' = bootStdenvStructuredAttrsByDefault; 443 }; 444 445 test-structured-env-attrset = testEnvAttrset { 446 name = "test-structured-env-attrset-structuredAttrsByDefault"; 447 stdenv' = bootStdenvStructuredAttrsByDefault; 448 }; 449 450 test-prepend-append-to-var = testPrependAndAppendToVar { 451 name = "test-prepend-append-to-var-structuredAttrsByDefault"; 452 stdenv' = bootStdenvStructuredAttrsByDefault; 453 extraAttrs = { 454 # will be a bash indexed array in attrs.sh 455 # declare -a list=('a' 'b' ) 456 # and a json array in attrs.json 457 # "list":["a","b"] 458 list = [ 459 "a" 460 "b" 461 ]; 462 # will be a bash associative array(dictionary) in attrs.sh 463 # declare -A array=(['a']='1' ['b']='2' ) 464 # and a json object in attrs.json 465 # {"array":{"a":"1","b":"2"} 466 array = { 467 a = "1"; 468 b = "2"; 469 }; 470 extraTest = '' 471 declare -p array 472 array+=(["c"]="3") 473 declare -p array 474 475 [[ "''${array[c]}" == "3" ]] || (echo "c element of '\$array' was not '3'" && false) 476 477 declare -p list 478 prependToVar list hello 479 # test that quoted strings work 480 appendToVar list "world" 481 declare -p list 482 483 [[ "''${list[0]}" == "hello" ]] || (echo "first element of '\$list' was not 'hello'" && false) 484 [[ "''${list[1]}" == "a" ]] || (echo "first element of '\$list' was not 'a'" && false) 485 [[ "''${list[-1]}" == "world" ]] || (echo "last element of '\$list' was not 'world'" && false) 486 ''; 487 }; 488 }; 489 490 test-concat-to = testConcatTo { 491 name = "test-concat-to-structuredAttrsByDefault"; 492 stdenv' = bootStdenvStructuredAttrsByDefault; 493 extraAttrs = { 494 # test that whitespace is kept in the bash array for structuredAttrs 495 listWithSpaces = [ 496 "c c" 497 "d d" 498 ]; 499 extraTest = '' 500 declare -a flagsWithSpaces 501 concatTo flagsWithSpaces string listWithSpaces 502 declare -p flagsWithSpaces 503 [[ "''${flagsWithSpaces[0]}" == "a" ]] || (echo "'\$flagsWithSpaces[0]' was not 'a'" && false) 504 [[ "''${flagsWithSpaces[1]}" == "*" ]] || (echo "'\$flagsWithSpaces[1]' was not '*'" && false) 505 [[ "''${flagsWithSpaces[2]}" == "c c" ]] || (echo "'\$flagsWithSpaces[2]' was not 'c c'" && false) 506 [[ "''${flagsWithSpaces[3]}" == "d d" ]] || (echo "'\$flagsWithSpaces[3]' was not 'd d'" && false) 507 ''; 508 }; 509 }; 510 511 test-concat-strings-sep = testConcatStringsSep { 512 name = "test-concat-strings-sep-structuredAttrsByDefault"; 513 stdenv' = bootStdenvStructuredAttrsByDefault; 514 }; 515 516 test-golden-example-structuredAttrs = 517 let 518 goldenSh = earlyPkgs.writeText "goldenSh" '' 519 declare -A EXAMPLE_ATTRS=(['foo']='bar' ) 520 declare EXAMPLE_BOOL_FALSE= 521 declare EXAMPLE_BOOL_TRUE=1 522 declare EXAMPLE_INT=123 523 declare EXAMPLE_INT_NEG=-123 524 declare -a EXAMPLE_LIST=('foo' 'bar' ) 525 declare EXAMPLE_STR='foo bar' 526 ''; 527 goldenJson = earlyPkgs.writeText "goldenSh" '' 528 { 529 "EXAMPLE_ATTRS": { 530 "foo": "bar" 531 }, 532 "EXAMPLE_BOOL_FALSE": false, 533 "EXAMPLE_BOOL_TRUE": true, 534 "EXAMPLE_INT": 123, 535 "EXAMPLE_INT_NEG": -123, 536 "EXAMPLE_LIST": [ 537 "foo", 538 "bar" 539 ], 540 "EXAMPLE_NESTED_ATTRS": { 541 "foo": { 542 "bar": "baz" 543 } 544 }, 545 "EXAMPLE_NESTED_LIST": [ 546 [ 547 "foo", 548 "bar" 549 ], 550 [ 551 "baz" 552 ] 553 ], 554 "EXAMPLE_STR": "foo bar" 555 } 556 ''; 557 in 558 bootStdenvStructuredAttrsByDefault.mkDerivation { 559 name = "test-golden-example-structuredAttrsByDefault"; 560 nativeBuildInputs = [ earlyPkgs.jq ]; 561 562 EXAMPLE_BOOL_TRUE = true; 563 EXAMPLE_BOOL_FALSE = false; 564 EXAMPLE_INT = 123; 565 EXAMPLE_INT_NEG = -123; 566 EXAMPLE_STR = "foo bar"; 567 EXAMPLE_LIST = [ 568 "foo" 569 "bar" 570 ]; 571 EXAMPLE_NESTED_LIST = [ 572 [ 573 "foo" 574 "bar" 575 ] 576 [ "baz" ] 577 ]; 578 EXAMPLE_ATTRS = { 579 foo = "bar"; 580 }; 581 EXAMPLE_NESTED_ATTRS = { 582 foo.bar = "baz"; 583 }; 584 585 inherit goldenSh; 586 inherit goldenJson; 587 588 buildCommand = '' 589 mkdir -p $out 590 cat $NIX_ATTRS_SH_FILE | grep "EXAMPLE" | grep -v -E 'installPhase|jq' > $out/sh 591 jq 'with_entries(select(.key|match("EXAMPLE")))' $NIX_ATTRS_JSON_FILE > $out/json 592 diff $out/sh $goldenSh 593 diff $out/json $goldenJson 594 ''; 595 }; 596 }; 597}