about summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/agda/default.nix34
-rw-r--r--pkgs/build-support/bintools-wrapper/default.nix65
-rw-r--r--pkgs/build-support/build-fhsenv-bubblewrap/default.nix40
-rw-r--r--pkgs/build-support/coq/default.nix27
-rw-r--r--pkgs/build-support/coq/extra-lib.nix22
-rw-r--r--pkgs/build-support/coq/meta-fetch/default.nix44
-rw-r--r--pkgs/build-support/fetchrepoproject/default.nix9
-rw-r--r--pkgs/build-support/fetchsourcehut/default.nix14
-rw-r--r--pkgs/build-support/nix-gitignore/default.nix57
-rw-r--r--pkgs/build-support/pkg-config-wrapper/default.nix22
-rw-r--r--pkgs/build-support/release/default.nix22
-rw-r--r--pkgs/build-support/replace-dependency.nix21
-rw-r--r--pkgs/build-support/setup-hooks/auto-patchelf.sh27
-rw-r--r--pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh46
-rw-r--r--pkgs/build-support/setup-hooks/make-wrapper.sh4
-rw-r--r--pkgs/build-support/vm/test.nix20
-rw-r--r--pkgs/build-support/writers/scripts.nix498
-rw-r--r--pkgs/build-support/writers/test.nix130
18 files changed, 830 insertions, 272 deletions
diff --git a/pkgs/build-support/agda/default.nix b/pkgs/build-support/agda/default.nix
index 681d1ea681bb4..44c9ade02ffb8 100644
--- a/pkgs/build-support/agda/default.nix
+++ b/pkgs/build-support/agda/default.nix
@@ -2,14 +2,28 @@
 
 { stdenv, lib, self, Agda, runCommand, makeWrapper, writeText, ghcWithPackages, nixosTests }:
 
-with lib.strings;
-
 let
+  inherit (lib)
+    attrValues
+    elem
+    filter
+    filterAttrs
+    isAttrs
+    isList
+    platforms
+    ;
+
+  inherit (lib.strings)
+    concatMapStrings
+    concatMapStringsSep
+    optionalString
+    ;
+
   withPackages' = {
     pkgs,
     ghc ? ghcWithPackages (p: with p; [ ieee754 ])
   }: let
