about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--pkgs/applications/networking/cluster/nomad-autoscaler/default.nix2
-rw-r--r--pkgs/build-support/bintools-wrapper/default.nix4
-rw-r--r--pkgs/build-support/release/nix-build.nix2
-rw-r--r--pkgs/build-support/setup-hooks/auto-patchelf.sh2
-rw-r--r--pkgs/build-support/setup-hooks/move-docs.sh12
-rw-r--r--pkgs/build-support/setup-hooks/multiple-outputs.sh34
-rw-r--r--pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh2
-rw-r--r--pkgs/build-support/setup-hooks/strip.sh21
-rw-r--r--pkgs/build-support/setup-hooks/win-dll-link.sh2
-rw-r--r--pkgs/build-support/testers/expect-failure.sh2
-rw-r--r--pkgs/data/icons/catppuccin-cursors/default.nix2
-rw-r--r--pkgs/data/icons/comixcursors/default.nix2
-rw-r--r--pkgs/desktops/gnome/core/gdm/default.nix2
-rw-r--r--pkgs/development/compilers/openjdk/11.nix4
-rw-r--r--pkgs/development/compilers/openjdk/12.nix4
-rw-r--r--pkgs/development/compilers/openjdk/13.nix4
-rw-r--r--pkgs/development/compilers/openjdk/14.nix4
-rw-r--r--pkgs/development/compilers/openjdk/15.nix4
-rw-r--r--pkgs/development/compilers/openjdk/16.nix4
-rw-r--r--pkgs/development/compilers/openjdk/17.nix4
-rw-r--r--pkgs/development/compilers/openjdk/18.nix4
-rw-r--r--pkgs/development/compilers/openjdk/19.nix4
-rw-r--r--pkgs/development/compilers/openjdk/8.nix4
-rw-r--r--pkgs/development/libraries/polkit/default.nix2
-rw-r--r--pkgs/os-specific/darwin/signing-utils/auto-sign-hook.sh2
-rw-r--r--pkgs/stdenv/generic/default-builder.sh4
-rw-r--r--pkgs/stdenv/generic/make-derivation.nix31
-rw-r--r--pkgs/stdenv/generic/setup.sh222
-rw-r--r--pkgs/tools/typesetting/tex/texlive/bin.nix2
29 files changed, 272 insertions, 120 deletions
diff --git a/pkgs/applications/networking/cluster/nomad-autoscaler/default.nix b/pkgs/applications/networking/cluster/nomad-autoscaler/default.nix
index 6329ff9ed11c0..eb185f9743c1f 100644
--- a/pkgs/applications/networking/cluster/nomad-autoscaler/default.nix
+++ b/pkgs/applications/networking/cluster/nomad-autoscaler/default.nix
@@ -45,7 +45,7 @@ let
       mv bin/nomad-autoscaler $bin/bin
       ln -s $bin/bin/nomad-autoscaler $out/bin/nomad-autoscaler
 
-      for d in $outputs; do
+      for d in $(getAllOutputNames); do
         mkdir -p ''${!d}/share
       done
       rmdir $bin/share
diff --git a/pkgs/build-support/bintools-wrapper/default.nix b/pkgs/build-support/bintools-wrapper/default.nix
index 121b50fe0f523..ea925e90fa479 100644
--- a/pkgs/build-support/bintools-wrapper/default.nix
+++ b/pkgs/build-support/bintools-wrapper/default.nix
@@ -68,7 +68,7 @@ let
   # The dynamic linker has different names on different platforms. This is a
   # shell glob that ought to match it.
   dynamicLinker =
-    /**/ if sharedLibraryLoader == null then null
+    /**/ if sharedLibraryLoader == null then ""
     else if targetPlatform.libc == "musl"             then "${sharedLibraryLoader}/lib/ld-musl-*"
     else if targetPlatform.libc == "uclibc"           then "${sharedLibraryLoader}/lib/ld*-uClibc.so.1"
     else if (targetPlatform.libc == "bionic" && targetPlatform.is32bit) then "/system/bin/linker"
@@ -87,7 +87,7 @@ let
     else if targetPlatform.isDarwin                   then "/usr/lib/dyld"
     else if targetPlatform.isFreeBSD                  then "/libexec/ld-elf.so.1"
     else if lib.hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
-    else null;
+    else "";
 
   expand-response-params =
     if buildPackages ? stdenv && buildPackages.stdenv.hasCC && buildPackages.stdenv.cc != "/dev/null"
