at 18.09-beta 1300 lines 37 kB view raw
1set -eu 2set -o pipefail 3 4if (( "${NIX_DEBUG:-0}" >= 6 )); then 5 set -x 6fi 7 8: ${outputs:=out} 9 10 11###################################################################### 12# Hook handling. 13 14 15# Run all hooks with the specified name in the order in which they 16# were added, stopping if any fails (returns a non-zero exit 17# code). The hooks for <hookName> are the shell function or variable 18# <hookName>, and the values of the shell array ‘<hookName>Hooks’. 19runHook() { 20 local oldOpts="$(shopt -po nounset)" 21 set -u # May be called from elsewhere, so do `set -u`. 22 23 local hookName="$1" 24 shift 25 local hooksSlice="${hookName%Hook}Hooks[@]" 26 27 local hook 28 # Hack around old bash being bad and thinking empty arrays are 29 # undefined. 30 for hook in "_callImplicitHook 0 $hookName" ${!hooksSlice+"${!hooksSlice}"}; do 31 _eval "$hook" "$@" 32 set -u # To balance `_eval` 33 done 34 35 eval "${oldOpts}" 36 return 0 37} 38 39 40# Run all hooks with the specified name, until one succeeds (returns a 41# zero exit code). If none succeed, return a non-zero exit code. 42runOneHook() { 43 local oldOpts="$(shopt -po nounset)" 44 set -u # May be called from elsewhere, so do `set -u`. 45 46 local hookName="$1" 47 shift 48 local hooksSlice="${hookName%Hook}Hooks[@]" 49 50 local hook ret=1 51 # Hack around old bash like above 52 for hook in "_callImplicitHook 1 $hookName" ${!hooksSlice+"${!hooksSlice}"}; do 53 if _eval "$hook" "$@"; then 54 ret=0 55 break 56 fi 57 set -u # To balance `_eval` 58 done 59 60 eval "${oldOpts}" 61 return "$ret" 62} 63 64 65# Run the named hook, either by calling the function with that name or 66# by evaluating the variable with that name. This allows convenient 67# setting of hooks both from Nix expressions (as attributes / 68# environment variables) and from shell scripts (as functions). If you 69# want to allow multiple hooks, use runHook instead. 70_callImplicitHook() { 71 set -u 72 local def="$1" 73 local hookName="$2" 74 case "$(type -t "$hookName")" in 75 (function|alias|builtin) 76 set +u 77 "$hookName";; 78 (file) 79 set +u 80 source "$hookName";; 81 (keyword) :;; 82 (*) if [ -z "${!hookName:-}" ]; then 83 return "$def"; 84 else 85 set +u 86 eval "${!hookName}" 87 fi;; 88 esac 89 # `_eval` expects hook to need nounset disable and leave it 90 # disabled anyways, so Ok to to delegate. The alternative of a 91 # return trap is no good because it would affect nested returns. 92} 93 94 95# A function wrapper around ‘eval’ that ensures that ‘return’ inside 96# hooks exits the hook, not the caller. Also will only pass args if 97# command can take them 98_eval() { 99 if [ "$(type -t "$1")" = function ]; then 100 set +u 101 "$@" # including args 102 else 103 set +u 104 eval "$1" 105 fi 106 # `run*Hook` reenables `set -u` 107} 108 109 110###################################################################### 111# Logging. 112 113# Obsolete. 114stopNest() { true; } 115header() { echo "$1"; } 116closeNest() { true; } 117 118# Prints a command such that all word splits are unambiguous. We need 119# to split the command in three parts because the middle format string 120# will be, and must be, repeated for each argument. The first argument 121# goes before the ':' and is just for convenience. 122echoCmd() { 123 printf "%s:" "$1" 124 shift 125 printf ' %q' "$@" 126 echo 127} 128 129 130###################################################################### 131# Error handling. 132 133exitHandler() { 134 exitCode="$?" 135 set +e 136 137 if [ -n "${showBuildStats:-}" ]; then 138 times > "$NIX_BUILD_TOP/.times" 139 local -a times=($(cat "$NIX_BUILD_TOP/.times")) 140 # Print the following statistics: 141 # - user time for the shell 142 # - system time for the shell 143 # - user time for all child processes 144 # - system time for all child processes 145 echo "build time elapsed: " "${times[@]}" 146 fi 147 148 if (( "$exitCode" != 0 )); then 149 runHook failureHook 150 151 # If the builder had a non-zero exit code and 152 # $succeedOnFailure is set, create the file 153 # ‘$out/nix-support/failed’ to signal failure, and exit 154 # normally. Otherwise, return the original exit code. 155 if [ -n "${succeedOnFailure:-}" ]; then 156 echo "build failed with exit code $exitCode (ignored)" 157 mkdir -p "$out/nix-support" 158 printf "%s" "$exitCode" > "$out/nix-support/failed" 159 exit 0 160 fi 161 162 else 163 runHook exitHook 164 fi 165 166 exit "$exitCode" 167} 168 169trap "exitHandler" EXIT 170 171 172###################################################################### 173# Helper functions. 174 175 176addToSearchPathWithCustomDelimiter() { 177 local delimiter="$1" 178 local varName="$2" 179 local dir="$3" 180 if [ -d "$dir" ]; then 181 export "${varName}=${!varName:+${!varName}${delimiter}}${dir}" 182 fi 183} 184 185PATH_DELIMITER=':' 186 187addToSearchPath() { 188 addToSearchPathWithCustomDelimiter "${PATH_DELIMITER}" "$@" 189} 190 191# Add $1/lib* into rpaths. 192# The function is used in multiple-outputs.sh hook, 193# so it is defined here but tried after the hook. 194_addRpathPrefix() { 195 if [ "${NIX_NO_SELF_RPATH:-0}" != 1 ]; then 196 export NIX_LDFLAGS="-rpath $1/lib $NIX_LDFLAGS" 197 if [ -n "${NIX_LIB64_IN_SELF_RPATH:-}" ]; then 198 export NIX_LDFLAGS="-rpath $1/lib64 $NIX_LDFLAGS" 199 fi 200 if [ -n "${NIX_LIB32_IN_SELF_RPATH:-}" ]; then 201 export NIX_LDFLAGS="-rpath $1/lib32 $NIX_LDFLAGS" 202 fi 203 fi 204} 205 206# Return success if the specified file is an ELF object. 207isELF() { 208 local fn="$1" 209 local fd 210 local magic 211 exec {fd}< "$fn" 212 read -r -n 4 -u "$fd" magic 213 exec {fd}<&- 214 if [[ "$magic" =~ ELF ]]; then return 0; else return 1; fi 215} 216 217# Return success if the specified file is a script (i.e. starts with 218# "#!"). 219isScript() { 220 local fn="$1" 221 local fd 222 local magic 223 exec {fd}< "$fn" 224 read -r -n 2 -u "$fd" magic 225 exec {fd}<&- 226 if [[ "$magic" =~ \#! ]]; then return 0; else return 1; fi 227} 228 229# printf unfortunately will print a trailing newline regardless 230printLines() { 231 (( "$#" > 0 )) || return 0 232 printf '%s\n' "$@" 233} 234 235printWords() { 236 (( "$#" > 0 )) || return 0 237 printf '%s ' "$@" 238} 239 240###################################################################### 241# Initialisation. 242 243 244# Set a fallback default value for SOURCE_DATE_EPOCH, used by some 245# build tools to provide a deterministic substitute for the "current" 246# time. Note that 1 = 1970-01-01 00:00:01. We don't use 0 because it 247# confuses some applications. 248export SOURCE_DATE_EPOCH 249: ${SOURCE_DATE_EPOCH:=1} 250 251 252# Wildcard expansions that don't match should expand to an empty list. 253# This ensures that, for instance, "for i in *; do ...; done" does the 254# right thing. 255shopt -s nullglob 256 257 258# Set up the initial path. 259PATH= 260for i in $initialPath; do 261 if [ "$i" = / ]; then i=; fi 262 addToSearchPath PATH "$i/bin" 263done 264 265if (( "${NIX_DEBUG:-0}" >= 1 )); then 266 echo "initial path: $PATH" 267fi 268 269 270# Check that the pre-hook initialised SHELL. 271if [ -z "${SHELL:-}" ]; then echo "SHELL not set"; exit 1; fi 272BASH="$SHELL" 273export CONFIG_SHELL="$SHELL" 274 275 276# Dummy implementation of the paxmark function. On Linux, this is 277# overwritten by paxctl's setup hook. 278paxmark() { true; } 279 280 281# Execute the pre-hook. 282if [ -z "${shell:-}" ]; then export shell="$SHELL"; fi 283runHook preHook 284 285 286# Allow the caller to augment buildInputs (it's not always possible to 287# do this before the call to setup.sh, since the PATH is empty at that 288# point; here we have a basic Unix environment). 289runHook addInputsHook 290 291 292# Package accumulators 293 294# shellcheck disable=SC2034 295declare -a pkgsBuildBuild pkgsBuildHost pkgsBuildTarget 296declare -a pkgsHostHost pkgsHostTarget 297declare -a pkgsTargetTarget 298 299declare -ra pkgBuildAccumVars=(pkgsBuildBuild pkgsBuildHost pkgsBuildTarget) 300declare -ra pkgHostAccumVars=(pkgsHostHost pkgsHostTarget) 301declare -ra pkgTargetAccumVars=(pkgsTargetTarget) 302 303declare -ra pkgAccumVarVars=(pkgBuildAccumVars pkgHostAccumVars pkgTargetAccumVars) 304 305 306# Hooks 307 308declare -a envBuildBuildHooks envBuildHostHooks envBuildTargetHooks 309declare -a envHostHostHooks envHostTargetHooks 310declare -a envTargetTargetHooks 311 312declare -ra pkgBuildHookVars=(envBuildBuildHook envBuildHostHook envBuildTargetHook) 313declare -ra pkgHostHookVars=(envHostHostHook envHostTargetHook) 314declare -ra pkgTargetHookVars=(envTargetTargetHook) 315 316declare -ra pkgHookVarVars=(pkgBuildHookVars pkgHostHookVars pkgTargetHookVars) 317 318# Add env hooks for all sorts of deps with the specified host offset. 319addEnvHooks() { 320 local depHostOffset="$1" 321 shift 322 local pkgHookVarsSlice="${pkgHookVarVars[$depHostOffset + 1]}[@]" 323 local pkgHookVar 324 for pkgHookVar in "${!pkgHookVarsSlice}"; do 325 eval "${pkgHookVar}s"'+=("$@")' 326 done 327} 328 329 330# Propagated dep files 331 332declare -ra propagatedBuildDepFiles=( 333 propagated-build-build-deps 334 propagated-native-build-inputs # Legacy name for back-compat 335 propagated-build-target-deps 336) 337declare -ra propagatedHostDepFiles=( 338 propagated-host-host-deps 339 propagated-build-inputs # Legacy name for back-compat 340) 341declare -ra propagatedTargetDepFiles=( 342 propagated-target-target-deps 343) 344declare -ra propagatedDepFilesVars=( 345 propagatedBuildDepFiles 346 propagatedHostDepFiles 347 propagatedTargetDepFiles 348) 349 350# Platform offsets: build = -1, host = 0, target = 1 351declare -ra allPlatOffsets=(-1 0 1) 352 353 354# Mutually-recursively find all build inputs. See the dependency section of the 355# stdenv chapter of the Nixpkgs manual for the specification this algorithm 356# implements. 357findInputs() { 358 local -r pkg="$1" 359 local -ri hostOffset="$2" 360 local -ri targetOffset="$3" 361 362 # Sanity check 363 (( "$hostOffset" <= "$targetOffset" )) || exit -1 364 365 local varVar="${pkgAccumVarVars[$hostOffset + 1]}" 366 local varRef="$varVar[\$targetOffset - \$hostOffset]" 367 local var="${!varRef}" 368 unset -v varVar varRef 369 370 # TODO(@Ericson2314): Restore using associative array once Darwin 371 # nix-shell doesn't use impure bash. This should replace the O(n) 372 # case with an O(1) hash map lookup, assuming bash is implemented 373 # well :D. 374 local varSlice="$var[*]" 375 # ${..-} to hack around old bash empty array problem 376 case "${!varSlice-}" in 377 *" $pkg "*) return 0 ;; 378 esac 379 unset -v varSlice 380 381 eval "$var"'+=("$pkg")' 382 383 if ! [ -e "$pkg" ]; then 384 echo "build input $pkg does not exist" >&2 385 exit 1 386 fi 387 388 # The current package's host and target offset together 389 # provide a <=-preserving homomorphism from the relative 390 # offsets to current offset 391 function mapOffset() { 392 local -ri inputOffset="$1" 393 if (( "$inputOffset" <= 0 )); then 394 local -ri outputOffset="$inputOffset + $hostOffset" 395 else 396 local -ri outputOffset="$inputOffset - 1 + $targetOffset" 397 fi 398 echo "$outputOffset" 399 } 400 401 # Host offset relative to that of the package whose immediate 402 # dependencies we are currently exploring. 403 local -i relHostOffset 404 for relHostOffset in "${allPlatOffsets[@]}"; do 405 # `+ 1` so we start at 0 for valid index 406 local files="${propagatedDepFilesVars[$relHostOffset + 1]}" 407 408 # Host offset relative to the package currently being 409 # built---as absolute an offset as will be used. 410 local -i hostOffsetNext 411 hostOffsetNext="$(mapOffset relHostOffset)" 412 413 # Ensure we're in bounds relative to the package currently 414 # being built. 415 [[ "${allPlatOffsets[*]}" = *"$hostOffsetNext"* ]] || continue 416 417 # Target offset relative to the *host* offset of the package 418 # whose immediate dependencies we are currently exploring. 419 local -i relTargetOffset 420 for relTargetOffset in "${allPlatOffsets[@]}"; do 421 (( "$relHostOffset" <= "$relTargetOffset" )) || continue 422 423 local fileRef="${files}[$relTargetOffset - $relHostOffset]" 424 local file="${!fileRef}" 425 unset -v fileRef 426 427 # Target offset relative to the package currently being 428 # built. 429 local -i targetOffsetNext 430 targetOffsetNext="$(mapOffset relTargetOffset)" 431 432 # Once again, ensure we're in bounds relative to the 433 # package currently being built. 434 [[ "${allPlatOffsets[*]}" = *"$targetOffsetNext"* ]] || continue 435 436 [[ -f "$pkg/nix-support/$file" ]] || continue 437 438 local pkgNext 439 for pkgNext in $(< "$pkg/nix-support/$file"); do 440 findInputs "$pkgNext" "$hostOffsetNext" "$targetOffsetNext" 441 done 442 done 443 done 444} 445 446# Make sure all are at least defined as empty 447: ${depsBuildBuild=} ${depsBuildBuildPropagated=} 448: ${nativeBuildInputs=} ${propagatedNativeBuildInputs=} ${defaultNativeBuildInputs=} 449: ${depsBuildTarget=} ${depsBuildTargetPropagated=} 450: ${depsHostHost=} ${depsHostHostPropagated=} 451: ${buildInputs=} ${propagatedBuildInputs=} ${defaultBuildInputs=} 452: ${depsTargetTarget=} ${depsTargetTargetPropagated=} 453 454for pkg in $depsBuildBuild $depsBuildBuildPropagated; do 455 findInputs "$pkg" -1 -1 456done 457for pkg in $nativeBuildInputs $propagatedNativeBuildInputs; do 458 findInputs "$pkg" -1 0 459done 460for pkg in $depsBuildTarget $depsBuildTargetPropagated; do 461 findInputs "$pkg" -1 1 462done 463for pkg in $depsHostHost $depsHostHostPropagated; do 464 findInputs "$pkg" 0 0 465done 466for pkg in $buildInputs $propagatedBuildInputs ; do 467 findInputs "$pkg" 0 1 468done 469for pkg in $depsTargetTarget $depsTargetTargetPropagated; do 470 findInputs "$pkg" 1 1 471done 472# Default inputs must be processed last 473for pkg in $defaultNativeBuildInputs; do 474 findInputs "$pkg" -1 0 475done 476for pkg in $defaultBuildInputs; do 477 findInputs "$pkg" 0 1 478done 479 480# Add package to the future PATH and run setup hooks 481activatePackage() { 482 local pkg="$1" 483 local -ri hostOffset="$2" 484 local -ri targetOffset="$3" 485 486 # Sanity check 487 (( "$hostOffset" <= "$targetOffset" )) || exit -1 488 489 if [ -f "$pkg" ]; then 490 local oldOpts="$(shopt -po nounset)" 491 set +u 492 source "$pkg" 493 eval "$oldOpts" 494 fi 495 496 # Only dependencies whose host platform is guaranteed to match the 497 # build platform are included here. That would be `depsBuild*`, 498 # and legacy `nativeBuildInputs`, in general. If we aren't cross 499 # compiling, however, everything can be put on the PATH. To ease 500 # the transition, we do include everything in thatcase. 501 # 502 # TODO(@Ericson2314): Don't special-case native compilation 503 if [[ ( -z "${strictDeps-}" || "$hostOffset" -le -1 ) && -d "$pkg/bin" ]]; then 504 addToSearchPath _PATH "$pkg/bin" 505 fi 506 507 if [[ "$hostOffset" -eq 0 && -d "$pkg/bin" ]]; then 508 addToSearchPath HOST_PATH "$pkg/bin" 509 fi 510 511 if [[ -f "$pkg/nix-support/setup-hook" ]]; then 512 local oldOpts="$(shopt -po nounset)" 513 set +u 514 source "$pkg/nix-support/setup-hook" 515 eval "$oldOpts" 516 fi 517} 518 519_activatePkgs() { 520 local -i hostOffset targetOffset 521 local pkg 522 523 for hostOffset in "${allPlatOffsets[@]}"; do 524 local pkgsVar="${pkgAccumVarVars[$hostOffset + 1]}" 525 for targetOffset in "${allPlatOffsets[@]}"; do 526 (( "$hostOffset" <= "$targetOffset" )) || continue 527 local pkgsRef="${pkgsVar}[$targetOffset - $hostOffset]" 528 local pkgsSlice="${!pkgsRef}[@]" 529 for pkg in ${!pkgsSlice+"${!pkgsSlice}"}; do 530 activatePackage "$pkg" "$hostOffset" "$targetOffset" 531 done 532 done 533 done 534} 535 536# Run the package setup hooks and build _PATH 537_activatePkgs 538 539# Set the relevant environment variables to point to the build inputs 540# found above. 541# 542# These `depOffset`s, beyond indexing the arrays, also tell the env 543# hook what sort of dependency (ignoring propagatedness) is being 544# passed to the env hook. In a real language, we'd append a closure 545# with this information to the relevant env hook array, but bash 546# doesn't have closures, so it's easier to just pass this in. 547_addToEnv() { 548 local -i depHostOffset depTargetOffset 549 local pkg 550 551 for depHostOffset in "${allPlatOffsets[@]}"; do 552 local hookVar="${pkgHookVarVars[$depHostOffset + 1]}" 553 local pkgsVar="${pkgAccumVarVars[$depHostOffset + 1]}" 554 for depTargetOffset in "${allPlatOffsets[@]}"; do 555 (( "$depHostOffset" <= "$depTargetOffset" )) || continue 556 local hookRef="${hookVar}[$depTargetOffset - $depHostOffset]" 557 if [[ -z "${strictDeps-}" ]]; then 558 # Apply environment hooks to all packages during native 559 # compilation to ease the transition. 560 # 561 # TODO(@Ericson2314): Don't special-case native compilation 562 for pkg in \ 563 ${pkgsBuildBuild+"${pkgsBuildBuild[@]}"} \ 564 ${pkgsBuildHost+"${pkgsBuildHost[@]}"} \ 565 ${pkgsBuildTarget+"${pkgsBuildTarget[@]}"} \ 566 ${pkgsHostHost+"${pkgsHostHost[@]}"} \ 567 ${pkgsHostTarget+"${pkgsHostTarget[@]}"} \ 568 ${pkgsTargetTarget+"${pkgsTargetTarget[@]}"} 569 do 570 runHook "${!hookRef}" "$pkg" 571 done 572 else 573 local pkgsRef="${pkgsVar}[$depTargetOffset - $depHostOffset]" 574 local pkgsSlice="${!pkgsRef}[@]" 575 for pkg in ${!pkgsSlice+"${!pkgsSlice}"}; do 576 runHook "${!hookRef}" "$pkg" 577 done 578 fi 579 done 580 done 581} 582 583# Run the package-specific hooks set by the setup-hook scripts. 584_addToEnv 585 586 587_addRpathPrefix "$out" 588 589 590# Set the TZ (timezone) environment variable, otherwise commands like 591# `date' will complain (e.g., `Tue Mar 9 10:01:47 Local time zone must 592# be set--see zic manual page 2004'). 593export TZ=UTC 594 595 596# Set the prefix. This is generally $out, but it can be overriden, 597# for instance if we just want to perform a test build/install to a 598# temporary location and write a build report to $out. 599if [ -z "${prefix:-}" ]; then 600 prefix="$out"; 601fi 602 603if [ "${useTempPrefix:-}" = 1 ]; then 604 prefix="$NIX_BUILD_TOP/tmp_prefix"; 605fi 606 607 608PATH="${_PATH-}${_PATH:+${PATH:+:}}$PATH" 609if (( "${NIX_DEBUG:-0}" >= 1 )); then 610 echo "final path: $PATH" 611fi 612 613 614# Make GNU Make produce nested output. 615export NIX_INDENT_MAKE=1 616 617 618# Normalize the NIX_BUILD_CORES variable. The value might be 0, which 619# means that we're supposed to try and auto-detect the number of 620# available CPU cores at run-time. 621 622if [ -z "${NIX_BUILD_CORES:-}" ]; then 623 NIX_BUILD_CORES="1" 624elif [ "$NIX_BUILD_CORES" -le 0 ]; then 625 NIX_BUILD_CORES=$(nproc 2>/dev/null || true) 626 if expr >/dev/null 2>&1 "$NIX_BUILD_CORES" : "^[0-9][0-9]*$"; then 627 : 628 else 629 NIX_BUILD_CORES="1" 630 fi 631fi 632export NIX_BUILD_CORES 633 634 635# Prevent OpenSSL-based applications from using certificates in 636# /etc/ssl. 637# Leave it in shells for convenience. 638if [ -z "${SSL_CERT_FILE:-}" ] && [ -z "${IN_NIX_SHELL:-}" ]; then 639 export SSL_CERT_FILE=/no-cert-file.crt 640fi 641 642 643###################################################################### 644# Textual substitution functions. 645 646 647substituteStream() { 648 local var=$1 649 shift 650 651 while (( "$#" )); do 652 case "$1" in 653 --replace) 654 pattern="$2" 655 replacement="$3" 656 shift 3 657 ;; 658 659 --subst-var) 660 local varName="$2" 661 shift 2 662 # check if the used nix attribute name is a valid bash name 663 if ! [[ "$varName" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then 664 echo "substituteStream(): ERROR: substitution variables must be valid Bash names, \"$varName\" isn't." >&2 665 return 1 666 fi 667 if [ -z ${!varName+x} ]; then 668 echo "substituteStream(): ERROR: variable \$$varName is unset" >&2 669 return 1 670 fi 671 pattern="@$varName@" 672 replacement="${!varName}" 673 ;; 674 675 --subst-var-by) 676 pattern="@$2@" 677 replacement="$3" 678 shift 3 679 ;; 680 681 *) 682 echo "substituteStream(): ERROR: Invalid command line argument: $1" >&2 683 return 1 684 ;; 685 esac 686 687 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}' 688 done 689 690 printf "%s" "${!var}" 691} 692 693consumeEntire() { 694 # read returns non-0 on EOF, so we want read to fail 695 if IFS='' read -r -N 0 $1; then 696 echo "consumeEntire(): ERROR: Input null bytes, won't process" >&2 697 return 1 698 fi 699} 700 701substitute() { 702 local input="$1" 703 local output="$2" 704 shift 2 705 706 if [ ! -f "$input" ]; then 707 echo "substitute(): ERROR: file '$input' does not exist" >&2 708 return 1 709 fi 710 711 local content 712 consumeEntire content < "$input" 713 714 if [ -e "$output" ]; then chmod +w "$output"; fi 715 substituteStream content "$@" > "$output" 716} 717 718substituteInPlace() { 719 local fileName="$1" 720 shift 721 substitute "$fileName" "$fileName" "$@" 722} 723 724_allFlags() { 725 for varName in $(awk 'BEGIN { for (v in ENVIRON) if (v ~ /^[a-z][a-zA-Z0-9_]*$/) print v }'); do 726 if (( "${NIX_DEBUG:-0}" >= 1 )); then 727 printf "@%s@ -> %q\n" "${varName}" "${!varName}" 728 fi 729 args+=("--subst-var" "$varName") 730 done 731} 732 733substituteAllStream() { 734 local -a args=() 735 _allFlags 736 737 substituteStream "$1" "${args[@]}" 738} 739 740# Substitute all environment variables that start with a lowercase character and 741# are valid Bash names. 742substituteAll() { 743 local input="$1" 744 local output="$2" 745 746 local -a args=() 747 _allFlags 748 749 substitute "$input" "$output" "${args[@]}" 750} 751 752 753substituteAllInPlace() { 754 local fileName="$1" 755 shift 756 substituteAll "$fileName" "$fileName" "$@" 757} 758 759 760###################################################################### 761# What follows is the generic builder. 762 763 764# This function is useful for debugging broken Nix builds. It dumps 765# all environment variables to a file `env-vars' in the build 766# directory. If the build fails and the `-K' option is used, you can 767# then go to the build directory and source in `env-vars' to reproduce 768# the environment used for building. 769dumpVars() { 770 if [ "${noDumpEnvVars:-0}" != 1 ]; then 771 export > "$NIX_BUILD_TOP/env-vars" || true 772 fi 773} 774 775 776# Utility function: echo the base name of the given path, with the 777# prefix `HASH-' removed, if present. 778stripHash() { 779 local strippedName 780 # On separate line for `set -e` 781 strippedName="$(basename "$1")" 782 if echo "$strippedName" | grep -q '^[a-z0-9]\{32\}-'; then 783 echo "$strippedName" | cut -c34- 784 else 785 echo "$strippedName" 786 fi 787} 788 789 790unpackCmdHooks+=(_defaultUnpack) 791_defaultUnpack() { 792 local fn="$1" 793 794 if [ -d "$fn" ]; then 795 796 # We can't preserve hardlinks because they may have been 797 # introduced by store optimization, which might break things 798 # in the build. 799 cp -pr --reflink=auto -- "$fn" "$(stripHash "$fn")" 800 801 else 802 803 case "$fn" in 804 *.tar.xz | *.tar.lzma | *.txz) 805 # Don't rely on tar knowing about .xz. 806 xz -d < "$fn" | tar xf - 807 ;; 808 *.tar | *.tar.* | *.tgz | *.tbz2 | *.tbz) 809 # GNU tar can automatically select the decompression method 810 # (info "(tar) gzip"). 811 tar xf "$fn" 812 ;; 813 *) 814 return 1 815 ;; 816 esac 817 818 fi 819} 820 821 822unpackFile() { 823 curSrc="$1" 824 header "unpacking source archive $curSrc" 3 825 if ! runOneHook unpackCmd "$curSrc"; then 826 echo "do not know how to unpack source archive $curSrc" 827 exit 1 828 fi 829} 830 831 832unpackPhase() { 833 runHook preUnpack 834 835 if [ -z "${srcs:-}" ]; then 836 if [ -z "${src:-}" ]; then 837 # shellcheck disable=SC2016 838 echo 'variable $src or $srcs should point to the source' 839 exit 1 840 fi 841 srcs="$src" 842 fi 843 844 # To determine the source directory created by unpacking the 845 # source archives, we record the contents of the current 846 # directory, then look below which directory got added. Yeah, 847 # it's rather hacky. 848 local dirsBefore="" 849 for i in *; do 850 if [ -d "$i" ]; then 851 dirsBefore="$dirsBefore $i " 852 fi 853 done 854 855 # Unpack all source archives. 856 for i in $srcs; do 857 unpackFile "$i" 858 done 859 860 # Find the source directory. 861 862 # set to empty if unset 863 : ${sourceRoot=} 864 865 if [ -n "${setSourceRoot:-}" ]; then 866 runOneHook setSourceRoot 867 elif [ -z "$sourceRoot" ]; then 868 for i in *; do 869 if [ -d "$i" ]; then 870 case $dirsBefore in 871 *\ $i\ *) 872 ;; 873 *) 874 if [ -n "$sourceRoot" ]; then 875 echo "unpacker produced multiple directories" 876 exit 1 877 fi 878 sourceRoot="$i" 879 ;; 880 esac 881 fi 882 done 883 fi 884 885 if [ -z "$sourceRoot" ]; then 886 echo "unpacker appears to have produced no directories" 887 exit 1 888 fi 889 890 echo "source root is $sourceRoot" 891 892 # By default, add write permission to the sources. This is often 893 # necessary when sources have been copied from other store 894 # locations. 895 if [ "${dontMakeSourcesWritable:-0}" != 1 ]; then 896 chmod -R u+w -- "$sourceRoot" 897 fi 898 899 runHook postUnpack 900} 901 902 903patchPhase() { 904 runHook prePatch 905 906 for i in ${patches:-}; do 907 header "applying patch $i" 3 908 local uncompress=cat 909 case "$i" in 910 *.gz) 911 uncompress="gzip -d" 912 ;; 913 *.bz2) 914 uncompress="bzip2 -d" 915 ;; 916 *.xz) 917 uncompress="xz -d" 918 ;; 919 *.lzma) 920 uncompress="lzma -d" 921 ;; 922 esac 923 # "2>&1" is a hack to make patch fail if the decompressor fails (nonexistent patch, etc.) 924 # shellcheck disable=SC2086 925 $uncompress < "$i" 2>&1 | patch ${patchFlags:--p1} 926 done 927 928 runHook postPatch 929} 930 931 932fixLibtool() { 933 sed -i -e 's^eval sys_lib_.*search_path=.*^^' "$1" 934} 935 936 937configurePhase() { 938 runHook preConfigure 939 940 # set to empty if unset 941 : ${configureScript=} 942 : ${configureFlags=} 943 944 if [[ -z "$configureScript" && -x ./configure ]]; then 945 configureScript=./configure 946 fi 947 948 if [ -z "${dontFixLibtool:-}" ]; then 949 local i 950 find . -iname "ltmain.sh" -print0 | while IFS='' read -r -d '' i; do 951 echo "fixing libtool script $i" 952 fixLibtool "$i" 953 done 954 fi 955 956 if [[ -z "${dontAddPrefix:-}" && -n "$prefix" ]]; then 957 configureFlags="${prefixKey:---prefix=}$prefix $configureFlags" 958 fi 959 960 # Add --disable-dependency-tracking to speed up some builds. 961 if [ -z "${dontAddDisableDepTrack:-}" ]; then 962 if [ -f "$configureScript" ] && grep -q dependency-tracking "$configureScript"; then 963 configureFlags="--disable-dependency-tracking $configureFlags" 964 fi 965 fi 966 967 # By default, disable static builds. 968 if [ -z "${dontDisableStatic:-}" ]; then 969 if [ -f "$configureScript" ] && grep -q enable-static "$configureScript"; then 970 configureFlags="--disable-static $configureFlags" 971 fi 972 fi 973 974 if [ -n "$configureScript" ]; then 975 # Old bash empty array hack 976 # shellcheck disable=SC2086 977 local flagsArray=( 978 $configureFlags ${configureFlagsArray+"${configureFlagsArray[@]}"} 979 ) 980 echoCmd 'configure flags' "${flagsArray[@]}" 981 # shellcheck disable=SC2086 982 $configureScript "${flagsArray[@]}" 983 unset flagsArray 984 else 985 echo "no configure script, doing nothing" 986 fi 987 988 runHook postConfigure 989} 990 991 992buildPhase() { 993 runHook preBuild 994 995 # set to empty if unset 996 : ${makeFlags=} 997 998 if [[ -z "$makeFlags" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then 999 echo "no Makefile, doing nothing" 1000 else 1001 foundMakefile=1 1002 1003 # See https://github.com/NixOS/nixpkgs/pull/1354#issuecomment-31260409 1004 makeFlags="SHELL=$SHELL $makeFlags" 1005 1006 # Old bash empty array hack 1007 # shellcheck disable=SC2086 1008 local flagsArray=( 1009 ${enableParallelBuilding:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} 1010 $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} 1011 $buildFlags ${buildFlagsArray+"${buildFlagsArray[@]}"} 1012 ) 1013 1014 echoCmd 'build flags' "${flagsArray[@]}" 1015 make ${makefile:+-f $makefile} "${flagsArray[@]}" 1016 unset flagsArray 1017 fi 1018 1019 runHook postBuild 1020} 1021 1022 1023checkPhase() { 1024 runHook preCheck 1025 1026 if [[ -z "${foundMakefile:-}" ]]; then 1027 echo "no Makefile or custom buildPhase, doing nothing" 1028 runHook postCheck 1029 return 1030 fi 1031 1032 if [[ -z "${checkTarget:-}" ]]; then 1033 #TODO(@oxij): should flagsArray influence make -n? 1034 if make -n ${makefile:+-f $makefile} check >/dev/null 2>&1; then 1035 checkTarget=check 1036 elif make -n ${makefile:+-f $makefile} test >/dev/null 2>&1; then 1037 checkTarget=test 1038 fi 1039 fi 1040 1041 if [[ -z "${checkTarget:-}" ]]; then 1042 echo "no check/test target in ${makefile:-Makefile}, doing nothing" 1043 else 1044 # Old bash empty array hack 1045 # shellcheck disable=SC2086 1046 local flagsArray=( 1047 ${enableParallelBuilding:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} 1048 $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} 1049 ${checkFlags:-VERBOSE=y} ${checkFlagsArray+"${checkFlagsArray[@]}"} 1050 ${checkTarget} 1051 ) 1052 1053 echoCmd 'check flags' "${flagsArray[@]}" 1054 make ${makefile:+-f $makefile} "${flagsArray[@]}" 1055 1056 unset flagsArray 1057 fi 1058 1059 runHook postCheck 1060} 1061 1062 1063installPhase() { 1064 runHook preInstall 1065 1066 if [ -n "$prefix" ]; then 1067 mkdir -p "$prefix" 1068 fi 1069 1070 # Old bash empty array hack 1071 # shellcheck disable=SC2086 1072 local flagsArray=( 1073 $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} 1074 $installFlags ${installFlagsArray+"${installFlagsArray[@]}"} 1075 ${installTargets:-install} 1076 ) 1077 1078 echoCmd 'install flags' "${flagsArray[@]}" 1079 make ${makefile:+-f $makefile} "${flagsArray[@]}" 1080 unset flagsArray 1081 1082 runHook postInstall 1083} 1084 1085 1086# The fixup phase performs generic, package-independent stuff, like 1087# stripping binaries, running patchelf and setting 1088# propagated-build-inputs. 1089fixupPhase() { 1090 # Make sure everything is writable so "strip" et al. work. 1091 local output 1092 for output in $outputs; do 1093 if [ -e "${!output}" ]; then chmod -R u+w "${!output}"; fi 1094 done 1095 1096 runHook preFixup 1097 1098 # Apply fixup to each output. 1099 local output 1100 for output in $outputs; do 1101 prefix="${!output}" runHook fixupOutput 1102 done 1103 1104 1105 # Propagate dependencies & setup hook into the development output. 1106 declare -ra flatVars=( 1107 # Build 1108 depsBuildBuildPropagated 1109 propagatedNativeBuildInputs 1110 depsBuildTargetPropagated 1111 # Host 1112 depsHostHostPropagated 1113 propagatedBuildInputs 1114 # Target 1115 depsTargetTargetPropagated 1116 ) 1117 declare -ra flatFiles=( 1118 "${propagatedBuildDepFiles[@]}" 1119 "${propagatedHostDepFiles[@]}" 1120 "${propagatedTargetDepFiles[@]}" 1121 ) 1122 1123 local propagatedInputsIndex 1124 for propagatedInputsIndex in "${!flatVars[@]}"; do 1125 local propagatedInputsSlice="${flatVars[$propagatedInputsIndex]}[@]" 1126 local propagatedInputsFile="${flatFiles[$propagatedInputsIndex]}" 1127 1128 [[ "${!propagatedInputsSlice}" ]] || continue 1129 1130 mkdir -p "${!outputDev}/nix-support" 1131 # shellcheck disable=SC2086 1132 printWords ${!propagatedInputsSlice} > "${!outputDev}/nix-support/$propagatedInputsFile" 1133 done 1134 1135 1136 if [ -n "${setupHook:-}" ]; then 1137 mkdir -p "${!outputDev}/nix-support" 1138 substituteAll "$setupHook" "${!outputDev}/nix-support/setup-hook" 1139 fi 1140 1141 # TODO(@Ericson2314): Remove after https://github.com/NixOS/nixpkgs/pull/31414 1142 if [ -n "${setupHooks:-}" ]; then 1143 mkdir -p "${!outputDev}/nix-support" 1144 local hook 1145 for hook in $setupHooks; do 1146 local content 1147 consumeEntire content < "$hook" 1148 substituteAllStream content >> "${!outputDev}/nix-support/setup-hook" 1149 unset -v content 1150 done 1151 unset -v hook 1152 fi 1153 1154 # Propagate user-env packages into the output with binaries, TODO? 1155 1156 if [ -n "${propagatedUserEnvPkgs:-}" ]; then 1157 mkdir -p "${!outputBin}/nix-support" 1158 # shellcheck disable=SC2086 1159 printWords $propagatedUserEnvPkgs > "${!outputBin}/nix-support/propagated-user-env-packages" 1160 fi 1161 1162 runHook postFixup 1163} 1164 1165 1166installCheckPhase() { 1167 runHook preInstallCheck 1168 1169 if [[ -z "${foundMakefile:-}" ]]; then 1170 echo "no Makefile or custom buildPhase, doing nothing" 1171 #TODO(@oxij): should flagsArray influence make -n? 1172 elif [[ -z "${installCheckTarget:-}" ]] \ 1173 && ! make -n ${makefile:+-f $makefile} ${installCheckTarget:-installcheck} >/dev/null 2>&1; then 1174 echo "no installcheck target in ${makefile:-Makefile}, doing nothing" 1175 else 1176 # Old bash empty array hack 1177 # shellcheck disable=SC2086 1178 local flagsArray=( 1179 ${enableParallelBuilding:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} 1180 $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} 1181 $installCheckFlags ${installCheckFlagsArray+"${installCheckFlagsArray[@]}"} 1182 ${installCheckTarget:-installcheck} 1183 ) 1184 1185 echoCmd 'installcheck flags' "${flagsArray[@]}" 1186 make ${makefile:+-f $makefile} "${flagsArray[@]}" 1187 unset flagsArray 1188 fi 1189 1190 runHook postInstallCheck 1191} 1192 1193 1194distPhase() { 1195 runHook preDist 1196 1197 # Old bash empty array hack 1198 # shellcheck disable=SC2086 1199 local flagsArray=( 1200 $distFlags ${distFlagsArray+"${distFlagsArray[@]}"} ${distTarget:-dist} 1201 ) 1202 1203 echo 'dist flags: %q' "${flagsArray[@]}" 1204 make ${makefile:+-f $makefile} "${flagsArray[@]}" 1205 1206 if [ "${dontCopyDist:-0}" != 1 ]; then 1207 mkdir -p "$out/tarballs" 1208 1209 # Note: don't quote $tarballs, since we explicitly permit 1210 # wildcards in there. 1211 # shellcheck disable=SC2086 1212 cp -pvd ${tarballs:-*.tar.gz} "$out/tarballs" 1213 fi 1214 1215 runHook postDist 1216} 1217 1218 1219showPhaseHeader() { 1220 local phase="$1" 1221 case "$phase" in 1222 unpackPhase) header "unpacking sources";; 1223 patchPhase) header "patching sources";; 1224 configurePhase) header "configuring";; 1225 buildPhase) header "building";; 1226 checkPhase) header "running tests";; 1227 installPhase) header "installing";; 1228 fixupPhase) header "post-installation fixup";; 1229 installCheckPhase) header "running install tests";; 1230 *) header "$phase";; 1231 esac 1232} 1233 1234 1235genericBuild() { 1236 if [ -f "${buildCommandPath:-}" ]; then 1237 local oldOpts="$(shopt -po nounset)" 1238 set +u 1239 source "$buildCommandPath" 1240 eval "$oldOpts" 1241 return 1242 fi 1243 if [ -n "${buildCommand:-}" ]; then 1244 local oldOpts="$(shopt -po nounset)" 1245 set +u 1246 eval "$buildCommand" 1247 eval "$oldOpts" 1248 return 1249 fi 1250 1251 if [ -z "${phases:-}" ]; then 1252 phases="${prePhases:-} unpackPhase patchPhase ${preConfigurePhases:-} \ 1253 configurePhase ${preBuildPhases:-} buildPhase checkPhase \ 1254 ${preInstallPhases:-} installPhase ${preFixupPhases:-} fixupPhase installCheckPhase \ 1255 ${preDistPhases:-} distPhase ${postPhases:-}"; 1256 fi 1257 1258 for curPhase in $phases; do 1259 if [[ "$curPhase" = buildPhase && -n "${dontBuild:-}" ]]; then continue; fi 1260 if [[ "$curPhase" = checkPhase && -z "${doCheck:-}" ]]; then continue; fi 1261 if [[ "$curPhase" = installPhase && -n "${dontInstall:-}" ]]; then continue; fi 1262 if [[ "$curPhase" = fixupPhase && -n "${dontFixup:-}" ]]; then continue; fi 1263 if [[ "$curPhase" = installCheckPhase && -z "${doInstallCheck:-}" ]]; then continue; fi 1264 if [[ "$curPhase" = distPhase && -z "${doDist:-}" ]]; then continue; fi 1265 1266 if [[ -n $NIX_LOG_FD ]]; then 1267 echo "@nix { \"action\": \"setPhase\", \"phase\": \"$curPhase\" }" >&$NIX_LOG_FD 1268 fi 1269 1270 showPhaseHeader "$curPhase" 1271 dumpVars 1272 1273 # Evaluate the variable named $curPhase if it exists, otherwise the 1274 # function named $curPhase. 1275 local oldOpts="$(shopt -po nounset)" 1276 set +u 1277 eval "${!curPhase:-$curPhase}" 1278 eval "$oldOpts" 1279 1280 if [ "$curPhase" = unpackPhase ]; then 1281 cd "${sourceRoot:-.}" 1282 fi 1283 done 1284} 1285 1286 1287# Execute the post-hooks. 1288runHook postHook 1289 1290 1291# Execute the global user hook (defined through the Nixpkgs 1292# configuration option ‘stdenv.userHook’). This can be used to set 1293# global compiler optimisation flags, for instance. 1294runHook userHook 1295 1296 1297dumpVars 1298 1299# Disable nounset for nix-shell. 1300set +u