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 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 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 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
1178# all environment variables to a file `env-vars' in the build
1179# directory. If the build fails and the `-K' option is used, you can
1180# then go to the build directory and source in `env-vars' to reproduce
1181# the environment used for building.
1182dumpVars() {
1183 if [ "${noDumpEnvVars:-0}" != 1 ]; then
1184 # Don't use `install` here to prevent executing a process each time.
1185
1186 # Set umask to create env-vars file with 0600 permissions (owner read/write only)
1187 local old_umask
1188 old_umask=$(umask)
1189 umask 0077
1190
1191 # Dump all environment variables to the env-vars file
1192 export 2>/dev/null > "$NIX_BUILD_TOP/env-vars"
1193
1194 # Restore original umask
1195 umask "$old_umask"
1196 fi
1197}
1198
1199
1200# Utility function: echo the base name of the given path, with the
1201# prefix `HASH-' removed, if present.
1202stripHash() {
1203 local strippedName casematchOpt=0
1204 # On separate line for `set -e`
1205 strippedName="$(basename -- "$1")"
1206 shopt -q nocasematch && casematchOpt=1
1207 shopt -u nocasematch
1208 if [[ "$strippedName" =~ ^[a-z0-9]{32}- ]]; then
1209 echo "${strippedName:33}"
1210 else
1211 echo "$strippedName"
1212 fi
1213 if (( casematchOpt )); then shopt -s nocasematch; fi
1214}
1215
1216
1217recordPropagatedDependencies() {
1218 # Propagate dependencies into the development output.
1219 declare -ra flatVars=(
1220 # Build
1221 depsBuildBuildPropagated
1222 propagatedNativeBuildInputs
1223 depsBuildTargetPropagated
1224 # Host
1225 depsHostHostPropagated
1226 propagatedBuildInputs
1227 # Target
1228 depsTargetTargetPropagated
1229 )
1230 declare -ra flatFiles=(
1231 "${propagatedBuildDepFiles[@]}"
1232 "${propagatedHostDepFiles[@]}"
1233 "${propagatedTargetDepFiles[@]}"
1234 )
1235
1236 local propagatedInputsIndex
1237 for propagatedInputsIndex in "${!flatVars[@]}"; do
1238 local propagatedInputsSlice="${flatVars[$propagatedInputsIndex]}[@]"
1239 local propagatedInputsFile="${flatFiles[$propagatedInputsIndex]}"
1240
1241 [[ "${!propagatedInputsSlice}" ]] || continue
1242
1243 mkdir -p "${!outputDev}/nix-support"
1244 # shellcheck disable=SC2086
1245 printWords ${!propagatedInputsSlice} > "${!outputDev}/nix-support/$propagatedInputsFile"
1246 done
1247}
1248
1249
1250unpackCmdHooks+=(_defaultUnpack)
1251_defaultUnpack() {
1252 local fn="$1"
1253 local destination
1254
1255 if [ -d "$fn" ]; then
1256
1257 destination="$(stripHash "$fn")"
1258
1259 if [ -e "$destination" ]; then
1260 echo "Cannot copy $fn to $destination: destination already exists!"
1261 echo "Did you specify two \"srcs\" with the same \"name\"?"
1262 return 1
1263 fi
1264
1265 # We can't preserve hardlinks because they may have been
1266 # introduced by store optimization, which might break things
1267 # in the build.
1268 cp -r --preserve=timestamps --reflink=auto -- "$fn" "$destination"
1269
1270 else
1271
1272 case "$fn" in
1273 *.tar.xz | *.tar.lzma | *.txz)
1274 # Don't rely on tar knowing about .xz.
1275 # Additionally, we have multiple different xz binaries with different feature sets in different
1276 # stages. The XZ_OPT env var is only used by the full "XZ utils" implementation, which supports
1277 # the --threads (-T) flag. This allows us to enable multithreaded decompression exclusively on
1278 # that implementation, without the use of complex bash conditionals and checks.
1279 # Since tar does not control the decompression, we need to
1280 # disregard the error code from the xz invocation. Otherwise,
1281 # it can happen that tar exits earlier, causing xz to fail
1282 # from a SIGPIPE.
1283 (XZ_OPT="--threads=$NIX_BUILD_CORES" xz -d < "$fn"; true) | tar xf - --mode=+w --warning=no-timestamp
1284 ;;
1285 *.tar | *.tar.* | *.tgz | *.tbz2 | *.tbz)
1286 # GNU tar can automatically select the decompression method
1287 # (info "(tar) gzip").
1288 tar xf "$fn" --mode=+w --warning=no-timestamp
1289 ;;
1290 *)
1291 return 1
1292 ;;
1293 esac
1294
1295 fi
1296}
1297
1298
1299unpackFile() {
1300 curSrc="$1"
1301 echo "unpacking source archive $curSrc"
1302 if ! runOneHook unpackCmd "$curSrc"; then
1303 echo "do not know how to unpack source archive $curSrc"
1304 exit 1
1305 fi
1306}
1307
1308
1309unpackPhase() {
1310 runHook preUnpack
1311
1312 if [ -z "${srcs:-}" ]; then
1313 if [ -z "${src:-}" ]; then
1314 # shellcheck disable=SC2016
1315 echo 'variable $src or $srcs should point to the source'
1316 exit 1
1317 fi
1318 srcs="$src"
1319 fi
1320
1321 local -a srcsArray
1322 concatTo srcsArray srcs
1323
1324 # To determine the source directory created by unpacking the
1325 # source archives, we record the contents of the current
1326 # directory, then look below which directory got added. Yeah,
1327 # it's rather hacky.
1328 local dirsBefore=""
1329 for i in *; do
1330 if [ -d "$i" ]; then
1331 dirsBefore="$dirsBefore $i "
1332 fi
1333 done
1334
1335 # Unpack all source archives.
1336 for i in "${srcsArray[@]}"; do
1337 unpackFile "$i"
1338 done
1339
1340 # Find the source directory.
1341
1342 # set to empty if unset
1343 : "${sourceRoot=}"
1344
1345 if [ -n "${setSourceRoot:-}" ]; then
1346 runOneHook setSourceRoot
1347 elif [ -z "$sourceRoot" ]; then
1348 for i in *; do
1349 if [ -d "$i" ]; then
1350 case $dirsBefore in
1351 *\ $i\ *)
1352 ;;
1353 *)
1354 if [ -n "$sourceRoot" ]; then
1355 echo "unpacker produced multiple directories"
1356 exit 1
1357 fi
1358 sourceRoot="$i"
1359 ;;
1360 esac
1361 fi
1362 done
1363 fi
1364
1365 if [ -z "$sourceRoot" ]; then
1366 echo "unpacker appears to have produced no directories"
1367 exit 1
1368 fi
1369
1370 echo "source root is $sourceRoot"
1371
1372 # By default, add write permission to the sources. This is often
1373 # necessary when sources have been copied from other store
1374 # locations.
1375 if [ "${dontMakeSourcesWritable:-0}" != 1 ]; then
1376 chmod -R u+w -- "$sourceRoot"
1377 fi
1378
1379 runHook postUnpack
1380}
1381
1382
1383patchPhase() {
1384 runHook prePatch
1385
1386 local -a patchesArray
1387 concatTo patchesArray patches
1388
1389 local -a flagsArray
1390 concatTo flagsArray patchFlags=-p1
1391
1392 for i in "${patchesArray[@]}"; do
1393 echo "applying patch $i"
1394 local uncompress=cat
1395 case "$i" in
1396 *.gz)
1397 uncompress="gzip -d"
1398 ;;
1399 *.bz2)
1400 uncompress="bzip2 -d"
1401 ;;
1402 *.xz)
1403 uncompress="xz -d"
1404 ;;
1405 *.lzma)
1406 uncompress="lzma -d"
1407 ;;
1408 esac
1409
1410 # "2>&1" is a hack to make patch fail if the decompressor fails (nonexistent patch, etc.)
1411 # shellcheck disable=SC2086
1412 $uncompress < "$i" 2>&1 | patch "${flagsArray[@]}"
1413 done
1414
1415 runHook postPatch
1416}
1417
1418
1419fixLibtool() {
1420 local search_path
1421 for flag in $NIX_LDFLAGS; do
1422 case $flag in
1423 -L*)
1424 search_path+=" ${flag#-L}"
1425 ;;
1426 esac
1427 done
1428
1429 sed -i "$1" \
1430 -e "s^eval \(sys_lib_search_path=\).*^\1'${search_path:-}'^" \
1431 -e 's^eval sys_lib_.+search_path=.*^^'
1432}
1433
1434
1435configurePhase() {
1436 runHook preConfigure
1437
1438 # set to empty if unset
1439 : "${configureScript=}"
1440
1441 if [[ -z "$configureScript" && -x ./configure ]]; then
1442 configureScript=./configure
1443 fi
1444
1445 if [ -z "${dontFixLibtool:-}" ]; then
1446 export lt_cv_deplibs_check_method="${lt_cv_deplibs_check_method-pass_all}"
1447 local i
1448 find . -iname "ltmain.sh" -print0 | while IFS='' read -r -d '' i; do
1449 echo "fixing libtool script $i"
1450 fixLibtool "$i"
1451 done
1452
1453 # replace `/usr/bin/file` with `file` in any `configure`
1454 # scripts with vendored libtool code. Preserve mtimes to
1455 # prevent some packages (e.g. libidn2) from spontaneously
1456 # autoreconf'ing themselves
1457 CONFIGURE_MTIME_REFERENCE=$(mktemp configure.mtime.reference.XXXXXX)
1458 find . \
1459 -executable \
1460 -type f \
1461 -name configure \
1462 -exec grep -l 'GNU Libtool is free software; you can redistribute it and/or modify' {} \; \
1463 -exec touch -r {} "$CONFIGURE_MTIME_REFERENCE" \; \
1464 -exec sed -i s_/usr/bin/file_file_g {} \; \
1465 -exec touch -r "$CONFIGURE_MTIME_REFERENCE" {} \;
1466 rm -f "$CONFIGURE_MTIME_REFERENCE"
1467 fi
1468
1469 if [[ -z "${dontAddPrefix:-}" && -n "$prefix" ]]; then
1470 prependToVar configureFlags "${prefixKey:---prefix=}$prefix"
1471 fi
1472
1473 if [[ -f "$configureScript" ]]; then
1474 # Add --disable-dependency-tracking to speed up some builds.
1475 if [ -z "${dontAddDisableDepTrack:-}" ]; then
1476 if grep -q dependency-tracking "$configureScript"; then
1477 prependToVar configureFlags --disable-dependency-tracking
1478 fi
1479 fi
1480
1481 # By default, disable static builds.
1482 if [ -z "${dontDisableStatic:-}" ]; then
1483 if grep -q enable-static "$configureScript"; then
1484 prependToVar configureFlags --disable-static
1485 fi
1486 fi
1487
1488 if [ -z "${dontPatchShebangsInConfigure:-}" ]; then
1489 patchShebangs --build "$configureScript"
1490 fi
1491 fi
1492
1493 if [ -n "$configureScript" ]; then
1494 local -a flagsArray
1495 concatTo flagsArray configureFlags configureFlagsArray
1496
1497 echoCmd 'configure flags' "${flagsArray[@]}"
1498 # shellcheck disable=SC2086
1499 $configureScript "${flagsArray[@]}"
1500 unset flagsArray
1501 else
1502 echo "no configure script, doing nothing"
1503 fi
1504
1505 runHook postConfigure
1506}
1507
1508
1509buildPhase() {
1510 runHook preBuild
1511
1512 if [[ -z "${makeFlags-}" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
1513 echo "no Makefile or custom buildPhase, doing nothing"
1514 else
1515 foundMakefile=1
1516
1517 # shellcheck disable=SC2086
1518 local flagsArray=(
1519 ${enableParallelBuilding:+-j${NIX_BUILD_CORES}}
1520 SHELL="$SHELL"
1521 )
1522 concatTo flagsArray makeFlags makeFlagsArray buildFlags buildFlagsArray
1523
1524 echoCmd 'build flags' "${flagsArray[@]}"
1525 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1526 unset flagsArray
1527 fi
1528
1529 runHook postBuild
1530}
1531
1532
1533checkPhase() {
1534 runHook preCheck
1535
1536 if [[ -z "${foundMakefile:-}" ]]; then
1537 echo "no Makefile or custom checkPhase, doing nothing"
1538 runHook postCheck
1539 return
1540 fi
1541
1542 if [[ -z "${checkTarget:-}" ]]; then
1543 #TODO(@oxij): should flagsArray influence make -n?
1544 if make -n ${makefile:+-f $makefile} check >/dev/null 2>&1; then
1545 checkTarget="check"
1546 elif make -n ${makefile:+-f $makefile} test >/dev/null 2>&1; then
1547 checkTarget="test"
1548 fi
1549 fi
1550
1551 if [[ -z "${checkTarget:-}" ]]; then
1552 echo "no check/test target in ${makefile:-Makefile}, doing nothing"
1553 else
1554 # Old bash empty array hack
1555 # shellcheck disable=SC2086
1556 local flagsArray=(
1557 ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
1558 SHELL="$SHELL"
1559 )
1560
1561 concatTo flagsArray makeFlags makeFlagsArray checkFlags=VERBOSE=y checkFlagsArray checkTarget
1562
1563 echoCmd 'check flags' "${flagsArray[@]}"
1564 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1565
1566 unset flagsArray
1567 fi
1568
1569 runHook postCheck
1570}
1571
1572
1573installPhase() {
1574 runHook preInstall
1575
1576 # Dont reuse 'foundMakefile' set in buildPhase, a makefile may have been created in buildPhase
1577 if [[ -z "${makeFlags-}" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
1578 echo "no Makefile or custom installPhase, doing nothing"
1579 runHook postInstall
1580 return
1581 else
1582 foundMakefile=1
1583 fi
1584
1585 if [ -n "$prefix" ]; then
1586 mkdir -p "$prefix"
1587 fi
1588
1589 # shellcheck disable=SC2086
1590 local flagsArray=(
1591 ${enableParallelInstalling:+-j${NIX_BUILD_CORES}}
1592 SHELL="$SHELL"
1593 )
1594
1595 concatTo flagsArray makeFlags makeFlagsArray installFlags installFlagsArray installTargets=install
1596
1597 echoCmd 'install flags' "${flagsArray[@]}"
1598 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1599 unset flagsArray
1600
1601 runHook postInstall
1602}
1603
1604
1605# The fixup phase performs generic, package-independent stuff, like
1606# stripping binaries, running patchelf and setting
1607# propagated-build-inputs.
1608fixupPhase() {
1609 # Make sure everything is writable so "strip" et al. work.
1610 local output
1611 for output in $(getAllOutputNames); do
1612 # for set*id bits see #300635
1613 if [ -e "${!output}" ]; then chmod -R u+w,u-s,g-s "${!output}"; fi
1614 done
1615
1616 runHook preFixup
1617
1618 # Apply fixup to each output.
1619 local output
1620 for output in $(getAllOutputNames); do
1621 prefix="${!output}" runHook fixupOutput
1622 done
1623
1624
1625 # record propagated dependencies & setup hook into the development output.
1626 recordPropagatedDependencies
1627
1628 if [ -n "${setupHook:-}" ]; then
1629 mkdir -p "${!outputDev}/nix-support"
1630 substituteAll "$setupHook" "${!outputDev}/nix-support/setup-hook"
1631 fi
1632
1633 # TODO(@Ericson2314): Remove after https://github.com/NixOS/nixpkgs/pull/31414
1634 if [ -n "${setupHooks:-}" ]; then
1635 mkdir -p "${!outputDev}/nix-support"
1636 local hook
1637 # have to use ${setupHooks[@]} without quotes because it needs to support setupHooks being a array or a whitespace separated string
1638 # # values of setupHooks won't have spaces so it won't cause problems
1639 # shellcheck disable=2068
1640 for hook in ${setupHooks[@]}; do
1641 local content
1642 consumeEntire content < "$hook"
1643 substituteAllStream content "file '$hook'" >> "${!outputDev}/nix-support/setup-hook"
1644 unset -v content
1645 done
1646 unset -v hook
1647 fi
1648
1649 # Propagate user-env packages into the output with binaries, TODO?
1650
1651 if [ -n "${propagatedUserEnvPkgs[*]:-}" ]; then
1652 mkdir -p "${!outputBin}/nix-support"
1653 printWords "${propagatedUserEnvPkgs[@]}" > "${!outputBin}/nix-support/propagated-user-env-packages"
1654 fi
1655
1656 runHook postFixup
1657}
1658
1659
1660installCheckPhase() {
1661 runHook preInstallCheck
1662
1663 if [[ -z "${foundMakefile:-}" ]]; then
1664 echo "no Makefile or custom installCheckPhase, doing nothing"
1665 #TODO(@oxij): should flagsArray influence make -n?
1666 elif [[ -z "${installCheckTarget:-}" ]] \
1667 && ! make -n ${makefile:+-f $makefile} "${installCheckTarget:-installcheck}" >/dev/null 2>&1; then
1668 echo "no installcheck target in ${makefile:-Makefile}, doing nothing"
1669 else
1670 # Old bash empty array hack
1671 # shellcheck disable=SC2086
1672 local flagsArray=(
1673 ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
1674 SHELL="$SHELL"
1675 )
1676
1677 concatTo flagsArray makeFlags makeFlagsArray \
1678 installCheckFlags installCheckFlagsArray installCheckTarget=installcheck
1679
1680 echoCmd 'installcheck flags' "${flagsArray[@]}"
1681 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1682 unset flagsArray
1683 fi
1684
1685 runHook postInstallCheck
1686}
1687
1688
1689distPhase() {
1690 runHook preDist
1691
1692 local flagsArray=()
1693 concatTo flagsArray distFlags distFlagsArray distTarget=dist
1694
1695 echo 'dist flags: %q' "${flagsArray[@]}"
1696 make ${makefile:+-f $makefile} "${flagsArray[@]}"
1697
1698 if [ "${dontCopyDist:-0}" != 1 ]; then
1699 mkdir -p "$out/tarballs"
1700
1701 # Note: don't quote $tarballs, since we explicitly permit
1702 # wildcards in there.
1703 # shellcheck disable=SC2086
1704 cp -pvd ${tarballs[*]:-*.tar.gz} "$out/tarballs"
1705 fi
1706
1707 runHook postDist
1708}
1709
1710
1711showPhaseHeader() {
1712 local phase="$1"
1713 echo "Running phase: $phase"
1714
1715 # The Nix structured logger allows derivations to update the phase as they're building,
1716 # which shows up in the terminal UI. See `handleJSONLogMessage` in the Nix source.
1717 if [[ -z ${NIX_LOG_FD-} ]]; then
1718 return
1719 fi
1720 printf "@nix { \"action\": \"setPhase\", \"phase\": \"%s\" }\n" "$phase" >&"$NIX_LOG_FD"
1721}
1722
1723
1724showPhaseFooter() {
1725 local phase="$1"
1726 local startTime="$2"
1727 local endTime="$3"
1728 local delta=$(( endTime - startTime ))
1729 (( delta < 30 )) && return
1730
1731 local H=$((delta/3600))
1732 local M=$((delta%3600/60))
1733 local S=$((delta%60))
1734 echo -n "$phase completed in "
1735 (( H > 0 )) && echo -n "$H hours "
1736 (( M > 0 )) && echo -n "$M minutes "
1737 echo "$S seconds"
1738}
1739
1740
1741runPhase() {
1742 local curPhase="$*"
1743 if [[ "$curPhase" = unpackPhase && -n "${dontUnpack:-}" ]]; then return; fi
1744 if [[ "$curPhase" = patchPhase && -n "${dontPatch:-}" ]]; then return; fi
1745 if [[ "$curPhase" = configurePhase && -n "${dontConfigure:-}" ]]; then return; fi
1746 if [[ "$curPhase" = buildPhase && -n "${dontBuild:-}" ]]; then return; fi
1747 if [[ "$curPhase" = checkPhase && -z "${doCheck:-}" ]]; then return; fi
1748 if [[ "$curPhase" = installPhase && -n "${dontInstall:-}" ]]; then return; fi
1749 if [[ "$curPhase" = fixupPhase && -n "${dontFixup:-}" ]]; then return; fi
1750 if [[ "$curPhase" = installCheckPhase && -z "${doInstallCheck:-}" ]]; then return; fi
1751 if [[ "$curPhase" = distPhase && -z "${doDist:-}" ]]; then return; fi
1752
1753 showPhaseHeader "$curPhase"
1754 dumpVars
1755
1756 local startTime endTime
1757 startTime=$(date +"%s")
1758
1759 # Evaluate the variable named $curPhase if it exists, otherwise the
1760 # function named $curPhase.
1761 eval "${!curPhase:-$curPhase}"
1762
1763 endTime=$(date +"%s")
1764
1765 showPhaseFooter "$curPhase" "$startTime" "$endTime"
1766
1767 if [ "$curPhase" = unpackPhase ]; then
1768 # make sure we can cd into the directory
1769 [ -n "${sourceRoot:-}" ] && chmod +x -- "${sourceRoot}"
1770
1771 cd -- "${sourceRoot:-.}"
1772 fi
1773}
1774
1775
1776genericBuild() {
1777 # variable used by our gzip wrapper to add -n.
1778 # 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.
1779 export GZIP_NO_TIMESTAMPS=1
1780
1781 if [ -f "${buildCommandPath:-}" ]; then
1782 source "$buildCommandPath"
1783 return
1784 fi
1785 if [ -n "${buildCommand:-}" ]; then
1786 eval "$buildCommand"
1787 return
1788 fi
1789
1790 if [ -z "${phases[*]:-}" ]; then
1791 phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \
1792 configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \
1793 ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \
1794 ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}";
1795 fi
1796
1797 # The use of ${phases[*]} gives the correct behavior both with and
1798 # without structured attrs. This relies on the fact that each
1799 # phase name is space-free, which it must be because it's the name
1800 # of either a shell variable or a shell function.
1801 for curPhase in ${phases[*]}; do
1802 runPhase "$curPhase"
1803 done
1804}
1805
1806
1807# Execute the post-hooks.
1808runHook postHook
1809
1810
1811# Execute the global user hook (defined through the Nixpkgs
1812# configuration option ‘stdenv.userHook’). This can be used to set
1813# global compiler optimisation flags, for instance.
1814runHook userHook
1815
1816
1817dumpVars
1818
1819
1820# Restore the original options for nix-shell
1821[[ $__nixpkgs_setup_set_original == *e* ]] || set +e
1822[[ $__nixpkgs_setup_set_original == *u* ]] || set +u
1823unset -v __nixpkgs_setup_set_original