diff --git a/pkgs/build-support/release/nix-build.nix b/pkgs/build-support/release/nix-build.nix
index ac51b90e01639..5ed2b0752efc5 100644
--- a/pkgs/build-support/release/nix-build.nix
+++ b/pkgs/build-support/release/nix-build.nix
@@ -124,7 +124,7 @@ stdenv.mkDerivation (
       echo "$system" > $out/nix-support/system
 
       if [ -z "${toString doingAnalysis}" ]; then
-          for i in $outputs; do
+          for i in $(getAllOutputNames); do
               if [ "$i" = out ]; then j=none; else j="$i"; fi
               mkdir -p ''${!i}/nix-support
               echo "nix-build $j ''${!i}" >> ''${!i}/nix-support/hydra-build-products
diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 7f5ff146e30b6..8a74a69bdcebb 100644
--- a/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -84,7 +84,7 @@ autoPatchelf() {
 # (Expressions don't expand in single quotes, use double quotes for that.)
 postFixupHooks+=('
     if [ -z "${dontAutoPatchelf-}" ]; then
-        autoPatchelf -- $(for output in $outputs; do
+        autoPatchelf -- $(for output in $(getAllOutputNames); do
             [ -e "${!output}" ] || continue
             echo "${!output}"
         done)
diff --git a/pkgs/build-support/setup-hooks/move-docs.sh b/pkgs/build-support/setup-hooks/move-docs.sh
index e4460f98816da..833113aa0fc81 100644
--- a/pkgs/build-support/setup-hooks/move-docs.sh
+++ b/pkgs/build-support/setup-hooks/move-docs.sh
@@ -5,10 +5,17 @@
 preFixupHooks+=(_moveToShare)
 
 _moveToShare() {
-    forceShare=${forceShare:=man doc info}
+    if [ -n "$__structuredAttrs" ]; then
+        if [ -z "${forceShare-}" ]; then
+            forceShare=( man doc info )
+        fi
+    else
+        forceShare=( ${forceShare:-man doc info} )
+    fi
+
     if [[ -z "$out" ]]; then return; fi
 
-    for d in $forceShare; do
+    for d in "${forceShare[@]}"; do
         if [ -d "$out/$d" ]; then
             if [ -d "$out/share/$d" ]; then
                 echo "both $d/ and share/$d/ exist!"
@@ -20,4 +27,3 @@ _moveToShare() {
         fi
     done
 }
-
diff --git a/pkgs/build-support/setup-hooks/multiple-outputs.sh b/pkgs/build-support/setup-hooks/multiple-outputs.sh
index 881cf6c90f483..8a2fc2f915e91 100644
--- a/pkgs/build-support/setup-hooks/multiple-outputs.sh
+++ b/pkgs/build-support/setup-hooks/multiple-outputs.sh
@@ -47,7 +47,7 @@ _overrideFirst outputInfo "info" "$outputBin"
 
 # Add standard flags to put files into the desired outputs.
 _multioutConfig() {
-    if [ "$outputs" = "out" ] || [ -z "${setOutputFlags-1}" ]; then return; fi;
+    if [ "$(getAllOutputNames)" = "out" ] || [ -z "${setOutputFlags-1}" ]; then return; fi;
 
     # try to detect share/doc/${shareDocName}
     # Note: sadly, $configureScript detection comes later in configurePhase,
@@ -66,19 +66,17 @@ _multioutConfig() {
         fi
     fi
 
-    configureFlags="\
-        --bindir=${!outputBin}/bin --sbindir=${!outputBin}/sbin \
-        --includedir=${!outputInclude}/include --oldincludedir=${!outputInclude}/include \
-        --mandir=${!outputMan}/share/man --infodir=${!outputInfo}/share/info \
-        --docdir=${!outputDoc}/share/doc/${shareDocName} \
-        --libdir=${!outputLib}/lib --libexecdir=${!outputLib}/libexec \
-        --localedir=${!outputLib}/share/locale \
-        $configureFlags"
-
-    installFlags="\
-        pkgconfigdir=${!outputDev}/lib/pkgconfig \
-        m4datadir=${!outputDev}/share/aclocal aclocaldir=${!outputDev}/share/aclocal \
-        $installFlags"
+    prependToVar configureFlags \
+        --bindir="${!outputBin}"/bin --sbindir="${!outputBin}"/sbin \
+        --includedir="${!outputInclude}"/include --oldincludedir="${!outputInclude}"/include \
+        --mandir="${!outputMan}"/share/man --infodir="${!outputInfo}"/share/info \
+        --docdir="${!outputDoc}"/share/doc/"${shareDocName}" \
+        --libdir="${!outputLib}"/lib --libexecdir="${!outputLib}"/libexec \
+        --localedir="${!outputLib}"/share/locale
+
+    prependToVar installFlags \
+        pkgconfigdir="${!outputDev}"/lib/pkgconfig \
+        m4datadir="${!outputDev}"/share/aclocal aclocaldir="${!outputDev}"/share/aclocal
 }
 
 
@@ -94,7 +92,7 @@ moveToOutput() {
     local patt="$1"
     local dstOut="$2"
     local output
-    for output in $outputs; do
+    for output in $(getAllOutputNames); do
         if [ "${!output}" = "$dstOut" ]; then continue; fi
         local srcPath
         for srcPath in "${!output}"/$patt; do
@@ -149,7 +147,7 @@ _multioutDocs() {
 
 # Move development-only stuff to the desired outputs.
 _multioutDevs() {
-    if [ "$outputs" = "out" ] || [ -z "${moveToDev-1}" ]; then return; fi;
+    if [ "$(getAllOutputNames)" = "out" ] || [ -z "${moveToDev-1}" ]; then return; fi;
     moveToOutput include "${!outputInclude}"
     # these files are sometimes provided even without using the corresponding tool
     moveToOutput lib/pkgconfig "${!outputDev}"
@@ -166,10 +164,10 @@ _multioutDevs() {
 
 # Make the "dev" propagate other outputs needed for development.
 _multioutPropagateDev() {
-    if [ "$outputs" = "out" ]; then return; fi;
+    if [ "$(getAllOutputNames)" = "out" ]; then return; fi;
 
     local outputFirst
-    for outputFirst in $outputs; do
+    for outputFirst in $(getAllOutputNames); do
         break
     done
     local propagaterOutput="$outputDev"
diff --git a/pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh b/pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh
index a450ecd7f963a..77322b245b277 100644
--- a/pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh
+++ b/pkgs/build-support/setup-hooks/patch-ppd-files/patch-ppd-hook.sh
@@ -70,7 +70,7 @@ patchPpdFileCommands () {
     # * outputs of current build before buildInputs
     # * `/lib/cups/filter' before `/bin`
     # * add HOST_PATH at end, so we don't miss anything
-    for path in $outputs; do
+    for path in $(getAllOutputNames); do
         addToSearchPath cupspath "${!path}/lib/cups/filter"
         addToSearchPath cupspath "${!path}/bin"
     done
diff --git a/pkgs/build-support/setup-hooks/strip.sh b/pkgs/build-support/setup-hooks/strip.sh
index 9bd7b24cab545..104b5515b3db6 100644
--- a/pkgs/build-support/setup-hooks/strip.sh
+++ b/pkgs/build-support/setup-hooks/strip.sh
@@ -12,11 +12,20 @@ _doStrip() {
     local -ra stripCmds=(STRIP STRIP_FOR_TARGET)
     local -ra ranlibCmds=(RANLIB RANLIB_FOR_TARGET)
 
+    # TODO(structured-attrs): This doesn't work correctly if one of
+    #   the items in strip*List or strip*Flags contains a space,
+    #   even with structured attrs enabled.  This is OK for now
+    #   because very few packages set any of these, and it doesn't
+    #   affect any of them.
+    #
+    #   After __structuredAttrs = true is universal, come back and
+    #   push arrays all the way through this logic.
+
     # Strip only host paths by default. Leave targets as is.
-    stripDebugList=${stripDebugList:-lib lib32 lib64 libexec bin sbin}
-    stripDebugListTarget=${stripDebugListTarget:-}
-    stripAllList=${stripAllList:-}
-    stripAllListTarget=${stripAllListTarget:-}
+    stripDebugList=${stripDebugList[*]:-lib lib32 lib64 libexec bin sbin}
+    stripDebugListTarget=${stripDebugListTarget[*]:-}
+    stripAllList=${stripAllList[*]:-}
+    stripAllListTarget=${stripAllListTarget[*]:-}
 
     local i
     for i in ${!stripCmds[@]}; do
@@ -30,8 +39,8 @@ _doStrip() {
         if [[ "${dontStrip-}" || "${flag-}" ]] || ! type -f "${stripCmd-}" 2>/dev/null
         then continue; fi
 
-        stripDirs "$stripCmd" "$ranlibCmd" "$debugDirList" "${stripDebugFlags:--S}"
-        stripDirs "$stripCmd" "$ranlibCmd" "$allDirList" "${stripAllFlags:--s}"
+        stripDirs "$stripCmd" "$ranlibCmd" "$debugDirList" "${stripDebugFlags[*]:--S}"
+        stripDirs "$stripCmd" "$ranlibCmd" "$allDirList" "${stripAllFlags[*]:--s}"
     done
 }
 
diff --git a/pkgs/build-support/setup-hooks/win-dll-link.sh b/pkgs/build-support/setup-hooks/win-dll-link.sh
index 6130f32bef86c..ca4cbb349b6c0 100644
--- a/pkgs/build-support/setup-hooks/win-dll-link.sh
+++ b/pkgs/build-support/setup-hooks/win-dll-link.sh
@@ -15,7 +15,7 @@ _linkDLLs() {
     #   prefix $PATH by currently-built outputs
     local DLLPATH=""
     local outName
-    for outName in $outputs; do
+    for outName in $(getAllOutputNames); do
         addToSearchPath DLLPATH "${!outName}/bin"
     done
     DLLPATH="$DLLPATH:$PATH"
diff --git a/pkgs/build-support/testers/expect-failure.sh b/pkgs/build-support/testers/expect-failure.sh
index 0e1bbe9a678c0..052ee85271766 100644
--- a/pkgs/build-support/testers/expect-failure.sh
+++ b/pkgs/build-support/testers/expect-failure.sh
@@ -35,7 +35,7 @@ echo "testBuildFailure: Original builder produced exit code: $r"
 # -----------------------------------------
 # Write the build log to the default output
 
-outs=( $outputs )
+outs=( $(getAllOutputNames) )
 defOut=${outs[0]}
 defOutPath=${!defOut}
 
diff --git a/pkgs/data/icons/catppuccin-cursors/default.nix b/pkgs/data/icons/catppuccin-cursors/default.nix
index d9eccc96a0291..4559705c169b2 100644
--- a/pkgs/data/icons/catppuccin-cursors/default.nix
+++ b/pkgs/data/icons/catppuccin-cursors/default.nix
@@ -50,7 +50,7 @@ stdenvNoCC.mkDerivation {
   installPhase = ''
     runHook preInstall
 
-    for output in $outputs; do
+    for output in $(getAllOutputNames); do
       if [ "$output" != "out" ]; then
         local outputDir="''${!output}"
         local iconsDir="$outputDir"/share/icons
diff --git a/pkgs/data/icons/comixcursors/default.nix b/pkgs/data/icons/comixcursors/default.nix
index b63877b282098..1c4fdc195180b 100644
--- a/pkgs/data/icons/comixcursors/default.nix
+++ b/pkgs/data/icons/comixcursors/default.nix
@@ -52,7 +52,7 @@ stdenvNoCC.mkDerivation rec {
   '';
 
   installPhase = ''
-    for outputName in $outputs ; do
+    for outputName in $(getAllOutputNames) ; do
       if [ $outputName != out ]; then
         local outputDir=''${!outputName};
         local iconsDir=$outputDir/share/icons
diff --git a/pkgs/desktops/gnome/core/gdm/default.nix b/pkgs/desktops/gnome/core/gdm/default.nix
index f1fbe7e49d305..a2265387e1ef7 100644
--- a/pkgs/desktops/gnome/core/gdm/default.nix
+++ b/pkgs/desktops/gnome/core/gdm/default.nix
@@ -143,7 +143,7 @@ stdenv.mkDerivation rec {
     # We use rsync to merge the directories.
     rsync --archive "${DESTDIR}/etc" "$out"
     rm --recursive "${DESTDIR}/etc"
-    for o in $outputs; do
+    for o in $(getAllOutputNames); do
         if [[ "$o" = "debug" ]]; then continue; fi
         rsync --archive "${DESTDIR}/''${!o}" "$(dirname "''${!o}")"
         rm --recursive "${DESTDIR}/''${!o}"
diff --git a/pkgs/development/compilers/openjdk/11.nix b/pkgs/development/compilers/openjdk/11.nix
index 95abb373272c5..33c3177d2927c 100644
--- a/pkgs/development/compilers/openjdk/11.nix
+++ b/pkgs/development/compilers/openjdk/11.nix
@@ -132,12 +132,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/12.nix b/pkgs/development/compilers/openjdk/12.nix
index 60100a5ecc16a..f56cf4a792202 100644
--- a/pkgs/development/compilers/openjdk/12.nix
+++ b/pkgs/development/compilers/openjdk/12.nix
@@ -136,12 +136,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/13.nix b/pkgs/development/compilers/openjdk/13.nix
index 68a0a9fa70072..ce84d334b4261 100644
--- a/pkgs/development/compilers/openjdk/13.nix
+++ b/pkgs/development/compilers/openjdk/13.nix
@@ -136,12 +136,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/14.nix b/pkgs/development/compilers/openjdk/14.nix
index 37c3a6a3de3a6..8e92b906189c7 100644
--- a/pkgs/development/compilers/openjdk/14.nix
+++ b/pkgs/development/compilers/openjdk/14.nix
@@ -132,12 +132,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/15.nix b/pkgs/development/compilers/openjdk/15.nix
index 6ea1d0b1dd31c..c33e937f9f24b 100644
--- a/pkgs/development/compilers/openjdk/15.nix
+++ b/pkgs/development/compilers/openjdk/15.nix
@@ -131,12 +131,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/16.nix b/pkgs/development/compilers/openjdk/16.nix
index 0e1911bb1a703..461cd724144ed 100644
--- a/pkgs/development/compilers/openjdk/16.nix
+++ b/pkgs/development/compilers/openjdk/16.nix
@@ -138,12 +138,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/17.nix b/pkgs/development/compilers/openjdk/17.nix
index bc92b1393fdb3..6d8087d7e948d 100644
--- a/pkgs/development/compilers/openjdk/17.nix
+++ b/pkgs/development/compilers/openjdk/17.nix
@@ -149,12 +149,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort -u | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/18.nix b/pkgs/development/compilers/openjdk/18.nix
index 5be60eb948754..fd620aaaf9a55 100644
--- a/pkgs/development/compilers/openjdk/18.nix
+++ b/pkgs/development/compilers/openjdk/18.nix
@@ -140,12 +140,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort -u | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/19.nix b/pkgs/development/compilers/openjdk/19.nix
index 11b2fa60c7332..9537b0d3ce52a 100644
--- a/pkgs/development/compilers/openjdk/19.nix
+++ b/pkgs/development/compilers/openjdk/19.nix
@@ -140,12 +140,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort -u | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/compilers/openjdk/8.nix b/pkgs/development/compilers/openjdk/8.nix
index c232b1f01f16d..5558a77ad90b6 100644
--- a/pkgs/development/compilers/openjdk/8.nix
+++ b/pkgs/development/compilers/openjdk/8.nix
@@ -187,12 +187,12 @@ let
     postFixup = ''
       # Build the set of output library directories to rpath against
       LIBDIRS=""
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         LIBDIRS="$(find $(eval echo \$$output) -name \*.so\* -exec dirname {} \+ | sort | uniq | tr '\n' ':'):$LIBDIRS"
       done
       # Add the local library paths to remove dependencies on the bootstrap
-      for output in $outputs; do
+      for output in $(getAllOutputNames); do
         if [ "$output" = debug ]; then continue; fi
         OUTPUTDIR=$(eval echo \$$output)
         BINLIBS=$(find $OUTPUTDIR/bin/ -type f; find $OUTPUTDIR -name \*.so\*)
diff --git a/pkgs/development/libraries/polkit/default.nix b/pkgs/development/libraries/polkit/default.nix
index a0344d68a12b5..6ab7a4bb9c3a7 100644
--- a/pkgs/development/libraries/polkit/default.nix
+++ b/pkgs/development/libraries/polkit/default.nix
@@ -167,7 +167,7 @@ stdenv.mkDerivation rec {
     rsync --archive "${DESTDIR}${system}"/* "$out"
     rm --recursive "${DESTDIR}${system}"/*
     rmdir --parents --ignore-fail-on-non-empty "${DESTDIR}${system}"
-    for o in $outputs; do
+    for o in $(getAllOutputNames); do
         rsync --archive "${DESTDIR}/''${!o}" "$(dirname "''${!o}")"
         rm --recursive "${DESTDIR}/''${!o}"
     done
diff --git a/pkgs/os-specific/darwin/signing-utils/auto-sign-hook.sh b/pkgs/os-specific/darwin/signing-utils/auto-sign-hook.sh
index cca65661f8a9c..6a254cd821236 100644
--- a/pkgs/os-specific/darwin/signing-utils/auto-sign-hook.sh
+++ b/pkgs/os-specific/darwin/signing-utils/auto-sign-hook.sh
@@ -25,7 +25,7 @@ signDarwinBinariesIn() {
 signDarwinBinariesInAllOutputs() {
   local output
 
-  for output in $outputs; do
+  for output in $(getAllOutputNames); do
      signDarwinBinariesIn "${!output}"
   done
 }
diff --git a/pkgs/stdenv/generic/default-builder.sh b/pkgs/stdenv/generic/default-builder.sh
index 273fc55c75523..8c6fec7873b6e 100644
--- a/pkgs/stdenv/generic/default-builder.sh
+++ b/pkgs/stdenv/generic/default-builder.sh
@@ -1,2 +1,6 @@
+if [ -f .attrs.sh ]; then
+    . .attrs.sh
+fi
+
 source $stdenv/setup
 genericBuild
diff --git a/pkgs/stdenv/generic/make-derivation.nix b/pkgs/stdenv/generic/make-derivation.nix
index 510537aac9f39..09a71da93b6e5 100644
--- a/pkgs/stdenv/generic/make-derivation.nix
+++ b/pkgs/stdenv/generic/make-derivation.nix
@@ -154,6 +154,12 @@ let
   (! attrs ? outputHash) # Fixed-output drvs can't be content addressed too
   && config.contentAddressedByDefault
 
+# Experimental.  For simple packages mostly just works,
+# but for anything complex, be prepared to debug if enabling.
+, __structuredAttrs ? false
+
+, env ? { }
+
 , ... } @ attrs:
 
 let
@@ -259,13 +265,16 @@ else let
     lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or [])
       (lib.concatLists propagatedDependencies));
 
+  envIsExportable = lib.isAttrs env && !lib.isDerivation env;
+
   derivationArg =
     (removeAttrs attrs
-      ["meta" "passthru" "pos"
+      (["meta" "passthru" "pos"
        "checkInputs" "installCheckInputs"
        "__darwinAllowLocalNetworking"
        "__impureHostDeps" "__propagatedImpureHostDeps"
-       "sandboxProfile" "propagatedSandboxProfile"])
+       "sandboxProfile" "propagatedSandboxProfile"]
+       ++ lib.optionals envIsExportable [ "env" ]))
     // (lib.optionalAttrs (attrs ? name || (attrs ? pname && attrs ? version)) {
       name =
         let
@@ -289,7 +298,7 @@ else let
           then attrs.name + hostSuffix
           else "${attrs.pname}${staticMarker}${hostSuffix}-${attrs.version}"
         );
-    }) // {
+    }) // lib.optionalAttrs (envIsExportable && __structuredAttrs) { env = checkedEnv; } // {
       builder = attrs.realBuilder or stdenv.shell;
       args = attrs.args or ["-e" (attrs.builder or ./default-builder.sh)];
       inherit stdenv;
@@ -304,8 +313,7 @@ else let
 
       userHook = config.stdenv.userHook or null;
       __ignoreNulls = true;
-
-      inherit strictDeps;
+      inherit __structuredAttrs strictDeps;
 
       depsBuildBuild              = lib.elemAt (lib.elemAt dependencies 0) 0;
       nativeBuildInputs           = lib.elemAt (lib.elemAt dependencies 0) 1;
@@ -473,6 +481,17 @@ else let
                    else true);
     };
 
+  checkedEnv =
+    let
+      overlappingNames = lib.intersectLists (lib.attrNames env) (lib.attrNames derivationArg);
+    in
+    assert lib.assertMsg (overlappingNames == [ ])
+      "The ‘env’ attribute set cannot contain any attributes passed to derivation. The following attributes are overlapping: ${lib.concatStringsSep ", " overlappingNames}";
+    lib.mapAttrs
+      (n: v: assert lib.assertMsg (lib.isString v || lib.isBool v || lib.isInt v)
+        "The ‘env’ attribute set can only contain string, boolean or integer attributes. The ‘${n}’ attribute is of type ${builtins.typeOf v}."; v)
+      env;
+
 in
 
 lib.extendDerivation
@@ -509,7 +528,7 @@ lib.extendDerivation
    # should be made available to Nix expressions using the
    # derivation (e.g., in assertions).
    passthru)
-  (derivation derivationArg);
+  (derivation (derivationArg // lib.optionalAttrs envIsExportable checkedEnv));
 
 in
   fnOrAttrs:
diff --git a/pkgs/stdenv/generic/setup.sh b/pkgs/stdenv/generic/setup.sh
index c6cdb6c3df750..de1dad5a16975 100644
--- a/pkgs/stdenv/generic/setup.sh
+++ b/pkgs/stdenv/generic/setup.sh
@@ -15,8 +15,29 @@ if (( "${NIX_DEBUG:-0}" >= 6 )); then
     set -x
 fi
 
-: ${outputs:=out}
+if [ -f .attrs.sh ]; then
+    __structuredAttrs=1
+    echo "structuredAttrs is enabled"
+else
+    __structuredAttrs=
+fi
+
+if [ -n "$__structuredAttrs" ]; then
+    for outputName in "${!outputs[@]}"; do
+        # ex: out=/nix/store/...
+        export "$outputName=${outputs[$outputName]}"
+    done
+else
+    : ${outputs:=out}
+fi
 
+getAllOutputNames() {
+    if [ -n "$__structuredAttrs" ]; then
+        echo "${!outputs[*]}"
+    else
+        echo "$outputs"
+    fi
+}
 
 ######################################################################
 # Hook handling.
@@ -175,6 +196,63 @@ addToSearchPath() {
     addToSearchPathWithCustomDelimiter ":" "$@"
 }
 
+# Prepend elements to variable "$1", which may come from an attr.
+#
+# This is useful in generic setup code, which must (for now) support
+# both derivations with and without __structuredAttrs true, so the
+# variable may be an array or a space-separated string.
+#
+# Expressions for individual packages should simply switch to array
+# syntax when they switch to setting __structuredAttrs = true.
+prependToVar() {
+    local -n nameref="$1"; shift
+    if [ -n "$__structuredAttrs" ]; then
+        nameref=( "$@" ${nameref+"${nameref[@]}"} )
+    else
+        nameref="$* ${nameref-}"
+    fi
+}
+
+# Same as above
+appendToVar() {
+    local -n nameref="$1"; shift
+    if [ -n "$__structuredAttrs" ]; then
+        nameref=( ${nameref+"${nameref[@]}"} "$@" )
+    else
+        nameref="${nameref-} $*"
+    fi
+}
+
+# Accumulate into `flagsArray` the flags from the named variables.
+#
+# If __structuredAttrs, the variables are all treated as arrays
+# and simply concatenated onto `flagsArray`.
+#
+# If not __structuredAttrs, then:
+#   * Each variable is treated as a string, and split on whitespace;
+#   * except variables whose names end in "Array", which are treated
+#     as arrays.
+_accumFlagsArray() {
+    local name
+    if [ -n "$__structuredAttrs" ]; then
+        for name in "$@"; do
+            local -n nameref="$name"
+            flagsArray+=( ${nameref+"${nameref[@]}"} )
+        done
+    else
+        for name in "$@"; do
+            local -n nameref="$name"
+            case "$name" in
+                *Array)
+                    flagsArray+=( ${nameref+"${nameref[@]}"} ) ;;
+                *)
+                    flagsArray+=( ${nameref-} ) ;;
+            esac
+        done
+    fi
+
+}
+
 # Add $1/lib* into rpaths.
 # The function is used in multiple-outputs.sh hook,
 # so it is defined here but tried after the hook.
@@ -255,6 +333,11 @@ printWords() {
 ######################################################################
 # Initialisation.
 
+# export all vars that should be in the ENV
+for envVar in "${!env[@]}"; do
+    declare -x "${envVar}=${env[${envVar}]}"
+done
+
 
 # Set a fallback default value for SOURCE_DATE_EPOCH, used by some build tools
 # to provide a deterministic substitute for the "current" time. Note that
@@ -469,6 +552,10 @@ findInputs() {
     done
 }
 
+# The way we handle deps* and *Inputs works with structured attrs
+# either enabled or disabled.  For this it's convenient that the items
+# in each list must be store paths, and therefore space-free.
+
 # Make sure all are at least defined as empty
 : ${depsBuildBuild=} ${depsBuildBuildPropagated=}
 : ${nativeBuildInputs=} ${propagatedNativeBuildInputs=} ${defaultNativeBuildInputs=}
@@ -477,29 +564,29 @@ findInputs() {
 : ${buildInputs=} ${propagatedBuildInputs=} ${defaultBuildInputs=}
 : ${depsTargetTarget=} ${depsTargetTargetPropagated=}
 
-for pkg in $depsBuildBuild $depsBuildBuildPropagated; do
+for pkg in ${depsBuildBuild[@]} ${depsBuildBuildPropagated[@]}; do
     findInputs "$pkg" -1 -1
 done
-for pkg in $nativeBuildInputs $propagatedNativeBuildInputs; do
+for pkg in ${nativeBuildInputs[@]} ${propagatedNativeBuildInputs[@]}; do
     findInputs "$pkg" -1  0
 done
-for pkg in $depsBuildTarget $depsBuildTargetPropagated; do
+for pkg in ${depsBuildTarget[@]} ${depsBuildTargetPropagated[@]}; do
     findInputs "$pkg" -1  1
 done
-for pkg in $depsHostHost $depsHostHostPropagated; do
+for pkg in ${depsHostHost[@]} ${depsHostHostPropagated[@]}; do
     findInputs "$pkg"  0  0
 done
-for pkg in $buildInputs $propagatedBuildInputs ; do
+for pkg in ${buildInputs[@]} ${propagatedBuildInputs[@]} ; do
     findInputs "$pkg"  0  1
 done
-for pkg in $depsTargetTarget $depsTargetTargetPropagated; do
+for pkg in ${depsTargetTarget[@]} ${depsTargetTargetPropagated[@]}; do
     findInputs "$pkg"  1  1
 done
 # Default inputs must be processed last
-for pkg in $defaultNativeBuildInputs; do
+for pkg in ${defaultNativeBuildInputs[@]}; do
     findInputs "$pkg" -1  0
 done
-for pkg in $defaultBuildInputs; do
+for pkg in ${defaultBuildInputs[@]}; do
     findInputs "$pkg"  0  1
 done
 
@@ -909,6 +996,13 @@ unpackPhase() {
         srcs="$src"
     fi
 
+    local -a srcsArray
+    if [ -n "$__structuredAttrs" ]; then
+        srcsArray=( "${srcs[@]}" )
+    else
+        srcsArray=( $srcs )
+    fi
+
     # To determine the source directory created by unpacking the
     # source archives, we record the contents of the current
     # directory, then look below which directory got added.  Yeah,
@@ -921,7 +1015,7 @@ unpackPhase() {
     done
 
     # Unpack all source archives.
-    for i in $srcs; do
+    for i in "${srcsArray[@]}"; do
         unpackFile "$i"
     done
 
@@ -971,7 +1065,14 @@ unpackPhase() {
 patchPhase() {
     runHook prePatch
 
-    for i in ${patches:-}; do
+    local -a patchesArray
+    if [ -n "$__structuredAttrs" ]; then
+        patchesArray=( ${patches:+"${patches[@]}"} )
+    else
+        patchesArray=( ${patches:-} )
+    fi
+
+    for i in "${patchesArray[@]}"; do
         header "applying patch $i" 3
         local uncompress=cat
         case "$i" in
@@ -988,9 +1089,17 @@ patchPhase() {
                 uncompress="lzma -d"
                 ;;
         esac
+
+        local -a flagsArray
+        if [ -n "$__structuredAttrs" ]; then
+            flagsArray=( "${patchFlags[@]:--p1}" )
+        else
+            # shellcheck disable=SC2086
+            flagsArray=( ${patchFlags:--p1} )
+        fi
         # "2>&1" is a hack to make patch fail if the decompressor fails (nonexistent patch, etc.)
         # shellcheck disable=SC2086
-        $uncompress < "$i" 2>&1 | patch ${patchFlags:--p1}
+        $uncompress < "$i" 2>&1 | patch "${flagsArray[@]}"
     done
 
     runHook postPatch
@@ -1018,7 +1127,6 @@ configurePhase() {
 
     # set to empty if unset
     : ${configureScript=}
-    : ${configureFlags=}
 
     if [[ -z "$configureScript" && -x ./configure ]]; then
         configureScript=./configure
@@ -1049,31 +1157,29 @@ configurePhase() {
     fi
 
     if [[ -z "${dontAddPrefix:-}" && -n "$prefix" ]]; then
-        configureFlags="${prefixKey:---prefix=}$prefix $configureFlags"
+        prependToVar configureFlags "${prefixKey:---prefix=}$prefix"
     fi
 
     if [[ -f "$configureScript" ]]; then
         # Add --disable-dependency-tracking to speed up some builds.
         if [ -z "${dontAddDisableDepTrack:-}" ]; then
             if grep -q dependency-tracking "$configureScript"; then
-                configureFlags="--disable-dependency-tracking $configureFlags"
+                prependToVar configureFlags --disable-dependency-tracking
             fi
         fi
 
         # By default, disable static builds.
         if [ -z "${dontDisableStatic:-}" ]; then
             if grep -q enable-static "$configureScript"; then
-                configureFlags="--disable-static $configureFlags"
+                prependToVar configureFlags --disable-static
             fi
         fi
     fi
 
     if [ -n "$configureScript" ]; then
-        # Old bash empty array hack
-        # shellcheck disable=SC2086
-        local flagsArray=(
-            $configureFlags "${configureFlagsArray[@]}"
-        )
+        local -a flagsArray
+        _accumFlagsArray configureFlags configureFlagsArray
+
         echoCmd 'configure flags' "${flagsArray[@]}"
         # shellcheck disable=SC2086
         $configureScript "${flagsArray[@]}"
@@ -1089,22 +1195,17 @@ configurePhase() {
 buildPhase() {
     runHook preBuild
 
-    # set to empty if unset
-    : ${makeFlags=}
-
-    if [[ -z "$makeFlags" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
+    if [[ -z "${makeFlags-}" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
         echo "no Makefile, doing nothing"
     else
         foundMakefile=1
 
-        # Old bash empty array hack
         # shellcheck disable=SC2086
         local flagsArray=(
             ${enableParallelBuilding:+-j${NIX_BUILD_CORES}}
             SHELL=$SHELL
-            $makeFlags "${makeFlagsArray[@]}"
-            $buildFlags "${buildFlagsArray[@]}"
         )
+        _accumFlagsArray makeFlags makeFlagsArray buildFlags buildFlagsArray
 
         echoCmd 'build flags' "${flagsArray[@]}"
         make ${makefile:+-f $makefile} "${flagsArray[@]}"
@@ -1141,11 +1242,17 @@ checkPhase() {
         local flagsArray=(
             ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
             SHELL=$SHELL
-            $makeFlags "${makeFlagsArray[@]}"
-            ${checkFlags:-VERBOSE=y} "${checkFlagsArray[@]}"
-            ${checkTarget}
         )
 
+        _accumFlagsArray makeFlags makeFlagsArray
+        if [ -n "$__structuredAttrs" ]; then
+            flagsArray+=( "${checkFlags[@]:-VERBOSE=y}" )
+        else
+            flagsArray+=( ${checkFlags:-VERBOSE=y} )
+        fi
+        _accumFlagsArray checkFlagsArray
+        flagsArray+=( ${checkTarget} )
+
         echoCmd 'check flags' "${flagsArray[@]}"
         make ${makefile:+-f $makefile} "${flagsArray[@]}"
 
@@ -1163,14 +1270,16 @@ installPhase() {
         mkdir -p "$prefix"
     fi
 
-    # Old bash empty array hack
     # shellcheck disable=SC2086
     local flagsArray=(
         SHELL=$SHELL
-        $makeFlags "${makeFlagsArray[@]}"
-        $installFlags "${installFlagsArray[@]}"
-        ${installTargets:-install}
     )
+    _accumFlagsArray makeFlags makeFlagsArray installFlags installFlagsArray
+    if [ -n "$__structuredAttrs" ]; then
+        flagsArray+=( "${installTargets[@]:-install}" )
+    else
+        flagsArray+=( ${installTargets:-install} )
+    fi
 
     echoCmd 'install flags' "${flagsArray[@]}"
     make ${makefile:+-f $makefile} "${flagsArray[@]}"
@@ -1186,7 +1295,7 @@ installPhase() {
 fixupPhase() {
     # Make sure everything is writable so "strip" et al. work.
     local output
-    for output in $outputs; do
+    for output in $(getAllOutputNames); do
         if [ -e "${!output}" ]; then chmod -R u+w "${!output}"; fi
     done
 
@@ -1194,7 +1303,7 @@ fixupPhase() {
 
     # Apply fixup to each output.
     local output
-    for output in $outputs; do
+    for output in $(getAllOutputNames); do
         prefix="${!output}" runHook fixupOutput
     done
 
@@ -1239,7 +1348,10 @@ fixupPhase() {
     if [ -n "${setupHooks:-}" ]; then
         mkdir -p "${!outputDev}/nix-support"
         local hook
-        for hook in $setupHooks; do
+        # have to use ${setupHooks[@]} without quotes because it needs to support setupHooks being a array or a whitespace separated string
+        # # values of setupHooks won't have spaces so it won't cause problems
+        # shellcheck disable=2068
+        for hook in ${setupHooks[@]}; do
             local content
             consumeEntire content < "$hook"
             substituteAllStream content "file '$hook'" >> "${!outputDev}/nix-support/setup-hook"
@@ -1275,11 +1387,12 @@ installCheckPhase() {
         local flagsArray=(
             ${enableParallelChecking:+-j${NIX_BUILD_CORES}}
             SHELL=$SHELL
-            $makeFlags "${makeFlagsArray[@]}"
-            $installCheckFlags "${installCheckFlagsArray[@]}"
-            ${installCheckTarget:-installcheck}
         )
 
+        _accumFlagsArray makeFlags makeFlagsArray \
+          installCheckFlags installCheckFlagsArray
+        flagsArray+=( ${installCheckTarget:-installcheck} )
+
         echoCmd 'installcheck flags' "${flagsArray[@]}"
         make ${makefile:+-f $makefile} "${flagsArray[@]}"
         unset flagsArray
@@ -1292,11 +1405,9 @@ installCheckPhase() {
 distPhase() {
     runHook preDist
 
-    # Old bash empty array hack
-    # shellcheck disable=SC2086
-    local flagsArray=(
-        $distFlags "${distFlagsArray[@]}" ${distTarget:-dist}
-    )
+    local flagsArray=()
+    _accumFlagsArray distFlags distFlagsArray
+    flagsArray+=( ${distTarget:-dist} )
 
     echo 'dist flags: %q' "${flagsArray[@]}"
     make ${makefile:+-f $makefile} "${flagsArray[@]}"
@@ -1307,7 +1418,7 @@ distPhase() {
         # Note: don't quote $tarballs, since we explicitly permit
         # wildcards in there.
         # shellcheck disable=SC2086
-        cp -pvd ${tarballs:-*.tar.gz} "$out/tarballs"
+        cp -pvd ${tarballs[*]:-*.tar.gz} "$out/tarballs"
     fi
 
     runHook postDist
@@ -1357,14 +1468,18 @@ genericBuild() {
         return
     fi
 
-    if [ -z "${phases:-}" ]; then
-        phases="${prePhases:-} unpackPhase patchPhase ${preConfigurePhases:-} \
-            configurePhase ${preBuildPhases:-} buildPhase checkPhase \
-            ${preInstallPhases:-} installPhase ${preFixupPhases:-} fixupPhase installCheckPhase \
-            ${preDistPhases:-} distPhase ${postPhases:-}";
+    if [ -z "${phases[*]:-}" ]; then
+        phases="${prePhases[*]:-} unpackPhase patchPhase ${preConfigurePhases[*]:-} \
+            configurePhase ${preBuildPhases[*]:-} buildPhase checkPhase \
+            ${preInstallPhases[*]:-} installPhase ${preFixupPhases[*]:-} fixupPhase installCheckPhase \
+            ${preDistPhases[*]:-} distPhase ${postPhases[*]:-}";
     fi
 
-    for curPhase in $phases; do
+    # The use of ${phases[*]} gives the correct behavior both with and
+    # without structured attrs.  This relies on the fact that each
+    # phase name is space-free, which it must be because it's the name
+    # of either a shell variable or a shell function.
+    for curPhase in ${phases[*]}; do
         if [[ "$curPhase" = unpackPhase && -n "${dontUnpack:-}" ]]; then continue; fi
         if [[ "$curPhase" = patchPhase && -n "${dontPatch:-}" ]]; then continue; fi
         if [[ "$curPhase" = configurePhase && -n "${dontConfigure:-}" ]]; then continue; fi
@@ -1414,6 +1529,7 @@ runHook userHook
 
 dumpVars
 
+
 # Restore the original options for nix-shell
 [[ $__nixpkgs_setup_set_original == *e* ]] || set +e
 [[ $__nixpkgs_setup_set_original == *u* ]] || set +u
diff --git a/pkgs/tools/typesetting/tex/texlive/bin.nix b/pkgs/tools/typesetting/tex/texlive/bin.nix
index 6c7d8880a3167..d874f443f4a35 100644
--- a/pkgs/tools/typesetting/tex/texlive/bin.nix
+++ b/pkgs/tools/typesetting/tex/texlive/bin.nix
@@ -247,7 +247,7 @@ core-big = stdenv.mkDerivation { #TODO: upmendex
     "xetex"
   ];
   postInstall = ''
-    for output in $outputs; do
+    for output in $(getAllOutputNames); do
       mkdir -p "''${!output}/bin"
     done