Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# shellcheck shell=bash
2# shellcheck disable=1090,2154,2123,2034,2178,2048,2068,1091
3__nixpkgs_setup_set_original=$-
4set -eu
5set -o pipefail
6
7if [[ -n "${BASH_VERSINFO-}" && "${BASH_VERSINFO-}" -lt 5 ]]; then
8 echo "Detected Bash version that isn't supported by Nixpkgs (${BASH_VERSION})"
9 echo "Please install Bash 5 or greater to continue."
10 exit 1
11fi
12
13shopt -s inherit_errexit
14
15# $NIX_DEBUG must be a documented integer level, if set, so we can use it safely as an integer.
16# See the `Verbosity` enum in the Nix source for these levels.
17if ! [[ -z ${NIX_DEBUG-} || $NIX_DEBUG == [0-7] ]]; then
18 # shellcheck disable=SC2016
19 printf 'The `NIX_DEBUG` environment variable has an unexpected value: %s\n' "${NIX_DEBUG}"
20 echo "It can only be unset or an integer between 0 and 7."
21 exit 1
22fi
23
24if [[ ${NIX_DEBUG:-0} -ge 6 ]]; then
25 set -x
26fi
27
28if [ -f .attrs.sh ] || [[ -n "${NIX_ATTRS_JSON_FILE:-}" ]]; then
29 __structuredAttrs=1
30 echo "structuredAttrs is enabled"
31
32 for outputName in "${!outputs[@]}"; do
33 # ex: out=/nix/store/...
34 export "$outputName=${outputs[$outputName]}"
35 done
36
37 # $NIX_ATTRS_JSON_FILE pointed to the wrong location in sandbox
38 # https://github.com/NixOS/nix/issues/6736; please keep around until the
39 # fix reaches *every patch version* that's >= lib/minver.nix
40 if ! [[ -e "${NIX_ATTRS_JSON_FILE:-}" ]]; then
41 export NIX_ATTRS_JSON_FILE="$NIX_BUILD_TOP/.attrs.json"
42 fi
43 if ! [[ -e "${NIX_ATTRS_SH_FILE:-}" ]]; then
44 export NIX_ATTRS_SH_FILE="$NIX_BUILD_TOP/.attrs.sh"
45 fi
46else
47 __structuredAttrs=
48 : "${outputs:=out}"
49fi
50
51getAllOutputNames() {
52 if [ -n "$__structuredAttrs" ]; then
53 echo "${!outputs[*]}"
54 else
55 echo "$outputs"
56 fi
57}
58
59# All provided arguments are joined with a space, then prefixed by the name of the function which invoked `nixLog` (or
60# the hook name if the caller was an implicit hook), then directed to $NIX_LOG_FD, if it's set.
61nixLog() {
62 # Return a value explicitly instead of the implicit return of the last command (result of the test).
63 # NOTE: By requiring NIX_LOG_FD be set, we avoid dumping logging inside of nix-shell.
64 [[ -z ${NIX_LOG_FD-} ]] && return 0
65
66 # Use the function name of the caller, unless it is _callImplicitHook, in which case use the name of the hook.
67 local callerName="${FUNCNAME[1]}"
68 if [[ $callerName == "_callImplicitHook" ]]; then
69 callerName="${hookName:?}"
70 fi
71 printf "%s: %s\n" "$callerName" "$*" >&"$NIX_LOG_FD"
72}
73
74# Identical to nixLog, but additionally prefixed by the logLevel.
75# NOTE: This function is only every meant to be called from the nix*Log family of functions.
76_nixLogWithLevel() {
77 # Return a value explicitly instead of the implicit return of the last command (result of the test).
78 # NOTE: By requiring NIX_LOG_FD be set, we avoid dumping logging inside of nix-shell.
79 [[ -z ${NIX_LOG_FD-} || ${NIX_DEBUG:-0} -lt ${1:?} ]] && return 0
80
81 local logLevel
82 case "${1:?}" in
83 0) logLevel=ERROR ;;
84 1) logLevel=WARN ;;
85 2) logLevel=NOTICE ;;
86 3) logLevel=INFO ;;
87 4) logLevel=TALKATIVE ;;
88 5) logLevel=CHATTY ;;
89 6) logLevel=DEBUG ;;
90 7) logLevel=VOMIT ;;
91 *)
92 echo "_nixLogWithLevel: called with invalid log level: ${1:?}" >&"$NIX_LOG_FD"
93 return 1
94 ;;
95 esac
96
97 # Use the function name of the caller, unless it is _callImplicitHook, in which case use the name of the hook.
98 # NOTE: Our index into FUNCNAME is 2, not 1, because we are only ever to be called from the nix*Log family of
99 # functions, never directly.
100 local callerName="${FUNCNAME[2]}"
101 if [[ $callerName == "_callImplicitHook" ]]; then
102 callerName="${hookName:?}"
103 fi
104
105 # Use the function name of the caller's caller, since we should only every be invoked by nix*Log functions.
106 printf "%s: %s: %s\n" "$logLevel" "$callerName" "${2:?}" >&"$NIX_LOG_FD"
107}
108
109# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
110# Corresponds to `Verbosity::lvlError` in the Nix source.
111nixErrorLog() {
112 _nixLogWithLevel 0 "$*"
113}
114
115# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
116# Corresponds to `Verbosity::lvlWarn` in the Nix source.
117nixWarnLog() {
118 _nixLogWithLevel 1 "$*"
119}
120
121# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
122# Corresponds to `Verbosity::lvlNotice` in the Nix source.
123nixNoticeLog() {
124 _nixLogWithLevel 2 "$*"
125}
126
127# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
128# Corresponds to `Verbosity::lvlInfo` in the Nix source.
129nixInfoLog() {
130 _nixLogWithLevel 3 "$*"
131}
132
133# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
134# Corresponds to `Verbosity::lvlTalkative` in the Nix source.
135nixTalkativeLog() {
136 _nixLogWithLevel 4 "$*"
137}
138
139# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
140# Corresponds to `Verbosity::lvlChatty` in the Nix source.
141nixChattyLog() {
142 _nixLogWithLevel 5 "$*"
143}
144
145# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
146# Corresponds to `Verbosity::lvlDebug` in the Nix source.
147nixDebugLog() {
148 _nixLogWithLevel 6 "$*"
149}
150
151# All provided arguments are joined with a space then directed to $NIX_LOG_FD, if it's set.
152# Corresponds to `Verbosity::lvlVomit` in the Nix source.
153nixVomitLog() {
154 _nixLogWithLevel 7 "$*"
155}
156
157# Log a hook, to be run before the hook is actually called.
158# logging for "implicit" hooks -- the ones specified directly
159# in derivation's arguments -- is done in _callImplicitHook instead.
160_logHook() {
161 # Fast path in case nixTalkativeLog is no-op.
162 if [[ -z ${NIX_LOG_FD-} ]]; then
163 return
164 fi
165
166 local hookKind="$1"
167 local hookExpr="$2"
168 shift 2
169
170 if declare -F "$hookExpr" > /dev/null 2>&1; then
171 nixTalkativeLog "calling '$hookKind' function hook '$hookExpr'" "$@"
172 elif type -p "$hookExpr" > /dev/null; then
173 nixTalkativeLog "sourcing '$hookKind' script hook '$hookExpr'"
174 elif [[ "$hookExpr" != "_callImplicitHook"* ]]; then
175 # Here we have a string hook to eval.
176 # Join lines onto one with literal \n characters unless NIX_DEBUG >= 5.
177 local exprToOutput
178 if [[ ${NIX_DEBUG:-0} -ge 5 ]]; then
179 exprToOutput="$hookExpr"
180 else
181 # We have `r'\n'.join([line.lstrip() for lines in text.split('\n')])` at home.
182 local hookExprLine
183 while IFS= read -r hookExprLine; do
184 # These lines often have indentation,
185 # so let's remove leading whitespace.
186 hookExprLine="${hookExprLine#"${hookExprLine%%[![:space:]]*}"}"
187 # If this line wasn't entirely whitespace,
188 # then add it to our output
189 if [[ -n "$hookExprLine" ]]; then
190 exprToOutput+="$hookExprLine\\n "
191 fi
192 done <<< "$hookExpr"
193
194 # And then remove the final, unnecessary, \n
195 exprToOutput="${exprToOutput%%\\n }"
196 fi
197 nixTalkativeLog "evaling '$hookKind' string hook '$exprToOutput'"
198 fi
199}
200
201######################################################################
202# Hook handling.
203
204# Run all hooks with the specified name in the order in which they
205# were added, stopping if any fails (returns a non-zero exit
206# code). The hooks for <hookName> are the shell function or variable
207# <hookName>, and the values of the shell array ‘<hookName>Hooks’.
208runHook() {
209 local hookName="$1"
210 shift
211 local hooksSlice="${hookName%Hook}Hooks[@]"
212
213 local hook
214 # Hack around old bash being bad and thinking empty arrays are
215 # undefined.
216 for hook in "_callImplicitHook 0 $hookName" ${!hooksSlice+"${!hooksSlice}"}; do
217 _logHook "$hookName" "$hook" "$@"
218 _eval "$hook" "$@"
219 done
220
221 return 0
222}
223
224
225# Run all hooks with the specified name, until one succeeds (returns a
226# zero exit code). If none succeed, return a non-zero exit code.
227runOneHook() {
228 local hookName="$1"
229 shift
230 local hooksSlice="${hookName%Hook}Hooks[@]"
231
232 local hook ret=1
233 # Hack around old bash like above
234 for hook in "_callImplicitHook 1 $hookName" ${!hooksSlice+"${!hooksSlice}"}; do
235 _logHook "$hookName" "$hook" "$@"
236 if _eval "$hook" "$@"; then
237 ret=0
238 break
239 fi
240 done
241
242 return "$ret"
243}
244
245
246# Run the named hook, either by calling the function with that name or
247# by evaluating the variable with that name. This allows convenient
248# setting of hooks both from Nix expressions (as attributes /
249# environment variables) and from shell scripts (as functions). If you
250# want to allow multiple hooks, use runHook instead.
251_callImplicitHook() {
252 local def="$1"
253 local hookName="$2"
254 if declare -F "$hookName" > /dev/null; then
255 nixTalkativeLog "calling implicit '$hookName' function hook"
256 "$hookName"
257 elif type -p "$hookName" > /dev/null; then
258 nixTalkativeLog "sourcing implicit '$hookName' script hook"
259 source "$hookName"
260 elif [ -n "${!hookName:-}" ]; then
261 nixTalkativeLog "evaling implicit '$hookName' string hook"
262 eval "${!hookName}"
263 else
264 return "$def"
265 fi
266 # `_eval` expects hook to need nounset disable and leave it
267 # disabled anyways, so Ok to to delegate. The alternative of a
268 # return trap is no good because it would affect nested returns.
269}
270
271
272# A function wrapper around ‘eval’ that ensures that ‘return’ inside
273# hooks exits the hook, not the caller. Also will only pass args if
274# command can take them
275_eval() {
276 if declare -F "$1" > /dev/null 2>&1; then
277 "$@" # including args
278 else
279 eval "$1"
280 fi
281}
282
283
284######################################################################
285# Logging.
286
287# Prints a command such that all word splits are unambiguous. We need
288# to split the command in three parts because the middle format string
289# will be, and must be, repeated for each argument. The first argument
290# goes before the ':' and is just for convenience.
291echoCmd() {
292 printf "%s:" "$1"
293 shift
294 printf ' %q' "$@"
295 echo
296}
297
298
299######################################################################
300# Error handling.
301
302exitHandler() {
303 exitCode="$?"
304 set +e
305
306 if [ -n "${showBuildStats:-}" ]; then
307 read -r -d '' -a buildTimes < <(times)
308 echo "build times:"
309 echo "user time for the shell ${buildTimes[0]}"
310 echo "system time for the shell ${buildTimes[1]}"
311 echo "user time for all child processes ${buildTimes[2]}"
312 echo "system time for all child processes ${buildTimes[3]}"
313 fi
314
315 if (( "$exitCode" != 0 )); then
316 runHook failureHook
317
318 # If the builder had a non-zero exit code and
319 # $succeedOnFailure is set, create the file
320 # ‘$out/nix-support/failed’ to signal failure, and exit
321 # normally. Otherwise, return the original exit code.
322 if [ -n "${succeedOnFailure:-}" ]; then
323 echo "build failed with exit code $exitCode (ignored)"
324 mkdir -p "$out/nix-support"
325 printf "%s" "$exitCode" > "$out/nix-support/failed"
326 exit 0
327 fi
328
329 else
330 runHook exitHook
331 fi
332
333 return "$exitCode"
334}
335
336trap "exitHandler" EXIT
337
338
339######################################################################
340# Helper functions.
341
342
343addToSearchPathWithCustomDelimiter() {
344 local delimiter="$1"
345 local varName="$2"
346 local dir="$3"
347 if [[ -d "$dir" && "${!varName:+${delimiter}${!varName}${delimiter}}" \
348 != *"${delimiter}${dir}${delimiter}"* ]]; then
349 export "${varName}=${!varName:+${!varName}${delimiter}}${dir}"
350 fi
351}
352
353addToSearchPath() {
354 addToSearchPathWithCustomDelimiter ":" "$@"
355}
356
357# Prepend elements to variable "$1", which may come from an attr.
358#
359# This is useful in generic setup code, which must (for now) support
360# both derivations with and without __structuredAttrs true, so the
361# variable may be an array or a space-separated string.
362#
363# Expressions for individual packages should simply switch to array
364# syntax when they switch to setting __structuredAttrs = true.
365prependToVar() {
366 local -n nameref="$1"
367 local useArray type
368
369 if [ -n "$__structuredAttrs" ]; then
370 useArray=true
371 else
372 useArray=false
373 fi
374
375 # check if variable already exist and if it does then do extra checks
376 if type=$(declare -p "$1" 2> /dev/null); then
377 case "${type#* }" in
378 -A*)
379 echo "prependToVar(): ERROR: trying to use prependToVar on an associative array." >&2
380 return 1 ;;
381 -a*)
382 useArray=true ;;
383 *)
384 useArray=false ;;
385 esac
386 fi
387
388 shift
389
390 if $useArray; then
391 nameref=( "$@" ${nameref+"${nameref[@]}"} )
392 else
393 nameref="$* ${nameref-}"
394 fi
395}
396
397# Same as above
398appendToVar() {
399 local -n nameref="$1"
400 local useArray type
401
402 if [ -n "$__structuredAttrs" ]; then
403 useArray=true
404 else
405 useArray=false
406 fi
407
408 # check if variable already exist and if it does then do extra checks
409 if type=$(declare -p "$1" 2> /dev/null); then
410 case "${type#* }" in
411 -A*)
412 echo "appendToVar(): ERROR: trying to use appendToVar on an associative array, use variable+=([\"X\"]=\"Y\") instead." >&2
413 return 1 ;;
414 -a*)
415 useArray=true ;;
416 *)
417 useArray=false ;;
418 esac
419 fi
420
421 shift
422
423 if $useArray; then
424 nameref=( ${nameref+"${nameref[@]}"} "$@" )
425 else
426 nameref="${nameref-} $*"
427 fi
428}
429
430# Accumulate flags from the named variables $2+ into the indexed array $1.
431#
432# Arrays are simply concatenated, strings are split on whitespace.
433# Default values can be passed via name=default.
434concatTo() {
435 local -
436 set -o noglob
437 local -n targetref="$1"; shift
438 local arg default name type
439 for arg in "$@"; do
440 IFS="=" read -r name default <<< "$arg"
441 local -n nameref="$name"
442 if [[ -z "${nameref[*]}" && -n "$default" ]]; then
443 targetref+=( "$default" )
444 elif type=$(declare -p "$name" 2> /dev/null); then
445 case "${type#* }" in
446 -A*)
447 echo "concatTo(): ERROR: trying to use concatTo on an associative array." >&2
448 return 1 ;;
449 -a*)
450 targetref+=( "${nameref[@]}" ) ;;
451 *)
452 if [[ "$name" = *"Array" ]]; then
453 nixErrorLog "concatTo(): $name is not declared as array, treating as a singleton. This will become an error in future"
454 # Reproduces https://github.com/NixOS/nixpkgs/pull/318614/files#diff-7c7ca80928136cfc73a02d5b28350bd900e331d6d304857053ffc9f7beaad576L359
455 targetref+=( ${nameref+"${nameref[@]}"} )
456 else
457 # shellcheck disable=SC2206
458 targetref+=( ${nameref-} )
459 fi
460 ;;
461 esac
462 fi
463 done
464}
465
466# Concatenate a list of strings ($2) with a separator ($1) between each element.
467# The list can be an indexed array of strings or a single string. A single string
468# is split on spaces and then concatenated with the separator.
469#
470# $ flags="lorem ipsum dolor sit amet"
471# $ concatStringsSep ";" flags
472# lorem;ipsum;dolor;sit;amet
473#
474# $ flags=("lorem ipsum" "dolor" "sit amet")
475# $ concatStringsSep ";" flags
476# lorem ipsum;dolor;sit amet
477#
478# Also supports multi-character separators;
479# $ flags=("lorem ipsum" "dolor" "sit amet")
480# $ concatStringsSep " and " flags
481# lorem ipsum and dolor and sit amet
482concatStringsSep() {
483 local sep="$1"
484 local name="$2"
485 local type oldifs
486 if type=$(declare -p "$name" 2> /dev/null); then
487 local -n nameref="$name"
488 case "${type#* }" in
489 -A*)
490 echo "concatStringsSep(): ERROR: trying to use concatStringsSep on an associative array." >&2
491 return 1 ;;
492 -a*)
493 # \036 is the "record separator" character. We assume that this will never need to be part of
494 # an argument string we create here. If anyone ever hits this limitation: Feel free to refactor.
495 # To avoid leaking an unescaped rs character when dumping the environment with nix, we use printf
496 # in a subshell.
497 local IFS="$(printf '\036')" ;;
498 *)
499 local IFS=" " ;;
500
501 esac
502 local ifs_separated="${nameref[*]}"
503 echo -n "${ifs_separated//"$IFS"/"$sep"}"
504 fi
505}
506
507# Add $1/lib* into rpaths.
508# The function is used in multiple-outputs.sh hook,
509# so it is defined here but tried after the hook.
510_addRpathPrefix() {
511 if [ "${NIX_NO_SELF_RPATH:-0}" != 1 ]; then
512 export NIX_LDFLAGS="-rpath $1/lib ${NIX_LDFLAGS-}"
513 fi
514}
515
516# Return success if the specified file is an ELF object.
517isELF() {
518 local fn="$1"
519 local fd
520 local magic
521 exec {fd}< "$fn"
522 LANG=C read -r -n 4 -u "$fd" magic
523 exec {fd}<&-
524 if [ "$magic" = $'\177ELF' ]; then return 0; else return 1; fi
525}
526
527# Return success if the specified file is a Mach-O object.
528isMachO() {
529 local fn="$1"
530 local fd
531 local magic
532 exec {fd}< "$fn"
533 LANG=C read -r -n 4 -u "$fd" magic
534 exec {fd}<&-
535
536 # nix uses 'declare -F' in get-env.sh to retrieve the loaded functions.
537 # If we use the $'string' syntax instead of 'echo -ne' then 'declare' will print the raw characters and break nix.
538 # See https://github.com/NixOS/nixpkgs/pull/138334 and https://github.com/NixOS/nix/issues/5262.
539
540 # https://opensource.apple.com/source/lldb/lldb-310.2.36/examples/python/mach_o.py.auto.html
541 if [[ "$magic" = $(echo -ne "\xfe\xed\xfa\xcf") || "$magic" = $(echo -ne "\xcf\xfa\xed\xfe") ]]; then
542 # MH_MAGIC_64 || MH_CIGAM_64
543 return 0;
544 elif [[ "$magic" = $(echo -ne "\xfe\xed\xfa\xce") || "$magic" = $(echo -ne "\xce\xfa\xed\xfe") ]]; then
545 # MH_MAGIC || MH_CIGAM
546 return 0;
547 elif [[ "$magic" = $(echo -ne "\xca\xfe\xba\xbe") || "$magic" = $(echo -ne "\xbe\xba\xfe\xca") ]]; then
548 # FAT_MAGIC || FAT_CIGAM
549 return 0;
550 else
551 return 1;
552 fi
553}
554
555# Return success if the specified file is a script (i.e. starts with
556# "#!").
557isScript() {
558 local fn="$1"
559 local fd
560 local magic
561 exec {fd}< "$fn"
562 LANG=C read -r -n 2 -u "$fd" magic
563 exec {fd}<&-
564 if [[ "$magic" =~ \#! ]]; then return 0; else return 1; fi
565}
566
567# printf unfortunately will print a trailing newline regardless
568printLines() {
569 (( "$#" > 0 )) || return 0
570 printf '%s\n' "$@"
571}
572
573printWords() {
574 (( "$#" > 0 )) || return 0
575 printf '%s ' "$@"
576}
577
578######################################################################
579# Initialisation.
580
581# If using structured attributes, export variables from `env` to the environment.
582# When not using structured attributes, those variables are already exported.
583if [[ -n $__structuredAttrs ]]; then
584 for envVar in "${!env[@]}"; do
585 declare -x "${envVar}=${env[${envVar}]}"
586 done
587fi
588
589
590# Set a fallback default value for SOURCE_DATE_EPOCH, used by some build tools
591# to provide a deterministic substitute for the "current" time. Note that
592# 315532800 = 1980-01-01 12:00:00. We use this date because python's wheel
593# implementation uses zip archive and zip does not support dates going back to
594# 1970.
595export SOURCE_DATE_EPOCH
596: "${SOURCE_DATE_EPOCH:=315532800}"
597
598
599# Wildcard expansions that don't match should expand to an empty list.
600# This ensures that, for instance, "for i in *; do ...; done" does the
601# right thing.
602shopt -s nullglob
603
604
605# Set up the initial path.
606PATH=
607HOST_PATH=
608for i in $initialPath; do
609 if [ "$i" = / ]; then i=; fi
610 addToSearchPath PATH "$i/bin"
611
612 # For backward compatibility, we add initial path to HOST_PATH so
613 # it can be used in auto patch-shebangs. Unfortunately this will
614 # not work with cross compilation.
615 if [ -z "${strictDeps-}" ]; then
616 addToSearchPath HOST_PATH "$i/bin"
617 fi
618done
619
620unset i
621
622nixWarnLog "initial path: $PATH"
623
624# Check that the pre-hook initialised SHELL.
625if [ -z "${SHELL:-}" ]; then echo "SHELL not set"; exit 1; fi
626BASH="$SHELL"
627export CONFIG_SHELL="$SHELL"
628
629
630# Execute the pre-hook.
631if [ -z "${shell:-}" ]; then export shell="$SHELL"; fi
632runHook preHook
633
634
635# Allow the caller to augment buildInputs (it's not always possible to
636# do this before the call to setup.sh, since the PATH is empty at that
637# point; here we have a basic Unix environment).
638runHook addInputsHook
639
640
641# Package accumulators
642
643declare -a pkgsBuildBuild pkgsBuildHost pkgsBuildTarget
644declare -a pkgsHostHost pkgsHostTarget
645declare -a pkgsTargetTarget
646
647declare -a pkgBuildAccumVars=(pkgsBuildBuild pkgsBuildHost pkgsBuildTarget)
648declare -a pkgHostAccumVars=(pkgsHostHost pkgsHostTarget)
649declare -a pkgTargetAccumVars=(pkgsTargetTarget)
650
651declare -a pkgAccumVarVars=(pkgBuildAccumVars pkgHostAccumVars pkgTargetAccumVars)
652
653
654# Hooks
655
656declare -a envBuildBuildHooks envBuildHostHooks envBuildTargetHooks
657declare -a envHostHostHooks envHostTargetHooks
658declare -a envTargetTargetHooks
659
660declare -a pkgBuildHookVars=(envBuildBuildHook envBuildHostHook envBuildTargetHook)
661declare -a pkgHostHookVars=(envHostHostHook envHostTargetHook)
662declare -a pkgTargetHookVars=(envTargetTargetHook)
663
664declare -a pkgHookVarVars=(pkgBuildHookVars pkgHostHookVars pkgTargetHookVars)
665
666# those variables are declared here, since where and if they are used varies
667declare -a preFixupHooks fixupOutputHooks preConfigureHooks postFixupHooks postUnpackHooks unpackCmdHooks
668
669# Add env hooks for all sorts of deps with the specified host offset.
670addEnvHooks() {
671 local depHostOffset="$1"
672 shift
673 local pkgHookVarsSlice="${pkgHookVarVars[$depHostOffset + 1]}[@]"
674 local pkgHookVar
675 for pkgHookVar in "${!pkgHookVarsSlice}"; do
676 eval "${pkgHookVar}s"'+=("$@")'
677 done
678}
679
680
681# Propagated dep files
682
683declare -a propagatedBuildDepFiles=(
684 propagated-build-build-deps
685 propagated-native-build-inputs # Legacy name for back-compat
686 propagated-build-target-deps
687)
688declare -a propagatedHostDepFiles=(
689 propagated-host-host-deps
690 propagated-build-inputs # Legacy name for back-compat
691)
692declare -a propagatedTargetDepFiles=(
693 propagated-target-target-deps
694)
695declare -a propagatedDepFilesVars=(
696 propagatedBuildDepFiles
697 propagatedHostDepFiles
698 propagatedTargetDepFiles
699)
700
701# Platform offsets: build = -1, host = 0, target = 1
702declare -a allPlatOffsets=(-1 0 1)
703
704
705# Mutually-recursively find all build inputs. See the dependency section of the
706# stdenv chapter of the Nixpkgs manual for the specification this algorithm
707# implements.
708findInputs() {
709 local -r pkg="$1"
710 local -r hostOffset="$2"
711 local -r targetOffset="$3"
712
713 # Sanity check
714 (( hostOffset <= targetOffset )) || exit 1
715
716 # shellcheck disable=SC1087
717 local varVar="${pkgAccumVarVars[hostOffset + 1]}"
718 # shellcheck disable=SC1087
719 local varRef="$varVar[$((targetOffset - hostOffset))]"
720 local var="${!varRef}"
721 unset -v varVar varRef
722
723 # TODO(@Ericson2314): Restore using associative array once Darwin
724 # nix-shell doesn't use impure bash. This should replace the O(n)
725 # case with an O(1) hash map lookup, assuming bash is implemented
726 # well :D.
727 # shellcheck disable=SC1087
728 local varSlice="$var[*]"
729 # ${..-} to hack around old bash empty array problem
730 case " ${!varSlice-} " in
731 *" $pkg "*) return 0 ;;
732 esac
733 unset -v varSlice
734
735 eval "$var"'+=("$pkg")'
736
737 if ! [ -e "$pkg" ]; then
738 echo "build input $pkg does not exist" >&2
739 exit 1
740 fi
741
742 # The current package's host and target offset together
743 # provide a <=-preserving homomorphism from the relative
744 # offsets to current offset
745 function mapOffset() {
746 local -r inputOffset="$1"
747 local -n outputOffset="$2"
748 if (( inputOffset <= 0 )); then
749 outputOffset=$((inputOffset + hostOffset))
750 else
751 outputOffset=$((inputOffset - 1 + targetOffset))
752 fi
753 }
754
755 # Host offset relative to that of the package whose immediate
756 # dependencies we are currently exploring.
757 local relHostOffset
758 for relHostOffset in "${allPlatOffsets[@]}"; do
759 # `+ 1` so we start at 0 for valid index
760 local files="${propagatedDepFilesVars[relHostOffset + 1]}"
761
762 # Host offset relative to the package currently being
763 # built---as absolute an offset as will be used.
764 local hostOffsetNext
765 mapOffset "$relHostOffset" hostOffsetNext
766
767 # Ensure we're in bounds relative to the package currently
768 # being built.
769 (( -1 <= hostOffsetNext && hostOffsetNext <= 1 )) || continue
770
771 # Target offset relative to the *host* offset of the package
772 # whose immediate dependencies we are currently exploring.
773 local relTargetOffset
774 for relTargetOffset in "${allPlatOffsets[@]}"; do
775 (( "$relHostOffset" <= "$relTargetOffset" )) || continue
776
777 local fileRef="${files}[$relTargetOffset - $relHostOffset]"
778 local file="${!fileRef}"
779 unset -v fileRef
780
781 # Target offset relative to the package currently being
782 # built.
783 local targetOffsetNext
784 mapOffset "$relTargetOffset" targetOffsetNext
785
786 # Once again, ensure we're in bounds relative to the
787 # package currently being built.
788 (( -1 <= hostOffsetNext && hostOffsetNext <= 1 )) || continue
789
790 [[ -f "$pkg/nix-support/$file" ]] || continue
791
792 local pkgNext
793 read -r -d '' pkgNext < "$pkg/nix-support/$file" || true
794 for pkgNext in $pkgNext; do
795 findInputs "$pkgNext" "$hostOffsetNext" "$targetOffsetNext"
796 done
797 done
798 done
799}
800
801# The way we handle deps* and *Inputs works with structured attrs
802# either enabled or disabled. For this it's convenient that the items
803# in each list must be store paths, and therefore space-free.
804
805# Make sure all are at least defined as empty
806: "${depsBuildBuild=}" "${depsBuildBuildPropagated=}"
807: "${nativeBuildInputs=}" "${propagatedNativeBuildInputs=}" "${defaultNativeBuildInputs=}"
808: "${depsBuildTarget=}" "${depsBuildTargetPropagated=}"
809: "${depsHostHost=}" "${depsHostHostPropagated=}"
810: "${buildInputs=}" "${propagatedBuildInputs=}" "${defaultBuildInputs=}"
811: "${depsTargetTarget=}" "${depsTargetTargetPropagated=}"
812
813for pkg in ${depsBuildBuild[@]} ${depsBuildBuildPropagated[@]}; do
814 findInputs "$pkg" -1 -1
815done
816for pkg in ${nativeBuildInputs[@]} ${propagatedNativeBuildInputs[@]}; do
817 findInputs "$pkg" -1 0
818done
819for pkg in ${depsBuildTarget[@]} ${depsBuildTargetPropagated[@]}; do
820 findInputs "$pkg" -1 1
821done
822for pkg in ${depsHostHost[@]} ${depsHostHostPropagated[@]}; do
823 findInputs "$pkg" 0 0
824done
825for pkg in ${buildInputs[@]} ${propagatedBuildInputs[@]} ; do
826 findInputs "$pkg" 0 1
827done
828for pkg in ${depsTargetTarget[@]} ${depsTargetTargetPropagated[@]}; do
829 findInputs "$pkg" 1 1
830done
831# Default inputs must be processed last
832for pkg in ${defaultNativeBuildInputs[@]}; do
833 findInputs "$pkg" -1 0
834done
835for pkg in ${defaultBuildInputs[@]}; do
836 findInputs "$pkg" 0 1
837done
838
839# Add package to the future PATH and run setup hooks
840activatePackage() {
841 local pkg="$1"
842 local -r hostOffset="$2"
843 local -r targetOffset="$3"
844
845 # Sanity check
846 (( hostOffset <= targetOffset )) || exit 1
847
848 if [ -f "$pkg" ]; then
849 nixTalkativeLog "sourcing setup hook '$pkg'"
850 source "$pkg"
851 fi
852
853 # Only dependencies whose host platform is guaranteed to match the
854 # build platform are included here. That would be `depsBuild*`,
855 # and legacy `nativeBuildInputs`, in general. If we aren't cross
856 # compiling, however, everything can be put on the PATH. To ease
857 # the transition, we do include everything in that case.
858 #
859 # TODO(@Ericson2314): Don't special-case native compilation
860 if [[ -z "${strictDeps-}" || "$hostOffset" -le -1 ]]; then
861 addToSearchPath _PATH "$pkg/bin"
862 fi
863
864 if (( hostOffset <= -1 )); then
865 addToSearchPath _XDG_DATA_DIRS "$pkg/share"
866 fi
867
868 if [[ "$hostOffset" -eq 0 && -d "$pkg/bin" ]]; then
869 addToSearchPath _HOST_PATH "$pkg/bin"
870 fi
871
872 if [[ -f "$pkg/nix-support/setup-hook" ]]; then
873 nixTalkativeLog "sourcing setup hook '$pkg/nix-support/setup-hook'"
874 source "$pkg/nix-support/setup-hook"
875 fi
876}
877
878_activatePkgs() {
879 local hostOffset targetOffset
880 local pkg
881
882 for hostOffset in "${allPlatOffsets[@]}"; do
883 local pkgsVar="${pkgAccumVarVars[hostOffset + 1]}"
884 for targetOffset in "${allPlatOffsets[@]}"; do
885 (( hostOffset <= targetOffset )) || continue
886 local pkgsRef="${pkgsVar}[$targetOffset - $hostOffset]"
887 local pkgsSlice="${!pkgsRef}[@]"
888 for pkg in ${!pkgsSlice+"${!pkgsSlice}"}; do
889 activatePackage "$pkg" "$hostOffset" "$targetOffset"
890 done
891 done
892 done
893}
894
895# Run the package setup hooks and build _PATH
896_activatePkgs
897
898# Set the relevant environment variables to point to the build inputs
899# found above.
900#
901# These `depOffset`s, beyond indexing the arrays, also tell the env
902# hook what sort of dependency (ignoring propagatedness) is being
903# passed to the env hook. In a real language, we'd append a closure
904# with this information to the relevant env hook array, but bash
905# doesn't have closures, so it's easier to just pass this in.
906_addToEnv() {
907 local depHostOffset depTargetOffset
908 local pkg
909
910 for depHostOffset in "${allPlatOffsets[@]}"; do
911 local hookVar="${pkgHookVarVars[depHostOffset + 1]}"
912 local pkgsVar="${pkgAccumVarVars[depHostOffset + 1]}"
913 for depTargetOffset in "${allPlatOffsets[@]}"; do
914 (( depHostOffset <= depTargetOffset )) || continue
915 local hookRef="${hookVar}[$depTargetOffset - $depHostOffset]"
916 if [[ -z "${strictDeps-}" ]]; then
917
918 # Keep track of which packages we have visited before.
919 local visitedPkgs=""
920
921 # Apply environment hooks to all packages during native
922 # compilation to ease the transition.
923 #
924 # TODO(@Ericson2314): Don't special-case native compilation
925 for pkg in \
926 "${pkgsBuildBuild[@]}" \
927 "${pkgsBuildHost[@]}" \
928 "${pkgsBuildTarget[@]}" \
929 "${pkgsHostHost[@]}" \
930 "${pkgsHostTarget[@]}" \
931 "${pkgsTargetTarget[@]}"
932 do
933 if [[ "$visitedPkgs" = *"$pkg"* ]]; then
934 continue
935 fi
936 runHook "${!hookRef}" "$pkg"
937 visitedPkgs+=" $pkg"
938 done
939 else
940 local pkgsRef="${pkgsVar}[$depTargetOffset - $depHostOffset]"
941 local pkgsSlice="${!pkgsRef}[@]"
942 for pkg in ${!pkgsSlice+"${!pkgsSlice}"}; do
943 runHook "${!hookRef}" "$pkg"
944 done
945 fi
946 done
947 done
948}
949
950# Run the package-specific hooks set by the setup-hook scripts.
951_addToEnv
952
953
954# Unset setup-specific declared variables
955unset allPlatOffsets
956unset pkgBuildAccumVars pkgHostAccumVars pkgTargetAccumVars pkgAccumVarVars
957unset pkgBuildHookVars pkgHostHookVars pkgTargetHookVars pkgHookVarVars
958unset propagatedDepFilesVars
959
960
961_addRpathPrefix "$out"
962
963
964# Set the TZ (timezone) environment variable, otherwise commands like
965# `date' will complain (e.g., `Tue Mar 9 10:01:47 Local time zone must
966# be set--see zic manual page 2004').
967export TZ=UTC
968
969
970# Set the prefix. This is generally $out, but it can be overriden,
971# for instance if we just want to perform a test build/install to a
972# temporary location and write a build report to $out.
973if [ -z "${prefix:-}" ]; then
974 prefix="$out";
975fi
976
977if [ "${useTempPrefix:-}" = 1 ]; then
978 prefix="$NIX_BUILD_TOP/tmp_prefix";
979fi
980
981
982PATH="${_PATH-}${_PATH:+${PATH:+:}}$PATH"
983HOST_PATH="${_HOST_PATH-}${_HOST_PATH:+${HOST_PATH:+:}}$HOST_PATH"
984export XDG_DATA_DIRS="${_XDG_DATA_DIRS-}${_XDG_DATA_DIRS:+${XDG_DATA_DIRS:+:}}${XDG_DATA_DIRS-}"
985
986nixWarnLog "final path: $PATH"
987nixWarnLog "final host path: $HOST_PATH"
988nixWarnLog "final data dirs: $XDG_DATA_DIRS"
989
990unset _PATH
991unset _HOST_PATH
992unset _XDG_DATA_DIRS
993
994
995# Normalize the NIX_BUILD_CORES variable. The value might be 0, which
996# means that we're supposed to try and auto-detect the number of
997# available CPU cores at run-time.
998
999NIX_BUILD_CORES="${NIX_BUILD_CORES:-1}"
1000if ((NIX_BUILD_CORES <= 0)); then
1001 guess=$(nproc 2>/dev/null || true)
1002 ((NIX_BUILD_CORES = guess <= 0 ? 1 : guess))
1003fi
1004export NIX_BUILD_CORES
1005
1006
1007# Prevent SSL libraries from using certificates in /etc/ssl, unless set explicitly.
1008# Leave it in impure shells for convenience.
1009if [[ -z "${NIX_SSL_CERT_FILE:-}" && "${IN_NIX_SHELL:-}" != "impure" ]]; then
1010 export NIX_SSL_CERT_FILE=/no-cert-file.crt
1011fi
1012# Another variant left for compatibility.
1013if [[ -z "${SSL_CERT_FILE:-}" && "${IN_NIX_SHELL:-}" != "impure" ]]; then
1014 export SSL_CERT_FILE=/no-cert-file.crt
1015fi
1016
1017
1018######################################################################
1019# Textual substitution functions.
1020
1021# only log once, due to max logging limit on hydra
1022_substituteStream_has_warned_replace_deprecation=false
1023
1024substituteStream() {
1025 local var=$1
1026 local description=$2
1027 shift 2
1028
1029 while (( "$#" )); do
1030 local replace_mode="$1"
1031 case "$1" in
1032 --replace)
1033 # deprecated 2023-11-22
1034 # this will either get removed, or switch to the behaviour of --replace-fail in the future
1035 if ! "$_substituteStream_has_warned_replace_deprecation"; then
1036 echo "substituteStream() in derivation $name: WARNING: '--replace' is deprecated, use --replace-{fail,warn,quiet}. ($description)" >&2
1037 _substituteStream_has_warned_replace_deprecation=true
1038 fi
1039 replace_mode='--replace-warn'
1040 ;&
1041 --replace-quiet|--replace-warn|--replace-fail)
1042 pattern="$2"
1043 replacement="$3"
1044 shift 3
1045 if ! [[ "${!var}" == *"$pattern"* ]]; then
1046 if [ "$replace_mode" == --replace-warn ]; then
1047 printf "substituteStream() in derivation $name: WARNING: pattern %q doesn't match anything in %s\n" "$pattern" "$description" >&2
1048 elif [ "$replace_mode" == --replace-fail ]; then
1049 printf "substituteStream() in derivation $name: ERROR: pattern %q doesn't match anything in %s\n" "$pattern" "$description" >&2
1050 return 1
1051 fi
1052 fi
1053 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}'
1054 ;;
1055
1056 --subst-var)
1057 local varName="$2"
1058 shift 2
1059 # check if the used nix attribute name is a valid bash name
1060 if ! [[ "$varName" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
1061 echo "substituteStream() in derivation $name: ERROR: substitution variables must be valid Bash names, \"$varName\" isn't." >&2
1062 return 1
1063 fi
1064 if [ -z ${!varName+x} ]; then
1065 echo "substituteStream() in derivation $name: ERROR: variable \$$varName is unset" >&2
1066 return 1
1067 fi
1068 pattern="@$varName@"
1069 replacement="${!varName}"
1070 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}'
1071 ;;
1072
1073 --subst-var-by)
1074 pattern="@$2@"
1075 replacement="$3"
1076 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}'
1077 shift 3
1078 ;;
1079
1080 *)
1081 echo "substituteStream() in derivation $name: ERROR: Invalid command line argument: $1" >&2
1082 return 1
1083 ;;
1084 esac
1085 done
1086
1087 printf "%s" "${!var}"
1088}
1089
1090# put the content of a file in a variable
1091# fail loudly if provided with a binary (containing null bytes)
1092consumeEntire() {
1093 # read returns non-0 on EOF, so we want read to fail
1094 if IFS='' read -r -d '' "$1" ; then
1095 echo "consumeEntire(): ERROR: Input null bytes, won't process" >&2
1096 return 1
1097 fi
1098}
1099
1100substitute() {
1101 local input="$1"
1102 local output="$2"
1103 shift 2
1104
1105 if [ ! -f "$input" ]; then
1106 echo "substitute(): ERROR: file '$input' does not exist" >&2
1107 return 1
1108 fi
1109
1110 local content
1111 consumeEntire content < "$input"
1112
1113 if [ -e "$output" ]; then chmod +w "$output"; fi
1114 substituteStream content "file '$input'" "$@" > "$output"
1115}
1116
1117substituteInPlace() {
1118 local -a fileNames=()
1119 for arg in "$@"; do
1120 if [[ "$arg" = "--"* ]]; then
1121 break
1122 fi
1123 fileNames+=("$arg")
1124 shift
1125 done
1126 if ! [[ "${#fileNames[@]}" -gt 0 ]]; then
1127 echo >&2 "substituteInPlace called without any files to operate on (files must come before options!)"
1128 return 1
1129 fi
1130
1131 for file in "${fileNames[@]}"; do
1132 substitute "$file" "$file" "$@"
1133 done
1134}
1135
1136_allFlags() {
1137 # Export some local variables for the `awk` below so some substitutions (such as name)
1138 # don't have to be in the env attrset when `__structuredAttrs` is enabled.
1139 export system pname name version
1140 while IFS='' read -r varName; do
1141 nixTalkativeLog "@${varName}@ -> ${!varName}"
1142 args+=("--subst-var" "$varName")
1143 done < <(awk 'BEGIN { for (v in ENVIRON) if (v ~ /^[a-z][a-zA-Z0-9_]*$/) print v }')
1144}
1145
1146substituteAllStream() {
1147 local -a args=()
1148 _allFlags
1149
1150 substituteStream "$1" "$2" "${args[@]}"
1151}
1152
1153# Substitute all environment variables that start with a lowercase character and
1154# are valid Bash names.
1155substituteAll() {
1156 local input="$1"
1157 local output="$2"
1158
1159 local -a args=()
1160 _allFlags
1161
1162 substitute "$input" "$output" "${args[@]}"
1163}
1164
1165
1166substituteAllInPlace() {
1167 local fileName="$1"
1168 shift
1169 substituteAll "$fileName" "$fileName" "$@"
1170}
1171
1172
1173######################################################################
1174# What follows is the generic builder.
1175
1176
1177# This function is useful for debugging broken Nix builds. It dumps all environment variables to a
1178# file `env-vars' in the Nix build directory. If the build fails and the `-K' option is used,
1179# you can then go to the build directory and source the `env-vars' file to reproduce the environment
1180# used for building. Set `noDumpEnvVars` in the derivation to avoid this, and if for whatever reason
1181# `$NIX_BUILD_TOP` is not a directory, this function also does nothing.
1182dumpVars() {
1183 if [[ "${noDumpEnvVars:-0}" != 1 && -d "$NIX_BUILD_TOP" ]]; then
1184 # Set umask to create env-vars file with 0600 permissions (owner read/write only)
1185 local old_umask
1186 old_umask=$(umask)
1187 umask 0077
1188
1189 # Dump all environment variables to the env-vars file
1190 export 2>/dev/null > "$NIX_BUILD_TOP/env-vars"
1191
1192 # Restore original umask
1193 umask "$old_umask"
1194 fi
1195}
1196
1197
1198# Utility function: echo the base name of the given path, with the
1199# prefix `HASH-' removed, if present.
1200stripHash() {
1201 local strippedName casematchOpt=0
1202 # On separate line for `set -e`
1203 strippedName="$(basename -- "$1")"
1204 shopt -q nocasematch && casematchOpt=1
1205 shopt -u nocasematch
1206 if [[ "$strippedName" =~ ^[a-z0-9]{32}- ]]; then
1207 echo "${strippedName:33}"
1208 else
1209 echo "$strippedName"
1210 fi
1211 if (( casematchOpt )); then shopt -s nocasematch; fi
1212}
1213
1214
1215recordPropagatedDependencies() {
1216 # Propagate dependencies into the development output.
1217 declare -ra flatVars=(
1218 # Build
1219 depsBuildBuildPropagated
1220 propagatedNativeBuildInputs
1221 depsBuildTargetPropagated
1222 # Host
1223 depsHostHostPropagated
1224 propagatedBuildInputs
1225 # Target
1226 depsTargetTargetPropagated
1227 )
1228 declare -ra flatFiles=(
1229 "${propagatedBuildDepFiles[@]}"
1230 "${propagatedHostDepFiles[@]}"
1231 "${propagatedTargetDepFiles[@]}"
1232 )
1233
1234 local propagatedInputsIndex
1235 for propagatedInputsIndex in "${!flatVars[@]}"; do
1236 local propagatedInputsSlice="${flatVars[$propagatedInputsIndex]}[@]"
1237 local propagatedInputsFile="${flatFiles[$propagatedInputsIndex]}"
1238
1239 [[ "${!propagatedInputsSlice}" ]] || continue
1240
1241 mkdir -p "${!outputDev}/nix-support"
1242 # shellcheck disable=SC2086
1243 printWords ${!propagatedInputsSlice} > "${!outputDev}/nix-support/$propagatedInputsFile"
1244 done
1245}
1246
1247
1248unpackCmdHooks+=(_defaultUnpack)
1249_defaultUnpack() {
1250 local fn="$1"
1251 local destination
1252
1253 if [ -d "$fn" ]; then
1254
1255 destination="$(stripHash "$fn")"
1256
1257 if [ -e "$destination" ]; then
1258 echo "Cannot copy $fn to $destination: destination already exists!"
1259 echo "Did you specify two \"srcs\" with the same \"name\"?"
1260 return 1
1261 fi
1262
1263 # We can't preserve hardlinks because they may have been
1264 # introduced by store optimization, which might break things
1265 # in the build.
1266 cp -r --preserve=timestamps --reflink=auto -- "$fn" "$destination"
1267
1268 else
1269
1270 case "$fn" in
1271 *.tar.xz | *.tar.lzma | *.txz)
1272 # Don't rely on tar knowing about .xz.
1273 # Additionally, we have multiple different xz binaries with different feature sets in different
1274 # stages. The XZ_OPT env var is only used by the full "XZ utils" implementation, which supports
1275 # the --threads (-T) flag. This allows us to enable multithreaded decompression exclusively on
1276 # that implementation, without the use of complex bash conditionals and checks.
1277 # Since tar does not control the decompression, we need to
1278 # disregard the error code from the xz invocation. Otherwise,
1279 # it can happen that tar exits earlier, causing xz to fail
1280 # from a SIGPIPE.
1281 (XZ_OPT="--threads=$NIX_BUILD_CORES" xz -d < "$fn"; true) | tar xf - --mode=+w --warning=no-timestamp
1282 ;;
1283 *.tar | *.tar.* | *.tgz | *.tbz2 | *.tbz)
1284 # GNU tar can automatically select the decompression method
1285 # (info "(tar) gzip").
1286 tar xf "$fn" --mode=+w --warning=no-timestamp
1287 ;;
1288 *)
1289 return 1
1290 ;;
1291 esac
1292
1293 fi
1294}
1295
1296
1297unpackFile() {
1298 curSrc="$1"
1299 echo "unpacking source archive $curSrc"
1300 if ! runOneHook unpackCmd "$curSrc"; then
1301 echo "do not know how to unpack source archive $curSrc"
1302 exit 1
1303 fi
1304}
1305
1306
1307unpackPhase() {
1308 runHook preUnpack
1309
1310 if [ -z "${srcs:-}" ]; then
1311 if [ -z "${src:-}" ]; then
1312 # shellcheck disable=SC2016
1313 echo 'variable $src or $srcs should point to the source'
1314 exit 1
1315 fi
1316 srcs="$src"
1317 fi
1318
1319 local -a srcsArray
1320 concatTo srcsArray srcs
1321
1322 # To determine the source directory created by unpacking the
1323 # source archives, we record the contents of the current
1324 # directory, then look below which directory got added. Yeah,
1325 # it's rather hacky.
1326 local dirsBefore=""
1327 for i in *; do
1328 if [ -d "$i" ]; then
1329 dirsBefore="$dirsBefore $i "
1330 fi
1331 done
1332
1333 # Unpack all source archives.
1334 for i in "${srcsArray[@]}"; do
1335 unpackFile "$i"
1336 done
1337
1338 # Find the source directory.
1339
1340 # set to empty if unset
1341 : "${sourceRoot=}"
1342
1343 if [ -n "${setSourceRoot:-}" ]; then
1344 runOneHook setSourceRoot
1345 elif [ -z "$sourceRoot" ]; then
1346 for i in *; do
1347 if [ -d "$i" ]; then
1348 case $dirsBefore in
1349 *\ $i\ *)
1350 ;;
1351 *)
1352 if [ -n "$sourceRoot" ]; then
1353 echo "unpacker produced multiple directories"
1354 exit 1
1355 fi
1356 sourceRoot="$i"
1357 ;;
1358 esac
1359 fi
1360 done
1361 fi
1362
1363 if [ -z "$sourceRoot" ]; then
1364 echo "unpacker appears to have produced no directories"
1365 exit 1
1366 fi
1367
1368 echo "source root is $sourceRoot"
1369
1370 # By default, add write permission to the sources. This is often
1371 # necessary when sources have been copied from other store
1372 # locations.
1373 if [ "${dontMakeSourcesWritable:-0}" != 1 ]; then
1374 chmod -R u+w -- "$sourceRoot"
1375 fi
1376
1377 runHook postUnpack
1378}
1379
1380
1381patchPhase() {
1382 runHook prePatch
1383
1384 local -a patchesArray
1385 concatTo patchesArray patches
1386
1387 local -a flagsArray
1388 concatTo flagsArray patchFlags=-p1
1389
1390 for i in "${patchesArray[@]}"; do
1391 echo "applying patch $i"
1392 local uncompress=cat
1393 case "$i" in
1394 *.gz)
1395 uncompress="gzip -d"
1396 ;;
1397 *.bz2)
1398 uncompress="bzip2 -d"
1399 ;;
1400 *.xz)
1401 uncompress="xz -d"
1402 ;;
1403 *.lzma)
1404 uncompress="lzma -d"
1405 ;;
1406 esac
1407
1408 # "2>&1" is a hack to make patch fail if the decompressor fails (nonexistent patch, etc.)
1409 # shellcheck disable=SC2086
1410 $uncompress < "$i" 2>&1 | patch "${flagsArray[@]}"
1411 done
1412
1413 runHook postPatch
1414}
1415
1416
1417fixLibtool() {
1418 local search_path
1419 for flag in $NIX_LDFLAGS; do
1420 case $flag in
1421 -L*)
1422 search_path+=" ${flag#-L}"
1423 ;;
1424 esac
1425 done
1426
1427 sed -i "$1" \
1428 -e "s^eval \(sys_lib_search_path=\).*^\1'${search_path:-}'^" \
1429 -e 's^eval sys_lib_.+search_path=.*^^'
1430}
1431
1432
1433configurePhase() {
1434 runHook preConfigure
1435
1436 # set to empty if unset
1437 : "${configureScript=}"
1438
1439 if [[ -z "$configureScript" && -x ./configure ]]; then
1440 configureScript=./configure
1441 fi
1442
1443 if [ -z "${dontFixLibtool:-}" ]; then
1444 export lt_cv_deplibs_check_method="${lt_cv_deplibs_check_method-pass_all}"
1445 local i
1446 find . -iname "ltmain.sh" -print0 | while IFS='' read -r -d '' i; do
1447 echo "fixing libtool script $i"
1448 fixLibtool "$i"
1449 done
1450
1451 # replace `/usr/bin/file` with `file` in any `configure`
1452 # scripts with vendored libtool code. Preserve mtimes to
1453 # prevent some packages (e.g. libidn2) from spontaneously
1454 # autoreconf'ing themselves
1455 CONFIGURE_MTIME_REFERENCE=$(mktemp configure.mtime.reference.XXXXXX)
1456 find . \
1457 -executable \
1458 -type f \
1459 -name configure \
1460 -exec grep -l 'GNU Libtool is free software; you can redistribute it and/or modify' {} \; \
1461 -exec touch -r {} "$CONFIGURE_MTIME_REFERENCE" \; \
1462 -exec sed -i s_/usr/bin/file_file_g {} \; \
1463 -exec touch -r "$CONFIGURE_MTIME_REFERENCE" {} \;
1464 rm -f "$CONFIGURE_MTIME_REFERENCE"
1465 fi
1466
1467 if [[ -z "${dontAddPrefix:-}" && -n "$prefix" ]]; then
1468 prependToVar configureFlags "${prefixKey:---prefix=}$prefix"
1469 fi
1470
1471 if [[ -f "$configureScript" ]]; then
1472 # Add --disable-dependency-tracking to speed up some builds.
1473 if [ -z "${dontAddDisableDepTrack:-}" ]; then
1474 if grep -q dependency-tracking "$configureScript"; then
1475 prependToVar configureFlags --disable-dependency-tracking
1476 fi
1477 fi
1478
1479 # By default, disable static builds.
1480 if [ -z "${dontDisableStatic:-}" ]; then
1481 if grep -q enable-static "$configureScript"; then
1482 prependToVar configureFlags --disable-static
1483 fi
1484 fi
1485
1486 if [ -z "${dontPatchShebangsInConfigure:-}" ]; then
1487 patchShebangs --build "$configureScript"
1488 fi
1489 fi
1490
1491 if [ -n "$configureScript" ]; then
1492 local -a flagsArray
1493 concatTo flagsArray configureFlags configureFlagsArray
1494
1495 echoCmd 'configure flags' "${flagsArray[@]}"
1496 # shellcheck disable=SC2086
1497 $configureScript "${flagsArray[@]}"
1498 unset flagsArray
1499 else
1500 echo "no configure script, doing nothing"
1501 fi
1502
1503 runHook postConfigure
1504}
1505
1506
1507buildPhase() {
1508 runHook preBuild
1509
1510 if [[ -z "${makeFlags-}" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
1511 echo "no Makefile or custom buildPhase, doing nothing"
1512 else
1513 foundMakefile=1
1514
1515 # shellcheck disable=SC2086
1516 local flagsArray=(
1517 ${enableParallelBuilding:+-j${NIX_BUILD_CORES}}
1518 SHELL="$SHELL"
1519 )
1520 concatTo flagsArray makeFlags makeFlagsArray buildFlags buildFlagsArray
1521
1522 echoCmd 'build flags' "${flagsArray[@]}"
1523 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1524 unset flagsArray
1525 fi
1526
1527 runHook postBuild
1528}
1529
1530
1531checkPhase() {
1532 runHook preCheck
1533
1534 if [[ -z "${foundMakefile:-}" ]]; then
1535 echo "no Makefile or custom checkPhase, doing nothing"
1536 runHook postCheck
1537 return
1538 fi
1539
1540 if [[ -z "${checkTarget:-}" ]]; then
1541 #TODO(@oxij): should flagsArray influence make -n?
1542 if make -n ${makefile:+-f $makefile} check >/dev/null 2>&1; then
1543 checkTarget="check"
1544 elif make -n ${makefile:+-f $makefile} test >/dev/null 2>&1; then
1545 checkTarget="test"
1546 fi
1547 fi
1548
1549 if [[ -z "${checkTarget:-}" ]]; then
1550 echo "no check/test target in ${makefile:-Makefile}, doing nothing"
1551 else
1552 # Old bash empty array hack
1553 # shellcheck disable=SC2086
1554 local flagsArray=(
1555 ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
1556 SHELL="$SHELL"
1557 )
1558
1559 concatTo flagsArray makeFlags makeFlagsArray checkFlags=VERBOSE=y checkFlagsArray checkTarget
1560
1561 echoCmd 'check flags' "${flagsArray[@]}"
1562 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1563
1564 unset flagsArray
1565 fi
1566
1567 runHook postCheck
1568}
1569
1570
1571installPhase() {
1572 runHook preInstall
1573
1574 # Dont reuse 'foundMakefile' set in buildPhase, a makefile may have been created in buildPhase
1575 if [[ -z "${makeFlags-}" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
1576 echo "no Makefile or custom installPhase, doing nothing"
1577 runHook postInstall
1578 return
1579 else
1580 foundMakefile=1
1581 fi
1582
1583 if [ -n "$prefix" ]; then
1584 mkdir -p "$prefix"
1585 fi
1586
1587 # shellcheck disable=SC2086
1588 local flagsArray=(
1589 ${enableParallelInstalling:+-j${NIX_BUILD_CORES}}
1590 SHELL="$SHELL"
1591 )
1592
1593 concatTo flagsArray makeFlags makeFlagsArray installFlags installFlagsArray installTargets=install
1594
1595 echoCmd 'install flags' "${flagsArray[@]}"
1596 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1597 unset flagsArray
1598
1599 runHook postInstall
1600}
1601
1602
1603# The fixup phase performs generic, package-independent stuff, like
1604# stripping binaries, running patchelf and setting
1605# propagated-build-inputs.
1606fixupPhase() {
1607 # Make sure everything is writable so "strip" et al. work.
1608 local output
1609 for output in $(getAllOutputNames); do
1610 # for set*id bits see #300635
1611 if [ -e "${!output}" ]; then chmod -R u+w,u-s,g-s "${!output}"; fi
1612 done
1613
1614 runHook preFixup
1615
1616 # Apply fixup to each output.
1617 local output
1618 for output in $(getAllOutputNames); do
1619 prefix="${!output}" runHook fixupOutput
1620 done
1621
1622
1623 # record propagated dependencies & setup hook into the development output.
1624 recordPropagatedDependencies
1625
1626 if [ -n "${setupHook:-}" ]; then
1627 mkdir -p "${!outputDev}/nix-support"
1628 substituteAll "$setupHook" "${!outputDev}/nix-support/setup-hook"
1629 fi
1630
1631 # TODO(@Ericson2314): Remove after https://github.com/NixOS/nixpkgs/pull/31414
1632 if [ -n "${setupHooks:-}" ]; then
1633 mkdir -p "${!outputDev}/nix-support"
1634 local hook
1635 # have to use ${setupHooks[@]} without quotes because it needs to support setupHooks being a array or a whitespace separated string
1636 # # values of setupHooks won't have spaces so it won't cause problems
1637 # shellcheck disable=2068
1638 for hook in ${setupHooks[@]}; do
1639 local content
1640 consumeEntire content < "$hook"
1641 substituteAllStream content "file '$hook'" >> "${!outputDev}/nix-support/setup-hook"
1642 unset -v content
1643 done
1644 unset -v hook
1645 fi
1646
1647 # Propagate user-env packages into the output with binaries, TODO?
1648
1649 if [ -n "${propagatedUserEnvPkgs[*]:-}" ]; then
1650 mkdir -p "${!outputBin}/nix-support"
1651 printWords "${propagatedUserEnvPkgs[@]}" > "${!outputBin}/nix-support/propagated-user-env-packages"
1652 fi
1653
1654 runHook postFixup
1655}
1656
1657
1658installCheckPhase() {
1659 runHook preInstallCheck
1660
1661 if [[ -z "${foundMakefile:-}" ]]; then
1662 echo "no Makefile or custom installCheckPhase, doing nothing"
1663 #TODO(@oxij): should flagsArray influence make -n?
1664 elif [[ -z "${installCheckTarget:-}" ]] \
1665 && ! make -n ${makefile:+-f $makefile} "${installCheckTarget:-installcheck}" >/dev/null 2>&1; then
1666 echo "no installcheck target in ${makefile:-Makefile}, doing nothing"
1667 else
1668 # Old bash empty array hack
1669 # shellcheck disable=SC2086
1670 local flagsArray=(
1671 ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
1672 SHELL="$SHELL"
1673 )
1674
1675 concatTo flagsArray makeFlags makeFlagsArray \
1676 installCheckFlags installCheckFlagsArray installCheckTarget=installcheck
1677
1678 echoCmd 'installcheck flags' "${flagsArray[@]}"
1679 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1680 unset flagsArray
1681 fi
1682
1683 runHook postInstallCheck
1684}
1685
1686
1687distPhase() {
1688 runHook preDist
1689
1690 local flagsArray=()
1691 concatTo flagsArray distFlags distFlagsArray distTarget=dist
1692
1693 echo 'dist flags: %q' "${flagsArray[@]}"
1694 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1695
1696 if [ "${dontCopyDist:-0}" != 1 ]; then
1697 mkdir -p "$out/tarballs"
1698
1699 # Note: don't quote $tarballs, since we explicitly permit
1700 # wildcards in there.
1701 # shellcheck disable=SC2086
1702 cp -pvd ${tarballs[*]:-*.tar.gz} "$out/tarballs"
1703 fi
1704
1705 runHook postDist
1706}
1707
1708
1709showPhaseHeader() {
1710 local phase="$1"
1711 echo "Running phase: $phase"
1712
1713 # The Nix structured logger allows derivations to update the phase as they're building,
1714 # which shows up in the terminal UI. See `handleJSONLogMessage` in the Nix source.
1715 if [[ -z ${NIX_LOG_FD-} ]]; then
1716 return
1717 fi
1718 printf "@nix { \"action\": \"setPhase\", \"phase\": \"%s\" }\n" "$phase" >&"$NIX_LOG_FD"
1719}
1720
1721
1722showPhaseFooter() {
1723 local phase="$1"
1724 local startTime="$2"
1725 local endTime="$3"
1726 local delta=$(( endTime - startTime ))
1727 (( delta < 30 )) && return
1728
1729 local H=$((delta/3600))
1730 local M=$((delta%3600/60))
1731 local S=$((delta%60))
1732 echo -n "$phase completed in "
1733 (( H > 0 )) && echo -n "$H hours "
1734 (( M > 0 )) && echo -n "$M minutes "
1735 echo "$S seconds"
1736}
1737
1738
1739runPhase() {
1740 local curPhase="$*"
1741 if [[ "$curPhase" = unpackPhase && -n "${dontUnpack:-}" ]]; then return; fi
1742 if [[ "$curPhase" = patchPhase && -n "${dontPatch:-}" ]]; then return; fi
1743 if [[ "$curPhase" = configurePhase && -n "${dontConfigure:-}" ]]; then return; fi
1744 if [[ "$curPhase" = buildPhase && -n "${dontBuild:-}" ]]; then return; fi
1745 if [[ "$curPhase" = checkPhase && -z "${doCheck:-}" ]]; then return; fi
1746 if [[ "$curPhase" = installPhase && -n "${dontInstall:-}" ]]; then return; fi
1747 if [[ "$curPhase" = fixupPhase && -n "${dontFixup:-}" ]]; then return; fi
1748 if [[ "$curPhase" = installCheckPhase && -z "${doInstallCheck:-}" ]]; then return; fi
1749 if [[ "$curPhase" = distPhase && -z "${doDist:-}" ]]; then return; fi
1750
1751 showPhaseHeader "$curPhase"
1752 dumpVars
1753
1754 local startTime endTime
1755 startTime=$(date +"%s")
1756
1757 # Evaluate the variable named $curPhase if it exists, otherwise the
1758 # function named $curPhase.
1759 eval "${!curPhase:-$curPhase}"
1760
1761 endTime=$(date +"%s")
1762
1763 showPhaseFooter "$curPhase" "$startTime" "$endTime"
1764
1765 if [ "$curPhase" = unpackPhase ]; then
1766 # make sure we can cd into the directory
1767 [ -n "${sourceRoot:-}" ] && chmod +x -- "${sourceRoot}"
1768
1769 cd -- "${sourceRoot:-.}"
1770 fi
1771}
1772
1773
1774genericBuild() {
1775 # variable used by our gzip wrapper to add -n.
1776 # gzip is in common-path.nix and is added to nix-shell but we only want to change its behaviour in nix builds. do not move to a setupHook in gzip.
1777 export GZIP_NO_TIMESTAMPS=1
1778
1779 if [ -f "${buildCommandPath:-}" ]; then
1780 source "$buildCommandPath"
1781 return
1782 fi
1783 if [ -n "${buildCommand:-}" ]; then
1784 eval "$buildCommand"
1785 return
1786 fi
1787
1788 if [ -z "${phases[*]:-}" ]; then
1789 phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \
1790 configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \
1791 ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \
1792 ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}";
1793 fi
1794
1795 # The use of ${phases[*]} gives the correct behavior both with and
1796 # without structured attrs. This relies on the fact that each
1797 # phase name is space-free, which it must be because it's the name
1798 # of either a shell variable or a shell function.
1799 for curPhase in ${phases[*]}; do
1800 runPhase "$curPhase"
1801 done
1802}
1803
1804
1805# Execute the post-hooks.
1806runHook postHook
1807
1808
1809# Execute the global user hook (defined through the Nixpkgs
1810# configuration option ‘stdenv.userHook’). This can be used to set
1811# global compiler optimisation flags, for instance.
1812runHook userHook
1813
1814
1815dumpVars
1816
1817
1818# Restore the original options for nix-shell
1819[[ $__nixpkgs_setup_set_original == *e* ]] || set +e
1820[[ $__nixpkgs_setup_set_original == *u* ]] || set +u
1821unset -v __nixpkgs_setup_set_original