-    pkgs' = if builtins.isList pkgs then pkgs else pkgs self;
+    pkgs' = if isList pkgs then pkgs else pkgs self;
     library-file = writeText "libraries" ''
       ${(concatMapStringsSep "\n" (p: "${p}/${p.libraryFile}") pkgs')}
     '';
@@ -23,7 +37,7 @@ let
       inherit withPackages;
       tests = {
         inherit (nixosTests) agda;
-        allPackages = withPackages (lib.filter self.lib.isUnbrokenAgdaPackage (lib.attrValues self));
+        allPackages = withPackages (filter self.lib.isUnbrokenAgdaPackage (attrValues self));
       };
     };
     inherit (Agda) meta;
@@ -35,7 +49,7 @@ let
     ln -s ${Agda}/bin/agda-mode $out/bin/agda-mode
     '';
 
-  withPackages = arg: if builtins.isAttrs arg then withPackages' arg else withPackages' { pkgs = arg; };
+  withPackages = arg: if isAttrs arg then withPackages' arg else withPackages' { pkgs = arg; };
 
   extensions = [
     "agda"
@@ -62,7 +76,7 @@ let
     , extraExtensions ? []
     , ...
     }: let
-      agdaWithArgs = withPackages (builtins.filter (p: p ? isAgdaDerivation) buildInputs);
+      agdaWithArgs = withPackages (filter (p: p ? isAgdaDerivation) buildInputs);
       includePathArgs = concatMapStrings (path: "-i" + path + " ") (includePaths ++ [(dirOf everythingFile)]);
     in
       {
@@ -91,13 +105,13 @@ let
         # darwin, it seems that there is no standard such locale; luckily,
         # the referenced issue doesn't seem to surface on darwin. Hence let's
         # set this only on non-darwin.
-        LC_ALL = lib.optionalString (!stdenv.isDarwin) "C.UTF-8";
+        LC_ALL = optionalString (!stdenv.isDarwin) "C.UTF-8";
 
-        meta = if meta.broken or false then meta // { hydraPlatforms = lib.platforms.none; } else meta;
+        meta = if meta.broken or false then meta // { hydraPlatforms = platforms.none; } else meta;
 
         # Retrieve all packages from the finished package set that have the current package as a dependency and build them
-        passthru.tests = with builtins;
-          lib.filterAttrs (name: pkg: self.lib.isUnbrokenAgdaPackage pkg && elem pname (map (pkg: pkg.pname) pkg.buildInputs)) self;
+        passthru.tests =
+          filterAttrs (name: pkg: self.lib.isUnbrokenAgdaPackage pkg && elem pname (map (pkg: pkg.pname) pkg.buildInputs)) self;
       };
 in
 {
diff --git a/pkgs/build-support/bintools-wrapper/default.nix b/pkgs/build-support/bintools-wrapper/default.nix
index 1a32f365bab2a..b2545d0ad0e57 100644
--- a/pkgs/build-support/bintools-wrapper/default.nix
+++ b/pkgs/build-support/bintools-wrapper/default.nix
@@ -35,7 +35,7 @@
 
 # Note: the hardening flags are part of the bintools-wrapper, rather than
 # the cc-wrapper, because a few of them are handled by the linker.
-, defaultHardeningFlags ? with stdenvNoCC; [
+, defaultHardeningFlags ? [
     "bindnow"
     "format"
     "fortify"
@@ -44,7 +44,7 @@
     "relro"
     "stackprotector"
     "strictoverflow"
-  ] ++ lib.optional (
+  ] ++ lib.optional (with stdenvNoCC;
     # Musl-based platforms will keep "pie", other platforms will not.
     # If you change this, make sure to update section `{#sec-hardening-in-nixpkgs}`
     # in the nixpkgs manual to inform users about the defaults.
@@ -59,15 +59,30 @@
 , postLinkSignHook ? null, signingUtils ? null
 }:
 
-with lib;
-
 assert nativeTools -> !propagateDoc && nativePrefix != "";
-assert !nativeTools ->
-  bintools != null && coreutils != null && gnugrep != null;
+assert !nativeTools -> bintools != null && coreutils != null && gnugrep != null;
 assert !(nativeLibc && noLibc);
 assert (noLibc || nativeLibc) == (libc == null);
 
 let
+  inherit (lib)
+    attrByPath
+    concatStringsSep
+    getBin
+    getDev
+    getLib
+    getName
+    getVersion
+    hasSuffix
+    optional
+    optionalAttrs
+    optionals
+    optionalString
+    platforms
+    removePrefix
+    replaceStrings
+    ;
+
   stdenv = stdenvNoCC;
   inherit (stdenv) hostPlatform targetPlatform;
 
@@ -75,18 +90,18 @@ let
   #
   # TODO(@Ericson2314) Make unconditional, or optional but always true by
   # default.
-  targetPrefix = lib.optionalString (targetPlatform != hostPlatform)
+  targetPrefix = optionalString (targetPlatform != hostPlatform)
                                         (targetPlatform.config + "-");
 
-  bintoolsVersion = lib.getVersion bintools;
-  bintoolsName = lib.removePrefix targetPrefix (lib.getName bintools);
+  bintoolsVersion = getVersion bintools;
+  bintoolsName = removePrefix targetPrefix (getName bintools);
 
-  libc_bin = lib.optionalString (libc != null) (getBin libc);
-  libc_dev = lib.optionalString (libc != null) (getDev libc);
-  libc_lib = lib.optionalString (libc != null) (getLib libc);
-  bintools_bin = lib.optionalString (!nativeTools) (getBin bintools);
+  libc_bin = optionalString (libc != null) (getBin libc);
+  libc_dev = optionalString (libc != null) (getDev libc);
+  libc_lib = optionalString (libc != null) (getLib libc);
+  bintools_bin = optionalString (!nativeTools) (getBin bintools);
   # The wrapper scripts use 'cat' and 'grep', so we may need coreutils.
-  coreutils_bin = lib.optionalString (!nativeTools) (getBin coreutils);
+  coreutils_bin = optionalString (!nativeTools) (getBin coreutils);
 
   # See description in cc-wrapper.
   suffixSalt = replaceStrings ["-" "."] ["_" "_"] targetPlatform.config;
@@ -114,11 +129,11 @@ let
     else if targetPlatform.isLoongArch64              then "${sharedLibraryLoader}/lib/ld-linux-loongarch*.so.1"
     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 if hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
     else "";
 
   expand-response-params =
-    lib.optionalString (buildPackages ? stdenv && buildPackages.stdenv.hasCC && buildPackages.stdenv.cc != "/dev/null")
+    optionalString (buildPackages ? stdenv && buildPackages.stdenv.hasCC && buildPackages.stdenv.cc != "/dev/null")
     (import ../expand-response-params { inherit (buildPackages) stdenv; });
 
 in
@@ -126,7 +141,7 @@ in
 stdenv.mkDerivation {
   pname = targetPrefix
     + (if name != "" then name else "${bintoolsName}-wrapper");
-  version = lib.optionalString (bintools != null) bintoolsVersion;
+  version = optionalString (bintools != null) bintoolsVersion;
 
   preferLocalBuild = true;
 
@@ -196,7 +211,7 @@ stdenv.mkDerivation {
     # as it must have both the GNU assembler from cctools (installed as `gas`)
     # and the Clang integrated assembler (installed as `as`).
     # See pkgs/os-specific/darwin/binutils/default.nix for details.
-    + lib.optionalString wrapGas ''
+    + optionalString wrapGas ''
       if [ -e $ldPath/${targetPrefix}gas ]; then
         ln -s $ldPath/${targetPrefix}gas $out/bin/${targetPrefix}gas
       fi
@@ -273,7 +288,7 @@ stdenv.mkDerivation {
 
         ${if targetPlatform.isDarwin then ''
           printf "export LD_DYLD_PATH=%q\n" "$dynamicLinker" >> $out/nix-support/setup-hook
-        '' else lib.optionalString (sharedLibraryLoader != null) ''
+        '' else optionalString (sharedLibraryLoader != null) ''
           if [ -e ${sharedLibraryLoader}/lib/32/ld-linux.so.2 ]; then
             echo ${sharedLibraryLoader}/lib/32/ld-linux.so.2 > $out/nix-support/dynamic-linker-m32
           fi
@@ -290,7 +305,7 @@ stdenv.mkDerivation {
     # install the wrapper, you get tools like objdump (same for any
     # binaries of libc).
     + optionalString (!nativeTools) ''
-      printWords ${bintools_bin} ${lib.optionalString (libc != null) libc_bin} > $out/nix-support/propagated-user-env-packages
+      printWords ${bintools_bin} ${optionalString (libc != null) libc_bin} > $out/nix-support/propagated-user-env-packages
     ''
 
     ##
@@ -406,7 +421,7 @@ stdenv.mkDerivation {
     # for substitution in utils.bash
     expandResponseParams = "${expand-response-params}/bin/expand-response-params";
     shell = getBin shell + shell.shellPath or "";
-    gnugrep_bin = lib.optionalString (!nativeTools) gnugrep;
+    gnugrep_bin = optionalString (!nativeTools) gnugrep;
     wrapperName = "BINTOOLS_WRAPPER";
     inherit dynamicLinker targetPrefix suffixSalt coreutils_bin;
     inherit bintools_bin libc_bin libc_dev libc_lib;
@@ -414,13 +429,13 @@ stdenv.mkDerivation {
   };
 
   meta =
-    let bintools_ = lib.optionalAttrs (bintools != null) bintools; in
-    (lib.optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta ["priority"])) //
+    let bintools_ = optionalAttrs (bintools != null) bintools; in
+    (optionalAttrs (bintools_ ? meta) (removeAttrs bintools.meta ["priority"])) //
     { description =
-        lib.attrByPath ["meta" "description"] "System binary utilities" bintools_
+        attrByPath ["meta" "description"] "System binary utilities" bintools_
         + " (wrapper script)";
       priority = 10;
   } // optionalAttrs useMacosReexportHack {
-    platforms = lib.platforms.darwin;
+    platforms = platforms.darwin;
   };
 }
diff --git a/pkgs/build-support/build-fhsenv-bubblewrap/default.nix b/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
index 5b4de2dd04cab..e06fb51dd4b67 100644
--- a/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
+++ b/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
@@ -31,10 +31,20 @@
 
 assert (pname != null || version != null) -> (name == null && pname != null); # You must declare either a name or pname + version (preferred).
 
-with builtins;
 let
+  inherit (lib)
+    concatLines
+    concatStringsSep
+    escapeShellArgs
+    filter
+    optionalString
+    splitString
+    ;
+
+  inherit (lib.attrsets) removeAttrs;
+
   pname = if args ? name && args.name != null then args.name else args.pname;
-  versionStr = lib.optionalString (version != null) ("-" + version);
+  versionStr = optionalString (version != null) ("-" + version);
   name = pname + versionStr;
 
   buildFHSEnv = callPackage ./buildFHSEnv.nix { };
@@ -116,10 +126,10 @@ let
     exec ${run} "$@"
   '';
 
-  indentLines = str: lib.concatLines (map (s: "  " + s) (filter (s: s != "") (lib.splitString "\n" str)));
+  indentLines = str: concatLines (map (s: "  " + s) (filter (s: s != "") (splitString "\n" str)));
   bwrapCmd = { initArgs ? "" }: ''
     ${extraPreBwrapCmds}
-    ignored=(/nix /dev /proc /etc ${lib.optionalString privateTmp "/tmp"})
+    ignored=(/nix /dev /proc /etc ${optionalString privateTmp "/tmp"})
     ro_mounts=()
     symlinks=()
     etc_ignored=()
@@ -164,7 +174,7 @@ let
     fi
 
     # link selected etc entries from the actual root
-    for i in ${lib.escapeShellArgs etcBindEntries}; do
+    for i in ${escapeShellArgs etcBindEntries}; do
       if [[ "''${etc_ignored[@]}" =~ "$i" ]]; then
         continue
       fi
@@ -195,7 +205,7 @@ let
       x11_args+=(--ro-bind-try "$local_socket" "$local_socket")
     fi
 
-    ${lib.optionalString privateTmp ''
+    ${optionalString privateTmp ''
     # sddm places XAUTHORITY in /tmp
     if [[ "$XAUTHORITY" == /tmp/* ]]; then
       x11_args+=(--ro-bind-try "$XAUTHORITY" "$XAUTHORITY")
@@ -220,15 +230,15 @@ let
       --dev-bind /dev /dev
       --proc /proc
       --chdir "$(pwd)"
-      ${lib.optionalString unshareUser "--unshare-user"}
-      ${lib.optionalString unshareIpc "--unshare-ipc"}
-      ${lib.optionalString unsharePid "--unshare-pid"}
-      ${lib.optionalString unshareNet "--unshare-net"}
-      ${lib.optionalString unshareUts "--unshare-uts"}
-      ${lib.optionalString unshareCgroup "--unshare-cgroup"}
-      ${lib.optionalString dieWithParent "--die-with-parent"}
+      ${optionalString unshareUser "--unshare-user"}
+      ${optionalString unshareIpc "--unshare-ipc"}
+      ${optionalString unsharePid "--unshare-pid"}
+      ${optionalString unshareNet "--unshare-net"}
+      ${optionalString unshareUts "--unshare-uts"}
+      ${optionalString unshareCgroup "--unshare-cgroup"}
+      ${optionalString dieWithParent "--die-with-parent"}
       --ro-bind /nix /nix
-      ${lib.optionalString privateTmp "--tmpfs /tmp"}
+      ${optionalString privateTmp "--tmpfs /tmp"}
       # Our glibc will look for the cache in its own path in `/nix/store`.
       # As such, we need a cache to exist there, because pressure-vessel
       # depends on the existence of an ld cache. However, adding one
@@ -242,7 +252,7 @@ let
       --symlink /etc/ld.so.cache ${glibc}/etc/ld.so.cache \
       --ro-bind ${glibc}/etc/rpc ${glibc}/etc/rpc \
       --remount-ro ${glibc}/etc \
-  '' + lib.optionalString (stdenv.isx86_64 && stdenv.isLinux) (indentLines ''
+  '' + optionalString (stdenv.isx86_64 && stdenv.isLinux) (indentLines ''
       --tmpfs ${pkgsi686Linux.glibc}/etc \
       --symlink /etc/ld.so.conf ${pkgsi686Linux.glibc}/etc/ld.so.conf \
       --symlink /etc/ld.so.cache ${pkgsi686Linux.glibc}/etc/ld.so.cache \
diff --git a/pkgs/build-support/coq/default.nix b/pkgs/build-support/coq/default.nix
index eb045ddf6865a..6036d0f05dd53 100644
--- a/pkgs/build-support/coq/default.nix
+++ b/pkgs/build-support/coq/default.nix
@@ -1,10 +1,33 @@
 { lib, stdenv, coqPackages, coq, which, fetchzip }@args:
-let lib = import ./extra-lib.nix {inherit (args) lib;}; in
-with builtins; with lib;
+
 let
+  lib = import ./extra-lib.nix {
+    inherit (args) lib;
+  };
+
+  inherit (lib)
+    concatStringsSep
+    flip
+    foldl
+    isFunction
+    isString
+    optional
+    optionalAttrs
+    optionals
+    optionalString
+    pred
+    remove
+    switch
+    versions
+    ;
+
+  inherit (lib.attrsets) removeAttrs;
+  inherit (lib.strings) match;
+
   isGitHubDomain = d: match "^github.*" d != null;
   isGitLabDomain = d: match "^gitlab.*" d != null;
 in
+
 { pname,
   version ? null,
   fetcher ? null,
diff --git a/pkgs/build-support/coq/extra-lib.nix b/pkgs/build-support/coq/extra-lib.nix
index 3c226b4920b62..94de7c0113d84 100644
--- a/pkgs/build-support/coq/extra-lib.nix
+++ b/pkgs/build-support/coq/extra-lib.nix
@@ -1,5 +1,25 @@
 { lib }:
-with builtins; with lib; recursiveUpdate lib (rec {
+
+let
+  inherit (lib)
+    all
+    concatStringsSep
+    findFirst
+    flip
+    getAttr
+    head
+    isFunction
+    length
+    recursiveUpdate
+    splitVersion
+    tail
+    take
+    versionAtLeast
+    versionOlder
+    zipListsWith
+    ;
+in
+recursiveUpdate lib (rec {
 
   versions =
     let
diff --git a/pkgs/build-support/coq/meta-fetch/default.nix b/pkgs/build-support/coq/meta-fetch/default.nix
index 82c29fb760b78..daed9faa3efed 100644
--- a/pkgs/build-support/coq/meta-fetch/default.nix
+++ b/pkgs/build-support/coq/meta-fetch/default.nix
@@ -1,8 +1,33 @@
 { lib, stdenv, fetchzip }@args:
-let lib' = lib; in
-let lib = import ../extra-lib.nix {lib = lib';}; in
-with builtins; with lib;
+
 let
+  lib = import ../extra-lib.nix {
+    inherit (args) lib;
+  };
+
+  inherit (lib)
+    attrNames
+    fakeSha256
+    filter
+    findFirst
+    head
+    isAttrs
+    isPath
+    isString
+    last
+    length
+    optionalAttrs
+    pathExists
+    pred
+    sort
+    switch
+    switch-if
+    versionAtLeast
+    versions
+    ;
+
+  inherit (lib.strings) match split;
+
   default-fetcher = {domain ? "github.com", owner ? "", repo, rev, name ? "source", sha256 ? null, ...}@args:
     let ext = if args?sha256 then "zip" else "tar.gz";
         fmt = if args?sha256 then "zip" else "tarball";
@@ -17,7 +42,7 @@ let
           { cond = (match "(www.)?mpi-sws.org" domain) != null;
             out = "https://www.mpi-sws.org/~${owner}/${repo}/download/${repo}-${rev}.${ext}";}
         ] (throw "meta-fetch: no fetcher found for domain ${domain} on ${rev}");
-        fetch = x: if args?sha256 then fetchzip (x // { inherit sha256; }) else fetchTarball x;
+        fetch = x: if args?sha256 then fetchzip (x // { inherit sha256; }) else builtins.fetchTarball x;
     in fetch { inherit url ; };
 in
 {
@@ -38,11 +63,12 @@ switch arg [
   { case = isNull;       out = { version = "broken"; src = ""; broken = true; }; }
   { case = isPathString; out = { version = "dev"; src = arg; }; }
   { case = pred.union isVersion isShortVersion;
-    out = let v = if isVersion arg then arg else shortVersion arg; in
-      let
-        given-sha256 = release.${v}.sha256 or "";
-        sha256 = if given-sha256 == "" then lib.fakeSha256 else given-sha256;
-        rv = release.${v} // { inherit sha256; }; in
+    out = let
+      v = if isVersion arg then arg else shortVersion arg;
+      given-sha256 = release.${v}.sha256 or "";
+      sha256 = if given-sha256 == "" then fakeSha256 else given-sha256;
+      rv = release.${v} // { inherit sha256; };
+    in
       {
         version = rv.version or v;
         src = rv.src or fetcher (location // { rev = releaseRev v; } // rv);
diff --git a/pkgs/build-support/fetchrepoproject/default.nix b/pkgs/build-support/fetchrepoproject/default.nix
index 78b8caeb8091e..a5e79c8d4ef97 100644
--- a/pkgs/build-support/fetchrepoproject/default.nix
+++ b/pkgs/build-support/fetchrepoproject/default.nix
@@ -9,9 +9,14 @@
 assert repoRepoRev != "" -> repoRepoURL != "";
 assert createMirror -> !useArchive;
 
-with lib;
-
 let
+  inherit (lib)
+    concatMapStringsSep
+    concatStringsSep
+    fetchers
+    optionalString
+    ;
+
   extraRepoInitFlags = [
     (optionalString (repoRepoURL != "") "--repo-url=${repoRepoURL}")
     (optionalString (repoRepoRev != "") "--repo-branch=${repoRepoRev}")
diff --git a/pkgs/build-support/fetchsourcehut/default.nix b/pkgs/build-support/fetchsourcehut/default.nix
index ed6e85bd639b2..42d437b3555e6 100644
--- a/pkgs/build-support/fetchsourcehut/default.nix
+++ b/pkgs/build-support/fetchsourcehut/default.nix
@@ -1,6 +1,14 @@
 { fetchgit, fetchhg, fetchzip, lib }:
 
-lib.makeOverridable (
+let
+  inherit (lib)
+    assertOneOf
+    makeOverridable
+    optionalString
+    ;
+in
+
+makeOverridable (
 { owner
 , repo, rev
 , domain ? "sr.ht"
@@ -10,9 +18,7 @@ lib.makeOverridable (
 , ... # For hash agility
 } @ args:
 
-with lib;
-
-assert (lib.assertOneOf "vc" vc [ "hg" "git" ]);
+assert (assertOneOf "vc" vc [ "hg" "git" ]);
 
 let
   urlFor = resource: "https://${resource}.${domain}/${owner}/${repo}";
diff --git a/pkgs/build-support/nix-gitignore/default.nix b/pkgs/build-support/nix-gitignore/default.nix
index c047bfc7d9a28..849720675c892 100644
--- a/pkgs/build-support/nix-gitignore/default.nix
+++ b/pkgs/build-support/nix-gitignore/default.nix
@@ -7,9 +7,32 @@
 # - zero or more directories. For example, "a/**/b" matches "a/b",
 # - "a/x/b", "a/x/y/b" and so on.
 
-with builtins;
-
 let
+  inherit (builtins) filterSource;
+
+  inherit (lib)
+    concatStringsSep
+    elemAt
+    filter
+    head
+    isList
+    length
+    optionals
+    optionalString
+    pathExists
+    readFile
+    removePrefix
+    replaceStrings
+    stringLength
+    sub
+    substring
+    toList
+    trace
+    ;
+
+
+  inherit (lib.strings) match split typeOf;
+
   debug = a: trace a a;
   last = l: elemAt l ((length l) - 1);
 in rec {
@@ -17,7 +40,7 @@ in rec {
   filterPattern = patterns: root:
     (name: _type:
       let
-        relPath = lib.removePrefix ((toString root) + "/") name;
+        relPath = removePrefix ((toString root) + "/") name;
         matches = pair: (match (head pair) relPath) != null;
         matched = map (pair: [(matches pair) (last pair)]) patterns;
       in
@@ -45,7 +68,7 @@ in rec {
           escs = "\\*?";
           splitString =
             let recurse = str : [(substring 0 1 str)] ++
-                                 (lib.optionals (str != "") (recurse (substring 1 (stringLength(str)) str) ));
+                                 (optionals (str != "") (recurse (substring 1 (stringLength(str)) str) ));
             in str : recurse str;
           chars = s: filter (c: c != "" && !isList c) (splitString s);
           escape = s: map (c: "\\" + c) (chars s);
@@ -66,7 +89,7 @@ in rec {
       handleSlashPrefix = l:
         let
           split = (match "^(/?)(.*)" l);
-          findSlash = l: lib.optionalString ((match ".+/.+" l) == null) l;
+          findSlash = l: optionalString ((match ".+/.+" l) == null) l;
           hasSlash = mapAroundCharclass findSlash l != l;
         in
           (if (elemAt split 0) == "/" || hasSlash
@@ -94,12 +117,12 @@ in rec {
   gitignoreCompileIgnore = file_str_patterns: root:
     let
       onPath = f: a: if typeOf a == "path" then f a else a;
-      str_patterns = map (onPath readFile) (lib.toList file_str_patterns);
+      str_patterns = map (onPath readFile) (toList file_str_patterns);
     in concatStringsSep "\n" str_patterns;
 
-  gitignoreFilterPure = filter: patterns: root: name: type:
+  gitignoreFilterPure = predicate: patterns: root: name: type:
     gitignoreFilter (gitignoreCompileIgnore patterns root) root name type
-    && filter name type;
+    && predicate name type;
 
   # This is a very hacky way of programming this!
   # A better way would be to reuse existing filtering by making multiple gitignore functions per each root.
@@ -145,23 +168,23 @@ in rec {
       '');
 
   withGitignoreFile = patterns: root:
-    lib.toList patterns ++ [ ".git" ] ++ [(root + "/.gitignore")];
+    toList patterns ++ [ ".git" ] ++ [(root + "/.gitignore")];
 
   withRecursiveGitignoreFile = patterns: root:
-    lib.toList patterns ++ [ ".git" ] ++ [(compileRecursiveGitignore root)];
+    toList patterns ++ [ ".git" ] ++ [(compileRecursiveGitignore root)];
 
   # filterSource derivatives
 
-  gitignoreFilterSourcePure = filter: patterns: root:
-    filterSource (gitignoreFilterPure filter patterns root) root;
+  gitignoreFilterSourcePure = predicate: patterns: root:
+    filterSource (gitignoreFilterPure predicate patterns root) root;
 
-  gitignoreFilterSource = filter: patterns: root:
-    gitignoreFilterSourcePure filter (withGitignoreFile patterns root) root;
+  gitignoreFilterSource = predicate: patterns: root:
+    gitignoreFilterSourcePure predicate (withGitignoreFile patterns root) root;
 
-  gitignoreFilterRecursiveSource = filter: patterns: root:
-    gitignoreFilterSourcePure filter (withRecursiveGitignoreFile patterns root) root;
+  gitignoreFilterRecursiveSource = predicate: patterns: root:
+    gitignoreFilterSourcePure predicate (withRecursiveGitignoreFile patterns root) root;
 
-  # "Filter"-less alternatives
+  # "Predicate"-less alternatives
 
   gitignoreSourcePure = gitignoreFilterSourcePure (_: _: true);
   gitignoreSource = patterns: let type = typeOf patterns; in
diff --git a/pkgs/build-support/pkg-config-wrapper/default.nix b/pkgs/build-support/pkg-config-wrapper/default.nix
index f409ca3a7d4b6..c7856bd1f8765 100644
--- a/pkgs/build-support/pkg-config-wrapper/default.nix
+++ b/pkgs/build-support/pkg-config-wrapper/default.nix
@@ -10,9 +10,17 @@
 , extraPackages ? [], extraBuildCommands ? ""
 }:
 
-with lib;
-
 let
+  inherit (lib)
+    attrByPath
+    getBin
+    optional
+    optionalAttrs
+    optionals
+    optionalString
+    replaceStrings
+    ;
+
   stdenv = stdenvNoCC;
   inherit (stdenv) hostPlatform targetPlatform;
 
@@ -20,7 +28,7 @@ let
   #
   # TODO(@Ericson2314) Make unconditional, or optional but always true by
   # default.
-  targetPrefix = lib.optionalString (targetPlatform != hostPlatform)
+  targetPrefix = optionalString (targetPlatform != hostPlatform)
                                         (targetPlatform.config + "-");
 
   # See description in cc-wrapper.
@@ -49,7 +57,7 @@ stdenv.mkDerivation {
   dontUnpack = true;
 
   # Additional flags passed to pkg-config.
-  addFlags = lib.optional stdenv.targetPlatform.isStatic "--static";
+  addFlags = optional stdenv.targetPlatform.isStatic "--static";
 
   installPhase =
     ''
@@ -119,10 +127,10 @@ stdenv.mkDerivation {
   };
 
   meta =
-    let pkg-config_ = lib.optionalAttrs (pkg-config != null) pkg-config; in
-    (lib.optionalAttrs (pkg-config_ ? meta) (removeAttrs pkg-config.meta ["priority"])) //
+    let pkg-config_ = optionalAttrs (pkg-config != null) pkg-config; in
+    (optionalAttrs (pkg-config_ ? meta) (removeAttrs pkg-config.meta ["priority"])) //
     { description =
-        lib.attrByPath ["meta" "description"] "pkg-config" pkg-config_
+        attrByPath ["meta" "description"] "pkg-config" pkg-config_
         + " (wrapper script)";
       priority = 10;
   };
diff --git a/pkgs/build-support/release/default.nix b/pkgs/build-support/release/default.nix
index 1cc6a5812f1f5..f5ab2db1a7541 100644
--- a/pkgs/build-support/release/default.nix
+++ b/pkgs/build-support/release/default.nix
@@ -1,6 +1,24 @@
 { lib, pkgs }:
 
-with pkgs;
+let
+  inherit (lib) optionalString;
+
+  inherit (pkgs)
+    autoconf
+    automake
+    checkinstall
+    clang-analyzer
+    cov-build
+    enableGCOVInstrumentation
+    lcov
+    libtool
+    makeGCOVReport
+    runCommand
+    stdenv
+    vmTools
+    xz
+    ;
+in
 
 rec {
 
@@ -91,7 +109,7 @@ rec {
       dontConfigure = true;
       dontBuild = true;
 
-      patchPhase = lib.optionalString isNixOS ''
+      patchPhase = optionalString isNixOS ''
         touch .update-on-nixos-rebuild
       '';
 
diff --git a/pkgs/build-support/replace-dependency.nix b/pkgs/build-support/replace-dependency.nix
index 5b4c127cdd8e7..7912d21bfd692 100644
--- a/pkgs/build-support/replace-dependency.nix
+++ b/pkgs/build-support/replace-dependency.nix
@@ -19,9 +19,20 @@
 # (and all of its dependencies) without rebuilding further.
 { drv, oldDependency, newDependency, verbose ? true }:
 
-with lib;
-
 let
+  inherit (lib)
+    any
+    attrNames
+    concatStringsSep
+    elem
+    filter
+    filterAttrs
+    listToAttrs
+    mapAttrsToList
+    stringLength
+    substring
+    ;
+
   warn = if verbose then builtins.trace else (x: y: y);
   references = import (runCommandLocal "references.nix" { exportReferencesGraph = [ "graph" drv ]; } ''
     (echo {
@@ -54,7 +65,7 @@ let
     (drv: { name = discard (toString drv);
             value = elem oldStorepath (referencesOf drv) ||
                     any dependsOnOld (referencesOf drv);
-          }) (builtins.attrNames references));
+          }) (attrNames references));
 
   dependsOnOld = drv: dependsOnOldMemo.${discard (toString drv)};
 
@@ -74,9 +85,9 @@ let
   rewriteMemo = listToAttrs (map
     (drv: { name = discard (toString drv);
             value = rewriteHashes (builtins.storePath drv)
-              (filterAttrs (n: v: builtins.elem (builtins.storePath (discard (toString n))) (referencesOf drv)) rewriteMemo);
+              (filterAttrs (n: v: elem (builtins.storePath (discard (toString n))) (referencesOf drv)) rewriteMemo);
           })
-    (filter dependsOnOld (builtins.attrNames references))) // rewrittenDeps;
+    (filter dependsOnOld (attrNames references))) // rewrittenDeps;
 
   drvHash = discard (toString drv);
 in assert (stringLength (drvName (toString oldDependency)) == stringLength (drvName (toString newDependency)));
diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh
index 9f6366b3feaed..783ea45f8eeb1 100644
--- a/pkgs/build-support/setup-hooks/auto-patchelf.sh
+++ b/pkgs/build-support/setup-hooks/auto-patchelf.sh
@@ -88,22 +88,21 @@ autoPatchelf() {
         --extra-args "${patchelfFlagsArray[@]}"
 }
 
-# XXX: This should ultimately use fixupOutputHooks but we currently don't have
-# a way to enforce the order. If we have $runtimeDependencies set, the setup
-# hook of patchelf is going to ruin everything and strip out those additional
-# RPATHs.
-#
-# So what we do here is basically run in postFixup and emulate the same
-# behaviour as fixupOutputHooks because the setup hook for patchelf is run in
-# fixupOutput and the postFixup hook runs later.
-#
-# shellcheck disable=SC2016
-# (Expressions don't expand in single quotes, use double quotes for that.)
-postFixupHooks+=('
-    if [ -z "${dontAutoPatchelf-}" ]; then
+autoPatchelfPostFixup() {
+    # XXX: This should ultimately use fixupOutputHooks but we currently don't have
+    # a way to enforce the order. If we have $runtimeDependencies set, the setup
+    # hook of patchelf is going to ruin everything and strip out those additional
+    # RPATHs.
+    #
+    # So what we do here is basically run in postFixup and emulate the same
+    # behaviour as fixupOutputHooks because the setup hook for patchelf is run in
+    # fixupOutput and the postFixup hook runs later.
+    if [[ -z "${dontAutoPatchelf-}" ]]; then
         autoPatchelf -- $(for output in $(getAllOutputNames); do
             [ -e "${!output}" ] || continue
             echo "${!output}"
         done)
     fi
-')
+}
+
+postFixupHooks+=(autoPatchelfPostFixup)
diff --git a/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh b/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh
index 6cd01f6bf6307..3948342a36fc9 100644
--- a/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh
+++ b/pkgs/build-support/setup-hooks/make-binary-wrapper/make-binary-wrapper.sh
@@ -19,6 +19,7 @@ assertExecutable() {
 #                          (if unset or empty, defaults to EXECUTABLE)
 # --inherit-argv0        : the executable inherits argv0 from the wrapper.
 #                          (use instead of --argv0 '$0')
+# --resolve-argv0        : if argv0 doesn't include a / character, resolve it against PATH
 # --set          VAR VAL : add VAR with value VAL to the executable's environment
 # --set-default  VAR VAL : like --set, but only adds VAR if not already set in
 #                          the environment
@@ -87,6 +88,7 @@ makeDocumentedCWrapper() {
 makeCWrapper() {
     local argv0 inherit_argv0 n params cmd main flagsBefore flagsAfter flags executable length
     local uses_prefix uses_suffix uses_assert uses_assert_success uses_stdio uses_asprintf
+    local resolve_path
     executable=$(escapeStringLiteral "$1")
     params=("$@")
     length=${#params[*]}
@@ -169,6 +171,12 @@ makeCWrapper() {
                 # Whichever comes last of --argv0 and --inherit-argv0 wins
                 inherit_argv0=1
             ;;
+            --resolve-argv0)
+                # this gets processed after other argv0 flags
+                uses_stdio=1
+                uses_string=1
+                resolve_argv0=1
+            ;;
             *) # Using an error macro, we will make sure the compiler gives an understandable error message
                 main="$main#error makeCWrapper: Unknown argument ${p}"$'\n'
             ;;
@@ -176,6 +184,7 @@ makeCWrapper() {
     done
     [[ -z "$flagsBefore" && -z "$flagsAfter" ]] || main="$main"${main:+$'\n'}$(addFlags "$flagsBefore" "$flagsAfter")$'\n'$'\n'
     [ -z "$inherit_argv0" ] && main="${main}argv[0] = \"${argv0:-${executable}}\";"$'\n'
+    [ -z "$resolve_argv0" ] || main="${main}argv[0] = resolve_argv0(argv[0]);"$'\n'
     main="${main}return execv(\"${executable}\", argv);"$'\n'
 
     [ -z "$uses_asprintf" ] || printf '%s\n' "#define _GNU_SOURCE         /* See feature_test_macros(7) */"
@@ -183,9 +192,11 @@ makeCWrapper() {
     printf '%s\n' "#include <stdlib.h>"
     [ -z "$uses_assert" ]   || printf '%s\n' "#include <assert.h>"
     [ -z "$uses_stdio" ]    || printf '%s\n' "#include <stdio.h>"
+    [ -z "$uses_string" ]   || printf '%s\n' "#include <string.h>"
     [ -z "$uses_assert_success" ] || printf '\n%s\n' "#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)"
     [ -z "$uses_prefix" ] || printf '\n%s\n' "$(setEnvPrefixFn)"
     [ -z "$uses_suffix" ] || printf '\n%s\n' "$(setEnvSuffixFn)"
+    [ -z "$resolve_argv0" ] || printf '\n%s\n' "$(resolveArgv0Fn)"
     printf '\n%s' "int main(int argc, char **argv) {"
     printf '\n%s' "$(indent4 "$main")"
     printf '\n%s\n' "}"
@@ -338,6 +349,41 @@ void set_env_suffix(char *env, char *sep, char *suffix) {
 "
 }
 
+resolveArgv0Fn() {
+  printf '%s' "\
+char *resolve_argv0(char *argv0) {
+  if (strchr(argv0, '/') != NULL) {
+    return argv0;
+  }
+  char *path = getenv(\"PATH\");
+  if (path == NULL) {
+    return argv0;
+  }
+  char *path_copy = strdup(path);
+  if (path_copy == NULL) {
+    return argv0;
+  }
+  char *dir = strtok(path_copy, \":\");
+  while (dir != NULL) {
+    char *candidate = malloc(strlen(dir) + strlen(argv0) + 2);
+    if (candidate == NULL) {
+      free(path_copy);
+      return argv0;
+    }
+    sprintf(candidate, \"%s/%s\", dir, argv0);
+    if (access(candidate, X_OK) == 0) {
+      free(path_copy);
+      return candidate;
+    }
+    free(candidate);
+    dir = strtok(NULL, \":\");
+  }
+  free(path_copy);
+  return argv0;
+}
+"
+}
+
 # Embed a C string which shows up as readable text in the compiled binary wrapper,
 # giving instructions for recreating the wrapper.
 # Keep in sync with makeBinaryWrapper.extractCmd
diff --git a/pkgs/build-support/setup-hooks/make-wrapper.sh b/pkgs/build-support/setup-hooks/make-wrapper.sh
index 11b332bfc3eb2..cba158bd31ea9 100644
--- a/pkgs/build-support/setup-hooks/make-wrapper.sh
+++ b/pkgs/build-support/setup-hooks/make-wrapper.sh
@@ -15,6 +15,7 @@ assertExecutable() {
 #                          (if unset or empty, defaults to EXECUTABLE)
 # --inherit-argv0        : the executable inherits argv0 from the wrapper.
 #                          (use instead of --argv0 '$0')
+# --resolve-argv0        : if argv0 doesn't include a / character, resolve it against PATH
 # --set          VAR VAL : add VAR with value VAL to the executable's environment
 # --set-default  VAR VAL : like --set, but only adds VAR if not already set in
 #                          the environment
@@ -177,6 +178,9 @@ makeShellWrapper() {
         elif [[ "$p" == "--inherit-argv0" ]]; then
             # Whichever comes last of --argv0 and --inherit-argv0 wins
             argv0='$0'
+        elif [[ "$p" == "--resolve-argv0" ]]; then
+            # this is noop in shell wrappers, since bash will always resolve $0
+            resolve_argv0=1
         else
             die "makeWrapper doesn't understand the arg $p"
         fi
diff --git a/pkgs/build-support/vm/test.nix b/pkgs/build-support/vm/test.nix
index ae6a10dea3b9c..50dbfeb750be5 100644
--- a/pkgs/build-support/vm/test.nix
+++ b/pkgs/build-support/vm/test.nix
@@ -1,5 +1,21 @@
-with import ../../.. { };
-with vmTools;
+let
+  pkgs = import ../../.. { };
+
+  inherit (pkgs)
+    hello
+    patchelf
+    pcmanfm
+    stdenv
+    ;
+
+  inherit (pkgs.vmTools)
+    buildRPM
+    diskImages
+    makeImageTestScript
+    runInLinuxImage
+    runInLinuxVM
+    ;
+in
 
 {
 
diff --git a/pkgs/build-support/writers/scripts.nix b/pkgs/build-support/writers/scripts.nix
index 1dd25c500719b..06d763ca9d6af 100644
--- a/pkgs/build-support/writers/scripts.nix
+++ b/pkgs/build-support/writers/scripts.nix
@@ -1,4 +1,14 @@
-{ pkgs, buildPackages, lib, stdenv, libiconv, mkNugetDeps, mkNugetSource, gixy }:
+{
+  buildPackages,
+  gixy,
+  lib,
+  libiconv,
+  makeBinaryWrapper,
+  mkNugetDeps,
+  mkNugetSource,
+  pkgs,
+  stdenv,
+}:
 let
   inherit (lib)
     concatMapStringsSep
@@ -6,7 +16,6 @@ let
     escapeShellArg
     last
     optionalString
-    stringLength
     strings
     types
     ;
@@ -18,137 +27,285 @@ rec {
   # Examples:
   #   writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; }
   #   makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world"
-  makeScriptWriter = { interpreter, check ? "" }: nameOrPath: content:
+  makeScriptWriter = { interpreter, check ? "", makeWrapperArgs ? [], }: nameOrPath: content:
     assert (types.path.check nameOrPath) || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
     assert (types.path.check content) || (types.str.check content);
     let
+      nameIsPath = types.path.check nameOrPath;
       name = last (builtins.split "/" nameOrPath);
-    in
+      path = if nameIsPath then nameOrPath else "/bin/${name}";
+      # The inner derivation which creates the executable under $out/bin (never at $out directly)
+      # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper.
+      inner =
+        pkgs.runCommandLocal name (
+          {
+            inherit makeWrapperArgs;
+            nativeBuildInputs = [
+              makeBinaryWrapper
+            ];
+            meta.mainProgram = name;
+          }
+          // (
+            if (types.str.check content) then {
+              inherit content interpreter;
+              passAsFile = [ "content" ];
+            } else {
+              inherit interpreter;
+              contentPath = content;
+            }
+          )
+        )
+        ''
+          # On darwin a script cannot be used as an interpreter in a shebang but
+          # there doesn't seem to be a limit to the size of shebang and multiple
+          # arguments to the interpreter are allowed.
+          if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter
+          then
+            wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3)
+            # Get first word from the line (note: xargs echo remove leading spaces)
+            wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1)
+
+            if isScript $wrapperInterpreter
+            then
+              echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported."
+              exit 1
+            fi
+
+            # This should work as long as wrapperInterpreter is a shell, which is
+            # the case for programs wrapped with makeWrapper, like
+            # python3.withPackages etc.
+            interpreterLine="$wrapperInterpreterLine $interpreter"
+          else
+            interpreterLine=$interpreter
+          fi
+
+          echo "#! $interpreterLine" > $out
+          cat "$contentPath" >> $out
+          ${optionalString (check != "") ''
+            ${check} $out
+          ''}
+          chmod +x $out
+
+          # Relocate executable
+          # Wrap it if makeWrapperArgs are specified
+          mv $out tmp
+            mkdir -p $out/$(dirname "${path}")
+            mv tmp $out/${path}
+          if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then
+              wrapProgram $out/${path} ''${makeWrapperArgs[@]}
+          fi
+        '';
+      in
+        if nameIsPath
+        then inner
+        # In case nameOrPath is a name, the user intends the executable to be located at $out.
+        # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}.
+        # This breaks the override pattern.
+        # In case this turns out to be a problem, we can still add more magic
+        else pkgs.runCommandLocal name {} ''
+          ln -s ${inner}/bin/${name} $out
+        '';
 
-    pkgs.runCommandLocal name (
-      lib.optionalAttrs (nameOrPath == "/bin/${name}") {
-        meta.mainProgram = name;
-      }
-      // (
-        if (types.str.check content) then {
-          inherit content interpreter;
-          passAsFile = [ "content" ];
-        } else {
-          inherit interpreter;
-          contentPath = content;
-        }
-      )
-    )
-    ''
-      # On darwin a script cannot be used as an interpreter in a shebang but
-      # there doesn't seem to be a limit to the size of shebang and multiple
-      # arguments to the interpreter are allowed.
-      if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter
-      then
-        wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3)
-        # Get first word from the line (note: xargs echo remove leading spaces)
-        wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1)
-
-        if isScript $wrapperInterpreter
-        then
-          echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported."
-          exit 1
-        fi
-
-        # This should work as long as wrapperInterpreter is a shell, which is
-        # the case for programs wrapped with makeWrapper, like
-        # python3.withPackages etc.
-        interpreterLine="$wrapperInterpreterLine $interpreter"
-      else
-        interpreterLine=$interpreter
-      fi
-
-      echo "#! $interpreterLine" > $out
-      cat "$contentPath" >> $out
-      ${optionalString (check != "") ''
-        ${check} $out
-      ''}
-      chmod +x $out
-      ${optionalString (types.path.check nameOrPath) ''
-        mv $out tmp
-        mkdir -p $out/$(dirname "${nameOrPath}")
-        mv tmp $out/${nameOrPath}
-      ''}
-    '';
 
   # Base implementation for compiled executables.
   # Takes a compile script, which in turn takes the name as an argument.
   #
   # Examples:
   #   writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; }
-  makeBinWriter = { compileScript, strip ? true }: nameOrPath: content:
+  makeBinWriter = { compileScript, strip ? true, makeWrapperArgs ? [] }: nameOrPath: content:
     assert (types.path.check nameOrPath) || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
     assert (types.path.check content) || (types.str.check content);
     let
+      nameIsPath = types.path.check nameOrPath;
       name = last (builtins.split "/" nameOrPath);
+      path = if nameIsPath then nameOrPath else "/bin/${name}";
+      # The inner derivation which creates the executable under $out/bin (never at $out directly)
+      # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper.
+      inner =
+        pkgs.runCommandLocal name (
+          {
+            inherit makeWrapperArgs;
+            nativeBuildInputs = [
+              makeBinaryWrapper
+            ];
+            meta.mainProgram = name;
+          }
+          // (
+            if (types.str.check content) then {
+            inherit content;
+            passAsFile = [ "content" ];
+          } else {
+            contentPath = content;
+                  }
+          )
+        )
+        ''
+          ${compileScript}
+          ${lib.optionalString strip
+              "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"}
+          # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid
+          # mach-o executables from the get-go, but need to be corrected somehow
+          # which is done by fixupPhase.
+          ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"}
+          mv $out tmp
+          mkdir -p $out/$(dirname "${path}")
+          mv tmp $out/${path}
+          if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then
+            wrapProgram $out/${path} ''${makeWrapperArgs[@]}
+          fi
+        '';
     in
-    pkgs.runCommand name ((if (types.str.check content) then {
-      inherit content;
-      passAsFile = [ "content" ];
-    } else {
-      contentPath = content;
-    }) // lib.optionalAttrs (nameOrPath == "/bin/${name}") {
-      meta.mainProgram = name;
-    }) ''
-      ${compileScript}
-      ${lib.optionalString strip
-          "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"}
-      # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid
-      # mach-o executables from the get-go, but need to be corrected somehow
-      # which is done by fixupPhase.
-      ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"}
-      ${optionalString (types.path.check nameOrPath) ''
-        mv $out tmp
-        mkdir -p $out/$(dirname "${nameOrPath}")
-        mv tmp $out/${nameOrPath}
-      ''}
-    '';
+      if nameIsPath
+      then inner
+      # In case nameOrPath is a name, the user intends the executable to be located at $out.
+      # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}.
+      # This breaks the override pattern.
+      # In case this turns out to be a problem, we can still add more magic
+      else pkgs.runCommandLocal name {} ''
+        ln -s ${inner}/bin/${name} $out
+      '';
 
   # Like writeScript but the first line is a shebang to bash
   #
-  # Example:
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
   #   writeBash "example" ''
   #     echo hello world
   #   ''
-  writeBash = makeScriptWriter {
-    interpreter = "${lib.getExe pkgs.bash}";
-  };
+  #
+  # Example with arguments:
+  #   writeBash "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix" "PATH" ":" "${pkgs.hello}/bin"
+  #       ];
+  #     }
+  #     ''
+  #       hello
+  #     ''
+  writeBash = name: argsOrScript:
+    if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript
+    then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.bash}"; }) name
+    else makeScriptWriter { interpreter = "${lib.getExe pkgs.bash}"; } name argsOrScript;
 
   # Like writeScriptBin but the first line is a shebang to bash
+  #
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
+  #   writeBashBin "example" ''
+  #     echo hello world
+  #   ''
+  #
+  # Example with arguments:
+  #  writeBashBin "example"
+  #    {
+  #      makeWrapperArgs = [
+  #        "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #      ];
+  #    }
+  #    ''
+  #      hello
+  #    ''
   writeBashBin = name:
     writeBash "/bin/${name}";
 
   # Like writeScript but the first line is a shebang to dash
   #
-  # Example:
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
   #   writeDash "example" ''
   #     echo hello world
   #   ''
-  writeDash = makeScriptWriter {
-    interpreter = "${lib.getExe pkgs.dash}";
-  };
+  #
+  # Example with arguments:
+  #   writeDash "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #       ];
+  #     }
+  #     ''
+  #       hello
+  #     ''
+  writeDash = name: argsOrScript:
+    if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript
+    then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.dash}"; }) name
+    else makeScriptWriter { interpreter = "${lib.getExe pkgs.dash}"; } name argsOrScript;
 
   # Like writeScriptBin but the first line is a shebang to dash
+  #
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
+  #   writeDashBin "example" ''
+  #     echo hello world
+  #   ''
+  #
+  # Example with arguments:
+  #  writeDashBin "example"
+  #    {
+  #      makeWrapperArgs = [
+  #        "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #      ];
+  #    }
+  #    ''
+  #      hello
+  #    ''
   writeDashBin = name:
     writeDash "/bin/${name}";
 
   # Like writeScript but the first line is a shebang to fish
   #
-  # Example:
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
   #   writeFish "example" ''
   #     echo hello world
   #   ''
-  writeFish = makeScriptWriter {
-    interpreter = "${lib.getExe pkgs.fish} --no-config";
-    check = "${lib.getExe pkgs.fish} --no-config --no-execute";  # syntax check only
-  };
+  #
+  # Example with arguments:
+  #   writeFish "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #       ];
+  #     }
+  #     ''
+  #       hello
+  #     ''
+  writeFish = name: argsOrScript:
+    if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript
+    then makeScriptWriter (argsOrScript // {
+      interpreter = "${lib.getExe pkgs.fish} --no-config";
+      check = "${lib.getExe pkgs.fish} --no-config --no-execute";  # syntax check only
+    }) name
+    else makeScriptWriter {
+      interpreter = "${lib.getExe pkgs.fish} --no-config";
+      check = "${lib.getExe pkgs.fish} --no-config --no-execute";  # syntax check only
+    } name argsOrScript;
 
   # Like writeScriptBin but the first line is a shebang to fish
+  #
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
+  #   writeFishBin "example" ''
+  #     echo hello world
+  #   ''
+  #
+  # Example with arguments:
+  #   writeFishBin "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #       ];
+  #     }
+  #     ''
+  #       hello
+  #     ''
   writeFishBin = name:
     writeFish "/bin/${name}";
 
@@ -162,11 +319,12 @@ rec {
   #     main = launchMissiles
   #   '';
   writeHaskell = name: {
-    libraries ? [],
     ghc ? pkgs.ghc,
     ghcArgs ? [],
+    libraries ? [],
+    makeWrapperArgs ? [],
+    strip ? true,
     threadedRuntime ? true,
-    strip ? true
   }:
     let
       appendIfNotSet = el: list: if elem el list then list else list ++ [ el ];
@@ -178,7 +336,7 @@ rec {
         ${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs
         mv tmp $out
       '';
-      inherit strip;
+      inherit makeWrapperArgs strip;
     } name;
 
   # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin)
@@ -187,36 +345,72 @@ rec {
 
   # Like writeScript but the first line is a shebang to nu
   #
-  # Example:
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
   #   writeNu "example" ''
   #     echo hello world
   #   ''
-  writeNu = makeScriptWriter {
-    interpreter = "${lib.getExe pkgs.nushell} --no-config-file";
-  };
+  #
+  # Example with arguments:
+  #   writeNu "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #       ];
+  #     }
+  #     ''
+  #       hello
+  #     ''
+  writeNu = name: argsOrScript:
+    if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript
+    then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; }) name
+    else makeScriptWriter { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; } name argsOrScript;
+
 
   # Like writeScriptBin but the first line is a shebang to nu
+  #
+  # Can be called with or without extra arguments.
+  #
+  # Example without arguments:
+  #   writeNuBin "example" ''
+  #     echo hello world
+  #   ''
+  #
+  # Example with arguments:
+  #   writeNuBin "example"
+  #     {
+  #       makeWrapperArgs = [
+  #         "--prefix", "PATH", ":", "${pkgs.hello}/bin",
+  #       ];
+  #     }
+  #    ''
+  #      hello
+  #    ''
   writeNuBin = name:
     writeNu "/bin/${name}";
 
   # makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer,
   # If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used.
-  makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], }:
-  makeScriptWriter {
-    interpreter =
-      if libraries == []
-      then "${ruby}/bin/ruby"
-      else "${(ruby.withPackages (ps: libraries))}/bin/ruby";
-    # Rubocop doesnt seem to like running in this fashion.
-    #check = (writeDash "rubocop.sh" ''
-    #  exec ${lib.getExe buildRubyPackages.rubocop} "$1"
-    #'');
-  } name;
+  makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], ... } @ args:
+  makeScriptWriter (
+    (builtins.removeAttrs args ["libraries"])
+    // {
+      interpreter =
+        if libraries == []
+        then "${ruby}/bin/ruby"
+        else "${(ruby.withPackages (ps: libraries))}/bin/ruby";
+      # Rubocop doesn't seem to like running in this fashion.
+      #check = (writeDash "rubocop.sh" ''
+      #  exec ${lib.getExe buildRubyPackages.rubocop} "$1"
+      #'');
+    }
+  ) name;
 
   # Like writeScript but the first line is a shebang to ruby
   #
   # Example:
-  #   writeRuby "example" ''
+  #   writeRuby "example" { libraries = [ pkgs.rubyPackages.git ]; } ''
   #    puts "hello world"
   #   ''
   writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages;
@@ -227,17 +421,20 @@ rec {
   # makeLuaWriter takes lua and compatible luaPackages and produces lua script writer,
   # which validates the script with luacheck at build time. If any libraries are specified,
   # lua.withPackages is used as interpreter, otherwise the "bare" lua is used.
-  makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], }:
-  makeScriptWriter {
-    interpreter = lua.interpreter;
-      # if libraries == []
-      # then lua.interpreter
-      # else (lua.withPackages (ps: libraries)).interpreter
-      # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this.
-    check = (writeDash "luacheck.sh" ''
-      exec ${buildLuaPackages.luacheck}/bin/luacheck "$1"
-    '');
-  } name;
+  makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], ... } @ args:
+  makeScriptWriter (
+    (builtins.removeAttrs args ["libraries"])
+    // {
+      interpreter = lua.interpreter;
+        # if libraries == []
+        # then lua.interpreter
+        # else (lua.withPackages (ps: libraries)).interpreter
+        # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this.
+      check = (writeDash "luacheck.sh" ''
+        exec ${buildLuaPackages.luacheck}/bin/luacheck "$1"
+      '');
+    }
+   ) name;
 
   # writeLua takes a name an attributeset with libraries and some lua source code and
   # returns an executable (should also work with luajit)
@@ -265,9 +462,10 @@ rec {
     writeLua "/bin/${name}";
 
   writeRust = name: {
-      rustc ? pkgs.rustc,
-      rustcArgs ? [],
-      strip ? true
+    makeWrapperArgs ? [],
+    rustc ? pkgs.rustc,
+    rustcArgs ? [],
+    strip ? true,
   }:
   let
     darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ];
@@ -277,7 +475,7 @@ rec {
         cp "$contentPath" tmp.rs
         PATH=${lib.makeBinPath [pkgs.gcc]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs
       '';
-      inherit strip;
+      inherit makeWrapperArgs strip;
     } name;
 
   writeRustBin = name:
@@ -337,10 +535,13 @@ rec {
   #     use boolean;
   #     print "Howdy!\n" if true;
   #   ''
-  writePerl = name: { libraries ? [] }:
-    makeScriptWriter {
-      interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}";
-    } name;
+  writePerl = name: { libraries ? [], ... } @ args:
+    makeScriptWriter (
+      (builtins.removeAttrs args ["libraries"])
+      // {
+        interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}";
+      }
+    ) name;
 
   # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin)
   writePerlBin = name:
@@ -349,22 +550,27 @@ rec {
   # makePythonWriter takes python and compatible pythonPackages and produces python script writer,
   # which validates the script with flake8 at build time. If any libraries are specified,
   # python.withPackages is used as interpreter, otherwise the "bare" python is used.
-  makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [] }:
+  makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [], ... } @ args:
   let
     ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}";
   in
-  makeScriptWriter {
-    interpreter =
-      if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then
-        if libraries == []
-        then python.interpreter
-        else (python.withPackages (ps: libraries)).interpreter
-      else python.interpreter
-    ;
-    check = optionalString python.isPy3k (writeDash "pythoncheck.sh" ''
-      exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1"
-    '');
-  } name;
+  makeScriptWriter
+    (
+      (builtins.removeAttrs args ["libraries" "flakeIgnore"])
+      // {
+        interpreter =
+          if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then
+            if libraries == []
+            then python.interpreter
+            else (python.withPackages (ps: libraries)).interpreter
+          else python.interpreter
+        ;
+        check = optionalString python.isPy3k (writeDash "pythoncheck.sh" ''
+          exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1"
+        '');
+      }
+    )
+    name;
 
   # writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and
   # returns an executable
@@ -421,7 +627,7 @@ rec {
     writePyPy3 "/bin/${name}";
 
 
-  makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [] }: nameOrPath:
+  makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [], ... } @ args: nameOrPath:
   let
     fname = last (builtins.split "/" nameOrPath);
     path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx";
@@ -442,9 +648,12 @@ rec {
       ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script"
     '';
 
-  in content: makeScriptWriter {
-    interpreter = fsi;
-  } path
+  in content: makeScriptWriter (
+    (builtins.removeAttrs args ["dotnet-sdk" "fsi-flags" "libraries"])
+    // {
+      interpreter = fsi;
+    }
+  ) path
   ''
     #i "nuget: ${nuget-source}/lib"
     ${ content }
@@ -456,5 +665,4 @@ rec {
 
   writeFSharpBin = name:
     writeFSharp "/bin/${name}";
-
 }
diff --git a/pkgs/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix
index 982c550d28e08..656d127930fad 100644
--- a/pkgs/build-support/writers/test.nix
+++ b/pkgs/build-support/writers/test.nix
@@ -1,13 +1,8 @@
-{ glib
-, haskellPackages
+{ haskellPackages
 , lib
 , nodePackages
 , perlPackages
-, pypy2Packages
 , python3Packages
-, pypy3Packages
-, luaPackages
-, rubyPackages
 , runCommand
 , testers
 , writers
@@ -16,8 +11,38 @@
 
 # If you are reading this, you can test these writers by running: nix-build . -A tests.writers
 
-with writers;
 let
+  inherit (lib) getExe recurseIntoAttrs;
+
+  inherit (writers)
+    makeFSharpWriter
+    writeBash
+    writeBashBin
+    writeDash
+    writeDashBin
+    writeFish
+    writeFishBin
+    writeFSharp
+    writeHaskell
+    writeHaskellBin
+    writeJS
+    writeJSBin
+    writeJSON
+    writeLua
+    writeNu
+    writePerl
+    writePerlBin
+    writePyPy3
+    writePython3
+    writePython3Bin
+    writeRuby
+    writeRust
+    writeRustBin
+    writeText
+    writeTOML
+    writeYAML
+    ;
+
   expectSuccess = test:
     runCommand "run-${test.name}" {} ''
       if [[ "$(${test})" != success ]]; then
@@ -30,7 +55,7 @@ let
 
   expectSuccessBin = test:
     runCommand "run-${test.name}" {} ''
-      if [[ "$(${lib.getExe test})" != success ]]; then
+      if [[ "$(${getExe test})" != success ]]; then
         echo 'test ${test.name} failed'
         exit 1
       fi
@@ -44,8 +69,8 @@ let
     in
     testers.testEqualContents { expected = expectedFile; actual = file; assertion = "${file.name} matches"; };
 in
-lib.recurseIntoAttrs {
-  bin = lib.recurseIntoAttrs {
+recurseIntoAttrs {
+  bin = recurseIntoAttrs {
     bash = expectSuccessBin (writeBashBin "test-writers-bash-bin" ''
      if [[ "test" == "test" ]]; then echo "success"; fi
     '');
@@ -145,7 +170,7 @@ lib.recurseIntoAttrs {
     #'');
   };
 
-  simple = lib.recurseIntoAttrs {
+  simple = recurseIntoAttrs {
     bash = expectSuccess (writeBash "test-writers-bash" ''
      if [[ "test" == "test" ]]; then echo "success"; fi
     '');
@@ -270,7 +295,7 @@ lib.recurseIntoAttrs {
     '');
   };
 
-  path = lib.recurseIntoAttrs {
+  path = recurseIntoAttrs {
     bash = expectSuccess (writeBash "test-writers-bash-path" (writeText "test" ''
       if [[ "test" == "test" ]]; then echo "success"; fi
     ''));
@@ -310,4 +335,85 @@ lib.recurseIntoAttrs {
       expected = "hello: world\n";
     };
   };
+
+  wrapping = recurseIntoAttrs {
+    bash-bin = expectSuccessBin (
+      writeBashBin "test-writers-wrapping-bash-bin"
+        {
+          makeWrapperArgs = [
+            "--set"
+            "ThaigerSprint"
+            "Thailand"
+          ];
+        }
+        ''
+          if [[ "$ThaigerSprint" == "Thailand" ]]; then
+            echo "success"
+          fi
+        ''
+    );
+
+    bash = expectSuccess (
+      writeBash "test-writers-wrapping-bash"
+        {
+          makeWrapperArgs = [
+            "--set"
+            "ThaigerSprint"
+            "Thailand"
+          ];
+        }
+        ''
+          if [[ "$ThaigerSprint" == "Thailand" ]]; then
+            echo "success"
+          fi
+        ''
+    );
+
+    python = expectSuccess (
+      writePython3 "test-writers-wrapping-python"
+        {
+          makeWrapperArgs = [
+            "--set"
+            "ThaigerSprint"
+            "Thailand"
+          ];
+        }
+        ''
+          import os
+
+          if os.environ.get("ThaigerSprint") == "Thailand":
+              print("success")
+        ''
+    );
+
+    rust = expectSuccess (
+      writeRust "test-writers-wrapping-rust"
+        {
+          makeWrapperArgs = [
+            "--set"
+            "ThaigerSprint"
+            "Thailand"
+          ];
+        }
+        ''
+          fn main(){
+            if std::env::var("ThaigerSprint").unwrap() == "Thailand" {
+              println!("success")
+            }
+          }
+        ''
+    );
+
+    no-empty-wrapper = let
+      bin = writeBashBin "bin" { makeWrapperArgs = []; } ''true'';
+    in runCommand "run-test-writers-wrapping-no-empty-wrapper" {} ''
+      ls -A ${bin}/bin
+      if [ $(ls -A ${bin}/bin | wc -l) -eq 1 ]; then
+        touch $out
+      else
+        echo "Error: Empty wrapper was created" >&2
+        exit 1
+      fi
+    '';
+  };
 }