about summary refs log tree commit diff
path: root/nixpkgs/pkgs/build-support
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/build-support')
-rw-r--r--nixpkgs/pkgs/build-support/bintools-wrapper/default.nix11
-rw-r--r--nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/default.nix6
-rw-r--r--nixpkgs/pkgs/build-support/build-maven.nix88
-rw-r--r--nixpkgs/pkgs/build-support/deterministic-uname/default.nix22
-rw-r--r--nixpkgs/pkgs/build-support/deterministic-uname/deterministic-uname.sh2
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix40
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix75
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh76
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh62
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh98
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh81
-rw-r--r--nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh80
-rw-r--r--nixpkgs/pkgs/build-support/emacs/melpa.nix32
-rw-r--r--nixpkgs/pkgs/build-support/flutter/default.nix47
-rw-r--r--nixpkgs/pkgs/build-support/libredirect/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/make-pkgconfigitem/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/node/fetch-yarn-deps/default.nix8
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/default.nix1
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/build-composer-project.nix7
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/build-composer-repository.nix5
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/build-composer-with-plugin.nix161
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-install-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-repository-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-with-plugin-vendor-hook.sh93
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/hooks/default.nix15
-rw-r--r--nixpkgs/pkgs/build-support/php/builders/v1/hooks/php-script-utils.bash15
-rw-r--r--nixpkgs/pkgs/build-support/php/pkgs/composer-local-repo-plugin.nix116
-rw-r--r--nixpkgs/pkgs/build-support/php/pkgs/composer-phar.nix2
-rw-r--r--nixpkgs/pkgs/build-support/rust/build-rust-crate/build-crate.nix2
-rw-r--r--nixpkgs/pkgs/build-support/rust/build-rust-crate/configure-crate.nix2
-rw-r--r--nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix4
-rw-r--r--nixpkgs/pkgs/build-support/rust/build-rust-crate/install-crate.nix2
-rw-r--r--nixpkgs/pkgs/build-support/rust/build-rust-crate/test/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/rust/hooks/cargo-build-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/rust/hooks/cargo-check-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/rust/hooks/cargo-nextest-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/rust/hooks/maturin-build-hook.sh2
-rw-r--r--nixpkgs/pkgs/build-support/setup-hooks/strip.sh16
-rw-r--r--nixpkgs/pkgs/build-support/singularity-tools/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/src-only/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/testers/default.nix4
-rw-r--r--nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tester.nix6
-rw-r--r--nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tests.nix7
-rw-r--r--nixpkgs/pkgs/build-support/testers/lychee.nix69
-rw-r--r--nixpkgs/pkgs/build-support/testers/test/default.nix2
-rw-r--r--nixpkgs/pkgs/build-support/trivial-builders/test/references/default.nix23
-rw-r--r--nixpkgs/pkgs/build-support/writers/scripts.nix2
48 files changed, 826 insertions, 480 deletions
diff --git a/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix b/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix
index 5ca5bc3f5eb3..2d75330f1c9e 100644
--- a/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix
+++ b/nixpkgs/pkgs/build-support/bintools-wrapper/default.nix
@@ -10,15 +10,15 @@
 , stdenvNoCC
 , runtimeShell
 , bintools ? null, libc ? null, coreutils ? null, gnugrep ? null
-, netbsd ? null, netbsdCross ? null
+, netbsd ? null
 , sharedLibraryLoader ?
   if libc == null then
     null
   else if stdenvNoCC.targetPlatform.isNetBSD then
-    if !(targetPackages ? netbsdCross) then
+    if !(targetPackages ? netbsd) then
       netbsd.ld_elf_so
-    else if libc != targetPackages.netbsdCross.headers then
-      targetPackages.netbsdCross.ld_elf_so
+    else if libc != targetPackages.netbsd.headers then
+      targetPackages.netbsd.ld_elf_so
     else
       null
   else
@@ -60,6 +60,7 @@
 , postLinkSignHook ? null, signingUtils ? null
 }:
 
+assert propagateDoc -> bintools ? man;
 assert nativeTools -> !propagateDoc && nativePrefix != "";
 assert !nativeTools -> bintools != null && coreutils != null && gnugrep != null;
 assert !(nativeLibc && noLibc);
@@ -128,7 +129,7 @@ let
     else if targetPlatform.isRiscV                    then "${sharedLibraryLoader}/lib/ld-linux-riscv*.so.1"
     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 targetPlatform.isFreeBSD                  then "${sharedLibraryLoader}/libexec/ld-elf.so.1"
     else if hasSuffix "pc-gnu" targetPlatform.config then "ld.so.1"
     else "";
 
diff --git a/nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/default.nix b/nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
index 12f3e7d58552..398a99e80e8c 100644
--- a/nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
+++ b/nixpkgs/pkgs/build-support/build-fhsenv-bubblewrap/default.nix
@@ -196,8 +196,10 @@ let
     x11_args+=(--tmpfs /tmp/.X11-unix)
 
     # Try to guess X socket path. This doesn't cover _everything_, but it covers some things.
-    if [[ "$DISPLAY" == :* ]]; then
-      display_nr=''${DISPLAY#?}
+    if [[ "$DISPLAY" == *:* ]]; then
+      # recover display number from $DISPLAY formatted [host]:num[.screen]
+      display_nr=''${DISPLAY/#*:} # strip host
+      display_nr=''${display_nr/%.*} # strip screen
       local_socket=/tmp/.X11-unix/X$display_nr
       x11_args+=(--ro-bind-try "$local_socket" "$local_socket")
     fi
diff --git a/nixpkgs/pkgs/build-support/build-maven.nix b/nixpkgs/pkgs/build-support/build-maven.nix
deleted file mode 100644
index 7ac8afdde225..000000000000
--- a/nixpkgs/pkgs/build-support/build-maven.nix
+++ /dev/null
@@ -1,88 +0,0 @@
-{ stdenv, maven, runCommand, writeText, fetchurl, lib, requireFile, linkFarm }:
-# Takes an info file generated by mvn2nix
-# (https://github.com/NixOS/mvn2nix-maven-plugin) and builds the maven
-# project with it.
-#
-# repo: A local maven repository with the project's dependencies.
-#
-# settings: A settings.xml to pass to maven to use the repo.
-#
-# build: A simple build derivation that uses mvn compile and package to build
-#        the project.
-#
-# @example
-#     project = pkgs.buildMaven ./project-info.json
-infoFile:
-let
-  info = lib.importJSON infoFile;
-
-  dependencies = lib.flatten (map (dep:
-    let
-      inherit (dep) sha1 groupId artifactId version metadata repository-id;
-      versionDir = dep.unresolved-version or version;
-      authenticated = dep.authenticated or false;
-      url = dep.url or "";
-
-      fetch = if (url != "") then
-        ((if authenticated then requireFile else fetchurl) {
-          inherit url sha1;
-        })
-      else
-        "";
-
-      fetchMetadata = (if authenticated then requireFile else fetchurl) {
-        inherit (metadata) url sha1;
-      };
-
-      layout = "${
-          builtins.replaceStrings [ "." ] [ "/" ] groupId
-        }/${artifactId}/${versionDir}";
-    in lib.optional (url != "") {
-      layout = "${layout}/${fetch.name}";
-      drv = fetch;
-    } ++ lib.optionals (dep ? metadata) ([{
-      layout = "${layout}/maven-metadata-${repository-id}.xml";
-      drv = fetchMetadata;
-    }] ++ lib.optional (fetch != "") {
-      layout = "${layout}/${
-          builtins.replaceStrings [ version ] [ dep.unresolved-version ]
-          fetch.name
-        }";
-      drv = fetch;
-    })) info.dependencies);
-
-  repo = linkFarm "maven-repository" (lib.forEach dependencies (dependency: {
-    name = dependency.layout;
-    path = dependency.drv;
-  }));
-
-  settings = writeText "settings.xml" ''
-    <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
-      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-      xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
-                          http://maven.apache.org/xsd/settings-1.0.0.xsd">
-      <localRepository>${repo}</localRepository>
-    </settings>
-  '';
-
-  src = dirOf infoFile;
-in {
-  inherit repo settings info;
-
-  build = stdenv.mkDerivation {
-    name = "${info.project.artifactId}-${info.project.version}.jar";
-
-    src = builtins.filterSource (path: type:
-      (toString path) != (toString (src + "/target")) && (toString path)
-      != (toString (src + "/.git"))) src;
-
-    buildInputs = [ maven ];
-
-    buildPhase = "mvn --offline --settings ${settings} compile";
-
-    installPhase = ''
-      mvn --offline --settings ${settings} package
-      mv target/*.jar $out
-    '';
-  };
-}
diff --git a/nixpkgs/pkgs/build-support/deterministic-uname/default.nix b/nixpkgs/pkgs/build-support/deterministic-uname/default.nix
index 6d150557aa9d..9efaa2558bfb 100644
--- a/nixpkgs/pkgs/build-support/deterministic-uname/default.nix
+++ b/nixpkgs/pkgs/build-support/deterministic-uname/default.nix
@@ -5,6 +5,7 @@
 , coreutils
 , getopt
 , modDirVersion ? ""
+, forPlatform ? stdenv.buildPlatform
 }:
 
 substituteAll {
@@ -17,8 +18,8 @@ substituteAll {
 
   inherit coreutils getopt;
 
-  uSystem = if stdenv.buildPlatform.uname.system != null then stdenv.buildPlatform.uname.system else "unknown";
-  inherit (stdenv.buildPlatform.uname) processor;
+  uSystem = if forPlatform.uname.system != null then forPlatform.uname.system else "unknown";
+  inherit (forPlatform.uname) processor;
 
   # uname -o
   # maybe add to lib/systems/default.nix uname attrset
@@ -26,10 +27,12 @@ substituteAll {
   # https://stackoverflow.com/questions/61711186/where-does-host-operating-system-in-uname-c-comes-from
   # https://github.com/coreutils/gnulib/blob/master/m4/host-os.m4
   operatingSystem =
-    if stdenv.buildPlatform.isLinux
+    if forPlatform.isLinux
     then "GNU/Linux"
-    else if stdenv.buildPlatform.isDarwin
+    else if forPlatform.isDarwin
     then "Darwin" # darwin isn't in host-os.m4 so where does this come from?
+    else if stdenv.buildPlatform.isFreeBSD
+    then "FreeBSD"
     else "unknown";
 
   # in os-specific/linux module packages
@@ -42,11 +45,12 @@ substituteAll {
     mainProgram = "uname";
     longDescription = ''
       This package provides a replacement for `uname` whose output depends only
-      on `stdenv.buildPlatform`.  It is meant to be used from within derivations.
-      Many packages' build processes run `uname` at compile time and embed its
-      output into the result of the build.  Since `uname` calls into the kernel,
-      and the Nix sandbox currently does not intercept these calls, builds made
-      on different kernels will produce different results.
+      on `stdenv.buildPlatform`, or a configurable `forPlatform`.  It is meant
+      to be used from within derivations. Many packages' build processes run
+      `uname` at compile time and embed its output into the result of the build.
+      Since `uname` calls into the kernel, and the Nix sandbox currently does
+      not intercept these calls, builds made on different kernels will produce
+      different results.
     '';
     license = [ licenses.mit ];
     maintainers = with maintainers; [ artturin ];
diff --git a/nixpkgs/pkgs/build-support/deterministic-uname/deterministic-uname.sh b/nixpkgs/pkgs/build-support/deterministic-uname/deterministic-uname.sh
index 31772aeee3cc..cb6f419b0331 100644
--- a/nixpkgs/pkgs/build-support/deterministic-uname/deterministic-uname.sh
+++ b/nixpkgs/pkgs/build-support/deterministic-uname/deterministic-uname.sh
@@ -131,6 +131,8 @@ fi
 #  Darwin *nodename* 22.1.0 Darwin Kernel Version 22.1.0: Sun Oct  9 20:14:30 PDT 2022; root:xnu-8792.41.9~2/RELEASE_ARM64_T8103 arm64 arm Darwin
 # NixOS:
 #  Linux *nodename* 6.0.13 #1-NixOS SMP PREEMPT_DYNAMIC Wed Dec 14 10:41:06 UTC 2022 x86_64 GNU/Linux
+# FreeBSD:
+#  FreeBSD *nodename* 14.0-RELEASE FreeBSD 14.0-RELEASE #0 releng/14.0-n265380-f9716eee8ab4: Fri Nov 10 05:57:23 UTC 2023     root@releng1.nyi.freebsd.org:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64 AMD Ryzen 9 5950X 16-Core Processor             FreeBSD
 output=()
 if [[ "$all" = "1" ]]; then
     output+=("$KERNEL_NAME_VAL" "$NODENAME_VAL" "$KERNEL_RELEASE_VAL" "$KERNEL_VERSION_VAL" "$MACHINE_VAL")
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix
index 16cf029ca345..7ae9cfc9f661 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-global-tool/default.nix
@@ -28,7 +28,7 @@ buildDotnetModule (args // {
     ] ++ (nugetDeps fetchNuGet);
   };
 
-  projectFile = "";
+  dotnetGlobalTool = true;
 
   useDotnetFromEnv = true;
 
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
index 4548616c7d80..7b88b16064bc 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/default.nix
@@ -69,7 +69,7 @@
 , disabledTests ? [ ]
   # The project file to run unit tests against. This is usually referenced in the regular project file, but sometimes it needs to be manually set.
   # It gets restored and build, but not installed. You may need to regenerate your nuget lockfile after setting this.
-, testProjectFile ? ""
+, testProjectFile ? null
 
   # The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc.
 , buildType ? "Release"
@@ -84,23 +84,22 @@
 , dotnet-sdk ? dotnetCorePackages.sdk_6_0
   # The dotnet runtime to use.
 , dotnet-runtime ? dotnetCorePackages.runtime_6_0
-  # The dotnet SDK to run tests against. This can differentiate from the SDK compiled against.
-, dotnet-test-sdk ? dotnet-sdk
 , ...
 } @ args:
 
 let
+  projectFiles =
+    lib.optionals (projectFile != null) (lib.toList projectFile);
+  testProjectFiles =
+    lib.optionals (testProjectFile != null) (lib.toList testProjectFile);
+
   platforms =
     if args ? meta.platforms
     then lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms
     else dotnet-sdk.meta.platforms;
 
   inherit (callPackage ./hooks {
-    inherit dotnet-sdk dotnet-test-sdk disabledTests nuget-source dotnet-runtime runtimeDeps buildType;
-    runtimeId =
-      if runtimeId != null
-      then runtimeId
-      else dotnetCorePackages.systemToDotnetRid stdenvNoCC.targetPlatform.system;
+    inherit dotnet-sdk dotnet-runtime;
   }) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook;
 
   localDeps =
@@ -121,7 +120,7 @@ let
   # contains the actual package dependencies
   dependenciesSource = mkNugetSource {
     name = "${name}-dependencies-source";
-    description = "A Nuget source with the dependencies for ${name}";
+    description = "Nuget source with the dependencies for ${name}";
     deps = [ _nugetDeps ] ++ lib.optional (localDeps != null) localDeps;
   };
 
@@ -145,6 +144,19 @@ let
   nugetDepsFile = _nugetDeps.sourceFile;
 in
 stdenvNoCC.mkDerivation (args // {
+  dotnetInstallPath = installPath;
+  dotnetExecutables = executables;
+  dotnetBuildType = buildType;
+  dotnetProjectFiles = projectFiles;
+  dotnetTestProjectFiles = testProjectFiles;
+  dotnetDisabledTests = disabledTests;
+  dotnetRuntimeId = runtimeId;
+  nugetSource = nuget-source;
+  dotnetRuntimeDeps = map lib.getLib runtimeDeps;
+  dotnetSelfContainedBuild = selfContainedBuild;
+  dotnetUseAppHost = useAppHost;
+  inherit useDotnetFromEnv;
+
   nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [
     dotnetConfigureHook
     dotnetBuildHook
@@ -174,7 +186,7 @@ stdenvNoCC.mkDerivation (args // {
        else [ ]));
 
   makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [
-    "--prefix LD_LIBRARY_PATH : ${dotnet-sdk.icu}/lib"
+    "--prefix" "LD_LIBRARY_PATH" ":" "${dotnet-sdk.icu}/lib"
   ];
 
   # Stripping breaks the executable
@@ -183,8 +195,6 @@ stdenvNoCC.mkDerivation (args // {
   # gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping
   dontWrapGApps = args.dontWrapGApps or true;
 
-  inherit selfContainedBuild useAppHost useDotnetFromEnv;
-
   # propagate the runtime sandbox profile since the contents apply to published
   # executables
   propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile;
@@ -269,11 +279,11 @@ stdenvNoCC.mkDerivation (args // {
                 --no-cache \
                 --force \
                 ${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \
-                ${lib.optionalString (flags != []) (toString flags)}
+                ${lib.escapeShellArgs flags}
         }
 
-        declare -a projectFiles=( ${toString (lib.toList projectFile)} )
-        declare -a testProjectFiles=( ${toString (lib.toList testProjectFile)} )
+        declare -a projectFiles=( ${lib.escapeShellArgs projectFiles} )
+        declare -a testProjectFiles=( ${lib.escapeShellArgs testProjectFiles} )
 
         export DOTNET_NOLOGO=1
         export DOTNET_CLI_TELEMETRY_OPTOUT=1
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix
index 7012ff36a4a5..b9c51a743c6a 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/default.nix
@@ -4,30 +4,21 @@
 , coreutils
 , zlib
 , openssl
-, callPackage
 , makeSetupHook
-, makeWrapper
+, dotnetCorePackages
+  # Passed from ../default.nix
 , dotnet-sdk
-, dotnet-test-sdk
-, disabledTests
-, nuget-source
 , dotnet-runtime
-, runtimeDeps
-, buildType
-, runtimeId
 }:
-assert (builtins.isString runtimeId);
-
 let
-  libraryPath = lib.makeLibraryPath runtimeDeps;
+  runtimeId = dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system;
 in
 {
-  dotnetConfigureHook = callPackage ({ }:
-    makeSetupHook {
+  dotnetConfigureHook = makeSetupHook
+    {
       name = "dotnet-configure-hook";
-      propagatedBuildInputs = [ dotnet-sdk nuget-source ];
       substitutions = {
-        nugetSource = nuget-source;
+        runtimeId = lib.escapeShellArg runtimeId;
         dynamicLinker = "${stdenv.cc}/nix-support/dynamic-linker";
         libPath = lib.makeLibraryPath [
           stdenv.cc.cc.lib
@@ -36,54 +27,44 @@ in
           zlib
           openssl
         ];
-        inherit runtimeId;
       };
-    } ./dotnet-configure-hook.sh) { };
+    }
+    ./dotnet-configure-hook.sh;
 
-  dotnetBuildHook = callPackage ({ }:
-    makeSetupHook {
+  dotnetBuildHook = makeSetupHook
+    {
       name = "dotnet-build-hook";
-      propagatedBuildInputs = [ dotnet-sdk ];
       substitutions = {
-        inherit buildType runtimeId;
+        runtimeId = lib.escapeShellArg runtimeId;
       };
-    } ./dotnet-build-hook.sh) { };
+    }
+    ./dotnet-build-hook.sh;
 
-  dotnetCheckHook = callPackage ({ }:
-    makeSetupHook {
+  dotnetCheckHook = makeSetupHook
+    {
       name = "dotnet-check-hook";
-      propagatedBuildInputs = [ dotnet-test-sdk ];
       substitutions = {
-        inherit buildType runtimeId libraryPath;
-        disabledTests = lib.optionalString (disabledTests != [])
-          (let
-            escapedNames = lib.lists.map (n: lib.replaceStrings [","] ["%2C"] n) disabledTests;
-            filters = lib.lists.map (n: "FullyQualifiedName!=${n}") escapedNames;
-          in
-          "${lib.concatStringsSep "&" filters}");
+        runtimeId = lib.escapeShellArg runtimeId;
       };
-    } ./dotnet-check-hook.sh) { };
+    }
+    ./dotnet-check-hook.sh;
 
-  dotnetInstallHook = callPackage ({ }:
-    makeSetupHook {
+  dotnetInstallHook = makeSetupHook
+    {
       name = "dotnet-install-hook";
-      propagatedBuildInputs = [ dotnet-sdk ];
       substitutions = {
-        inherit buildType runtimeId;
+        runtimeId = lib.escapeShellArg runtimeId;
       };
-    } ./dotnet-install-hook.sh) { };
+    }
+    ./dotnet-install-hook.sh;
 
-  dotnetFixupHook = callPackage ({ }:
-    makeSetupHook {
+  dotnetFixupHook = makeSetupHook
+    {
       name = "dotnet-fixup-hook";
-      propagatedBuildInputs = [ dotnet-runtime ];
       substitutions = {
         dotnetRuntime = dotnet-runtime;
-        runtimeDeps = libraryPath;
-        shell = stdenv.shell;
-        which = "${which}/bin/which";
-        dirname = "${coreutils}/bin/dirname";
-        realpath = "${coreutils}/bin/realpath";
+        wrapperPath = lib.makeBinPath [ which coreutils ];
       };
-    } ./dotnet-fixup-hook.sh) { };
+    }
+    ./dotnet-fixup-hook.sh;
 }
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh
index 0acfeced9b16..f209861f79b1 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-build-hook.sh
@@ -1,12 +1,25 @@
-# inherit arguments from derivation
-dotnetBuildFlags=( ${dotnetBuildFlags[@]-} )
-
 dotnetBuildHook() {
     echo "Executing dotnetBuildHook"
 
     runHook preBuild
 
-    if [ "${enableParallelBuilding-}" ]; then
+    local -r hostRuntimeId=@runtimeId@
+    local -r dotnetBuildType="${dotnetBuildType-Release}"
+    local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
+
+    if [[ -n $__structuredAttrs ]]; then
+        local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
+        local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
+        local dotnetFlagsArray=( "${dotnetFlags[@]}" )
+        local dotnetBuildFlagsArray=( "${dotnetBuildFlags[@]}" )
+    else
+        local dotnetProjectFilesArray=($dotnetProjectFiles)
+        local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
+        local dotnetFlagsArray=($dotnetFlags)
+        local dotnetBuildFlagsArray=($dotnetBuildFlags)
+    fi
+
+    if [[ -n "${enableParallelBuilding-}" ]]; then
         local -r maxCpuFlag="$NIX_BUILD_CORES"
         local -r parallelBuildFlag="true"
     else
@@ -14,50 +27,53 @@ dotnetBuildHook() {
         local -r parallelBuildFlag="false"
     fi
 
-    if [ "${selfContainedBuild-}" ]; then
-        dotnetBuildFlags+=("-p:SelfContained=true")
+    if [[ -n ${dotnetSelfContainedBuild-} ]]; then
+        dotnetBuildFlagsArray+=("-p:SelfContained=true")
     else
-        dotnetBuildFlags+=("-p:SelfContained=false")
+        dotnetBuildFlagsArray+=("-p:SelfContained=false")
     fi
 
-    if [ "${useAppHost-}" ]; then
-        dotnetBuildFlags+=("-p:UseAppHost=true")
+    if [[ -n ${dotnetUseAppHost-} ]]; then
+        dotnetBuildFlagsArray+=("-p:UseAppHost=true")
     fi
 
-    local versionFlags=()
-    if [ "${version-}" ]; then
-        versionFlags+=("-p:InformationalVersion=${version-}")
+    local versionFlagsArray=()
+    if [[ -n ${version-} ]]; then
+        versionFlagsArray+=("-p:InformationalVersion=$version")
     fi
 
-    if [ "${versionForDotnet-}" ]; then
-        versionFlags+=("-p:Version=${versionForDotnet-}")
+    if [[ -n ${versionForDotnet-} ]]; then
+        versionFlagsArray+=("-p:Version=$versionForDotnet")
     fi
 
     dotnetBuild() {
-        local -r project="${1-}"
+        local -r projectFile="${1-}"
 
-        runtimeIdFlags=()
-        if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then
-            runtimeIdFlags+=("--runtime @runtimeId@")
+        local runtimeIdFlagsArray=()
+        if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
+            runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
         fi
 
-        env dotnet build ${project-} \
-            -maxcpucount:$maxCpuFlag \
-            -p:BuildInParallel=$parallelBuildFlag \
+        dotnet build ${1+"$projectFile"} \
+            -maxcpucount:"$maxCpuFlag" \
+            -p:BuildInParallel="$parallelBuildFlag" \
             -p:ContinuousIntegrationBuild=true \
             -p:Deterministic=true \
-            --configuration "@buildType@" \
+            --configuration "$dotnetBuildType" \
             --no-restore \
-            ${versionFlags[@]} \
-            ${runtimeIdFlags[@]} \
-            ${dotnetBuildFlags[@]}  \
-            ${dotnetFlags[@]}
+            "${versionFlagsArray[@]}" \
+            "${runtimeIdFlagsArray[@]}" \
+            "${dotnetBuildFlagsArray[@]}" \
+            "${dotnetFlagsArray[@]}"
     }
 
-    (( "${#projectFile[@]}" == 0 )) && dotnetBuild
+    if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
+        dotnetBuild
+    fi
 
-    for project in ${projectFile[@]} ${testProjectFile[@]-}; do
-        dotnetBuild "$project"
+    local projectFile
+    for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
+        dotnetBuild "$projectFile"
     done
 
     runHook postBuild
@@ -65,6 +81,6 @@ dotnetBuildHook() {
     echo "Finished dotnetBuildHook"
 }
 
-if [[ -z "${dontDotnetBuild-}" && -z "${buildPhase-}" ]]; then
+if [[ -z ${dontDotnetBuild-} && -z ${buildPhase-} ]]; then
     buildPhase=dotnetBuildHook
 fi
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh
index 507721ef9818..c91251f4f180 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-check-hook.sh
@@ -1,39 +1,65 @@
-# inherit arguments from derivation
-dotnetTestFlags=( ${dotnetTestFlags[@]-} )
-
 dotnetCheckHook() {
     echo "Executing dotnetCheckHook"
 
     runHook preCheck
 
-    if [ "${disabledTests-}" ]; then
-        local -r disabledTestsFlag="--filter @disabledTests@"
+    local -r hostRuntimeId=@runtimeId@
+    local -r dotnetBuildType="${dotnetBuildType-Release}"
+    local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
+
+    if [[ -n $__structuredAttrs ]]; then
+        local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
+        local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
+        local dotnetTestFlagsArray=( "${dotnetTestFlags[@]}" )
+        local dotnetDisabledTestsArray=( "${dotnetDisabledTests[@]}" )
+        local dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
+    else
+        local dotnetProjectFilesArray=($dotnetProjectFiles)
+        local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
+        local dotnetTestFlagsArray=($dotnetTestFlags)
+        local dotnetDisabledTestsArray=($dotnetDisabledTests)
+        local dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
+    fi
+
+    if (( ${#dotnetDisabledTestsArray[@]} > 0 )); then
+        local disabledTestsFilters=("${dotnetDisabledTestsArray[@]/#/FullyQualifiedName!=}")
+        local OLDIFS="$IFS" IFS='&'
+        dotnetTestFlagsArray+=("--filter:${disabledTestsFilters[*]//,/%2C}")
+        IFS="$OLDIFS"
+    fi
+
+    local libraryPath="${LD_LIBRARY_PATH-}"
+    if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
+        local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
+        local OLDIFS="$IFS" IFS=':'
+        libraryPath="${libraryPathArray[*]}${libraryPath:+':'}$libraryPath"
+        IFS="$OLDIFS"
     fi
 
-    if [ "${enableParallelBuilding-}" ]; then
+    if [[ -n ${enableParallelBuilding-} ]]; then
         local -r maxCpuFlag="$NIX_BUILD_CORES"
     else
         local -r maxCpuFlag="1"
     fi
 
-    for project in ${testProjectFile[@]-${projectFile[@]}}; do
-        runtimeIdFlags=()
-        if [[ "$project" == *.csproj ]]; then
-            runtimeIdFlags=("--runtime @runtimeId@")
+    local projectFile
+    for projectFile in "${dotnetTestProjectFilesArray[@]-${dotnetProjectFilesArray[@]}}"; do
+        local runtimeIdFlagsArray=()
+        if [[ $projectFile == *.csproj ]]; then
+            runtimeIdFlagsArray=("--runtime" "$dotnetRuntimeId")
         fi
 
-        env "LD_LIBRARY_PATH=@libraryPath@" \
-            dotnet test "$project" \
-              -maxcpucount:$maxCpuFlag \
+        LD_LIBRARY_PATH=$libraryPath \
+            dotnet test "$projectFile" \
+              -maxcpucount:"$maxCpuFlag" \
               -p:ContinuousIntegrationBuild=true \
               -p:Deterministic=true \
-              --configuration "@buildType@" \
+              --configuration "$dotnetBuildType" \
               --no-build \
               --logger "console;verbosity=normal" \
-              ${disabledTestsFlag-} \
-              ${runtimeIdFlags[@]} \
-              "${dotnetTestFlags[@]}"  \
-              "${dotnetFlags[@]}"
+              "${runtimeIdFlagsArray[@]}" \
+              "${dotnetTestFlagsArray[@]}" \
+              "${dotnetFlagsArray[@]}"
     done
 
     runHook postCheck
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh
index c046fc3c306b..12fa34869986 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-configure-hook.sh
@@ -1,63 +1,103 @@
-declare -a projectFile testProjectFile
-
-# Inherit arguments from derivation
-dotnetFlags=( ${dotnetFlags[@]-} )
-dotnetRestoreFlags=( ${dotnetRestoreFlags[@]-} )
-
 dotnetConfigureHook() {
     echo "Executing dotnetConfigureHook"
 
     runHook preConfigure
 
-    if [ -z "${enableParallelBuilding-}" ]; then
+    if [[ -z ${nugetSource-} ]]; then
+        echo
+        echo "ERROR: no dependencies were specified"
+        echo 'Hint: set `nugetSource` if using these hooks individually. If this is happening with `buildDotnetModule`, please open an issue.'
+        echo
+
+        exit 1
+    fi
+
+    local nugetSourceSedQuoted="${nugetSource//[\/\\&$'\n']/\\&}"
+    local nugetSourceXMLQuoted="$nugetSource"
+    nugetSourceXMLQuoted="${nugetSource//&/\&amp;}"
+    nugetSourceXMLQuoted="${nugetSourceXMLQuoted//\"/\&quot;}"
+
+    local -r hostRuntimeId=@runtimeId@
+    local -r dynamicLinker=@dynamicLinker@
+    local -r libPath=@libPath@
+    local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
+
+    if [[ -n $__structuredAttrs ]]; then
+        local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
+        local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
+        local dotnetFlagsArray=( "${dotnetFlags[@]}" )
+        local dotnetRestoreFlagsArray=( "${dotnetRestoreFlags[@]}" )
+    else
+        local dotnetProjectFilesArray=($dotnetProjectFiles)
+        local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
+        local dotnetFlagsArray=($dotnetFlags)
+        local dotnetRestoreFlagsArray=($dotnetRestoreFlags)
+    fi
+
+    if [[ -z ${enableParallelBuilding-} ]]; then
         local -r parallelFlag="--disable-parallel"
     fi
 
     dotnetRestore() {
-        local -r project="${1-}"
-        env dotnet restore ${project-} \
+        local -r projectFile="${1-}"
+        dotnet restore ${1+"$projectFile"} \
             -p:ContinuousIntegrationBuild=true \
             -p:Deterministic=true \
-            --runtime "@runtimeId@" \
-            --source "@nugetSource@/lib" \
+            --runtime "$dotnetRuntimeId" \
+            --source "$nugetSource/lib" \
             ${parallelFlag-} \
-            ${dotnetRestoreFlags[@]} \
-            ${dotnetFlags[@]}
+            "${dotnetRestoreFlagsArray[@]}" \
+            "${dotnetFlagsArray[@]}"
     }
 
     # Generate a NuGet.config file to make sure everything,
     # including things like <Sdk /> dependencies, is restored from the proper source
-cat <<EOF > "./NuGet.config"
+    cat >NuGet.config <<EOF
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <packageSources>
     <clear />
-    <add key="nugetSource" value="@nugetSource@/lib" />
+    <add key="nugetSource" value="$nugetSourceXMLQuoted/lib" />
   </packageSources>
 </configuration>
 EOF
 
-    # Patch paket.dependencies and paket.lock (if found) to use the proper source. This ensures
-    # paket restore works correctly
-    # We use + instead of / in sed to avoid problems with slashes
-    find -name paket.dependencies -exec sed -i 's+source .*+source @nugetSource@/lib+g' {} \;
-    find -name paket.lock -exec sed -i 's+remote:.*+remote: @nugetSource@/lib+g' {} \;
-
-    env dotnet tool restore --add-source "@nugetSource@/lib"
-
-    (( "${#projectFile[@]}" == 0 )) && dotnetRestore
+    # Patch paket.dependencies and paket.lock (if found) to use the proper
+    # source. This ensures paket restore works correctly. Note that the
+    # nugetSourceSedQuoted abomination below safely escapes nugetSource string
+    # for use as a sed replacement string to avoid issues with slashes and other
+    # special characters ('&', '\\' and '\n').
+    find -name paket.dependencies -exec sed -i "s/source .*/source $nugetSourceSedQuoted\/lib/g" {} \;
+    find -name paket.lock -exec sed -i "s/remote:.*/remote: $nugetSourceSedQuoted\/lib/g" {} \;
+
+    dotnet tool restore --add-source "$nugetSource/lib"
+
+    # dotnetGlobalTool is set in buildDotnetGlobalTool to patch dependencies but
+    # avoid other project-specific logic. This is a hack, but the old behavior
+    # is worse as it relied on a bug: setting projectFile to an empty string
+    # made the hooks actually skip all project-specific logic. It’s hard to keep
+    # backwards compatibility with this odd behavior now since we are using
+    # arrays, so instead we just pass a variable to indicate that we don’t have
+    # projects.
+    if [[ -z ${dotnetGlobalTool-} ]]; then
+        if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
+            dotnetRestore
+        fi
 
-    for project in ${projectFile[@]} ${testProjectFile[@]-}; do
-        dotnetRestore "$project"
-    done
+        local projectFile
+        for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
+            dotnetRestore "$projectFile"
+        done
+    fi
 
     echo "Fixing up native binaries..."
     # Find all native binaries and nuget libraries, and fix them up,
     # by setting the proper interpreter and rpath to some commonly used libraries
+    local binary
     for binary in $(find "$HOME/.nuget/packages/" -type f -executable); do
         if patchelf --print-interpreter "$binary" >/dev/null 2>/dev/null; then
             echo "Found binary: $binary, fixing it up..."
-            patchelf --set-interpreter "$(cat "@dynamicLinker@")" "$binary"
+            patchelf --set-interpreter "$(cat "$dynamicLinker")" "$binary"
 
             # This makes sure that if the binary requires some specific runtime dependencies, it can find it.
             # This fixes dotnet-built binaries like crossgen2
@@ -68,7 +108,7 @@ EOF
                 --add-needed libssl.so \
                 "$binary"
 
-            patchelf --set-rpath "@libPath@" "$binary"
+            patchelf --set-rpath "$libPath" "$binary"
         fi
     done
 
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh
index e3671728af35..f9aba29a4355 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-fixup-hook.sh
@@ -1,28 +1,55 @@
-# Inherit arguments from the derivation
-declare -a derivationMakeWrapperArgs="( ${makeWrapperArgs-} )"
-makeWrapperArgs=( "${derivationMakeWrapperArgs[@]}" )
-
 # First argument is the executable you want to wrap,
 # the second is the destination for the wrapper.
 wrapDotnetProgram() {
-    local dotnetRootFlags=()
+    local -r dotnetRuntime=@dotnetRuntime@
+    local -r wrapperPath=@wrapperPath@
+
+    local -r dotnetFromEnvScript='dotnetFromEnv() {
+    local dotnetPath
+    if command -v dotnet 2>&1 >/dev/null; then
+        dotnetPath=$(which dotnet) && \
+            dotnetPath=$(realpath "$dotnetPath") && \
+            dotnetPath=$(dirname "$dotnetPath") && \
+            export DOTNET_ROOT="$dotnetPath"
+    fi
+}
+dotnetFromEnv'
+
+    if [[ -n $__structuredAttrs ]]; then
+        local -r dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
+        local -r makeWrapperArgsArray=( "${makeWrapperArgs[@]}" )
+    else
+        local -r dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
+        local -r makeWrapperArgsArray=($makeWrapperArgs)
+    fi
 
-    if [ ! "${selfContainedBuild-}" ]; then
-        if [ "${useDotnetFromEnv-}" ]; then
+    local dotnetRuntimeDepsFlags=()
+    if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
+        local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
+        local OLDIFS="$IFS" IFS=':'
+        dotnetRuntimeDepsFlags+=("--suffix" "LD_LIBRARY_PATH" ":" "${libraryPathArray[*]}")
+        IFS="$OLDIFS"
+    fi
+
+    local dotnetRootFlagsArray=()
+    if [[ -z ${dotnetSelfContainedBuild-} ]]; then
+        if [[ -n ${useDotnetFromEnv-} ]]; then
             # if dotnet CLI is available, set DOTNET_ROOT based on it. Otherwise set to default .NET runtime
-            dotnetRootFlags+=("--run" 'command -v dotnet &>/dev/null && export DOTNET_ROOT="$(@dirname@ "$(@realpath@ "$(@which@ dotnet)")")" || export DOTNET_ROOT="@dotnetRuntime@"')
-            dotnetRootFlags+=("--suffix" "PATH" ":" "@dotnetRuntime@/bin")
+            dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$wrapperPath")
+            dotnetRootFlagsArray+=("--run" "$dotnetFromEnvScript")
+            dotnetRootFlagsArray+=("--set-default" "DOTNET_ROOT" "$dotnetRuntime")
+            dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$dotnetRuntime/bin")
         else
-            dotnetRootFlags+=("--set" "DOTNET_ROOT" "@dotnetRuntime@")
-            dotnetRootFlags+=("--prefix" "PATH" ":" "@dotnetRuntime@/bin")
+            dotnetRootFlagsArray+=("--set" "DOTNET_ROOT" "$dotnetRuntime")
+            dotnetRootFlagsArray+=("--prefix" "PATH" ":" "$dotnetRuntime/bin")
         fi
     fi
 
     makeWrapper "$1" "$2" \
-        --suffix "LD_LIBRARY_PATH" : "@runtimeDeps@" \
-        "${dotnetRootFlags[@]}" \
+        "${dotnetRuntimeDepsFlags[@]}" \
+        "${dotnetRootFlagsArray[@]}" \
         "${gappsWrapperArgs[@]}" \
-        "${makeWrapperArgs[@]}"
+        "${makeWrapperArgsArray[@]}"
 
     echo "installed wrapper to "$2""
 }
@@ -30,13 +57,24 @@ wrapDotnetProgram() {
 dotnetFixupHook() {
     echo "Executing dotnetFixupPhase"
 
-    # check if executables is declared (including empty values, in which case we generate no executables)
-    if declare -p executables &>/dev/null; then
-        for executable in ${executables[@]}; do
-            path="${installPath-$out/lib/$pname}/$executable"
+    local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
+
+    local executable executableBasename
+
+    # check if dotnetExecutables is declared (including empty values, in which case we generate no executables)
+    if declare -p dotnetExecutables &>/dev/null; then
+        if [[ -n $__structuredAttrs ]]; then
+            local dotnetExecutablesArray=( "${dotnetExecutables[@]}" )
+        else
+            local dotnetExecutablesArray=($dotnetExecutables)
+        fi
+        for executable in "${dotnetExecutablesArray[@]}"; do
+            executableBasename=$(basename "$executable")
+
+            local path="$dotnetInstallPath/$executable"
 
             if test -x "$path"; then
-                wrapDotnetProgram "$path" "$out/bin/$(basename "$executable")"
+                wrapDotnetProgram "$path" "$out/bin/$executableBasename"
             else
                 echo "Specified binary \"$executable\" is either not an executable or does not exist!"
                 echo "Looked in $path"
@@ -45,8 +83,9 @@ dotnetFixupHook() {
         done
     else
         while IFS= read -d '' executable; do
-            wrapDotnetProgram "$executable" "$out/bin/$(basename "$executable")" \;
-        done < <(find "${installPath-$out/lib/$pname}" ! -name "*.dll" -executable -type f -print0)
+            executableBasename=$(basename "$executable")
+            wrapDotnetProgram "$executable" "$out/bin/$executableBasename" \;
+        done < <(find "$dotnetInstallPath" ! -name "*.dll" -executable -type f -print0)
     fi
 
     echo "Finished dotnetFixupPhase"
diff --git a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh
index d832eac28809..4d9b3c502c35 100644
--- a/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh
+++ b/nixpkgs/pkgs/build-support/dotnet/build-dotnet-module/hooks/dotnet-install-hook.sh
@@ -1,70 +1,86 @@
-# inherit arguments from derivation
-dotnetInstallFlags=( ${dotnetInstallFlags[@]-} )
-
 dotnetInstallHook() {
     echo "Executing dotnetInstallHook"
 
     runHook preInstall
 
-    if [ "${selfContainedBuild-}" ]; then
-        dotnetInstallFlags+=("--self-contained")
+    local -r hostRuntimeId=@runtimeId@
+    local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
+    local -r dotnetBuildType="${dotnetBuildType-Release}"
+    local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
+
+    if [[ -n $__structuredAttrs ]]; then
+        local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
+        local dotnetFlagsArray=( "${dotnetFlags[@]}" )
+        local dotnetInstallFlagsArray=( "${dotnetInstallFlags[@]}" )
+        local dotnetPackFlagsArray=( "${dotnetPackFlags[@]}" )
+    else
+        local dotnetProjectFilesArray=($dotnetProjectFiles)
+        local dotnetFlagsArray=($dotnetFlags)
+        local dotnetInstallFlagsArray=($dotnetInstallFlags)
+        local dotnetPackFlagsArray=($dotnetPackFlags)
+    fi
+
+    if [[ -n ${dotnetSelfContainedBuild-} ]]; then
+        dotnetInstallFlagsArray+=("--self-contained")
     else
-        dotnetInstallFlags+=("--no-self-contained")
+        dotnetInstallFlagsArray+=("--no-self-contained")
         # https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained
         # Trimming is only available for self-contained build, so force disable it here
-        dotnetInstallFlags+=("-p:PublishTrimmed=false")
+        dotnetInstallFlagsArray+=("-p:PublishTrimmed=false")
     fi
 
-    if [ "${useAppHost-}" ]; then
-        dotnetInstallFlags+=("-p:UseAppHost=true")
+    if [[ -n ${dotnetUseAppHost-} ]]; then
+        dotnetInstallFlagsArray+=("-p:UseAppHost=true")
     fi
 
     dotnetPublish() {
-        local -r project="${1-}"
+        local -r projectFile="${1-}"
 
-        runtimeIdFlags=()
-        if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then
-            runtimeIdFlags+=("--runtime @runtimeId@")
+        runtimeIdFlagsArray=()
+        if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
+            runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
         fi
 
-        env dotnet publish ${project-} \
+        dotnet publish ${1+"$projectFile"} \
             -p:ContinuousIntegrationBuild=true \
             -p:Deterministic=true \
-            --output "${installPath-$out/lib/$pname}" \
-            --configuration "@buildType@" \
+            --output "$dotnetInstallPath" \
+            --configuration "$dotnetBuildType" \
             --no-build \
-            ${runtimeIdFlags[@]} \
-            ${dotnetInstallFlags[@]}  \
-            ${dotnetFlags[@]}
+            "${runtimeIdFlagsArray[@]}" \
+            "${dotnetInstallFlagsArray[@]}" \
+            "${dotnetFlagsArray[@]}"
     }
 
     dotnetPack() {
-        local -r project="${1-}"
-        env dotnet pack ${project-} \
+        local -r projectFile="${1-}"
+        dotnet pack ${1+"$projectFile"} \
             -p:ContinuousIntegrationBuild=true \
             -p:Deterministic=true \
             --output "$out/share" \
-            --configuration "@buildType@" \
+            --configuration "$dotnetBuildType" \
             --no-build \
-            --runtime "@runtimeId@" \
-            ${dotnetPackFlags[@]}  \
-            ${dotnetFlags[@]}
+            --runtime "$dotnetRuntimeId" \
+            "${dotnetPackFlagsArray[@]}" \
+            "${dotnetFlagsArray[@]}"
     }
 
-    if (( "${#projectFile[@]}" == 0 )); then
+    if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
         dotnetPublish
     else
-        for project in ${projectFile[@]}; do
-            dotnetPublish "$project"
+        local projectFile
+        for projectFile in "${dotnetProjectFilesArray[@]}"; do
+            dotnetPublish "$projectFile"
         done
     fi
 
-    if [[ "${packNupkg-}" ]]; then
-        if (( "${#projectFile[@]}" == 0 )); then
+    if [[ -n ${packNupkg-} ]]; then
+        if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
             dotnetPack
         else
-            for project in ${projectFile[@]}; do
-                dotnetPack "$project"
+            local projectFile
+            for projectFile in "${dotnetProjectFilesArray[@]}"; do
+                dotnetPack "$projectFile"
             done
         fi
     fi
diff --git a/nixpkgs/pkgs/build-support/emacs/melpa.nix b/nixpkgs/pkgs/build-support/emacs/melpa.nix
index 323c6e65d9d9..c8f656704918 100644
--- a/nixpkgs/pkgs/build-support/emacs/melpa.nix
+++ b/nixpkgs/pkgs/build-support/emacs/melpa.nix
@@ -35,25 +35,41 @@ in
   pname
   /*
     ename: Original Emacs package name, possibly containing special symbols.
+    Default: pname
   */
-, ename ? null
+, ename ? pname
 , version
-, recipe
+  /*
+    commit: Optional package history commit.
+    Default: src.rev or "unknown"
+    This will be written into the generated package but it is not needed during
+    the build process.
+  */
+, commit ? (args.src.rev or "unknown")
+  /*
+    files: Optional recipe property specifying the files used to build the package.
+    If null, do not set it in recipe, keeping the default upstream behaviour.
+    Default: null
+  */
+, files ? null
+  /*
+    recipe: Optional MELPA recipe.
+    Default: a minimally functional recipe
+  */
+, recipe ? (writeText "${pname}-recipe" ''
+    (${ename} :fetcher git :url ""
+              ${lib.optionalString (files != null) ":files ${files}"})
+  '')
 , meta ? {}
 , ...
 }@args:
 
 genericBuild ({
 
-  ename =
-    if ename == null
-    then pname
-    else ename;
-
   elpa2nix = ./elpa2nix.el;
   melpa2nix = ./melpa2nix.el;
 
-  inherit packageBuild;
+  inherit packageBuild commit ename recipe;
 
   preUnpack = ''
     mkdir -p "$NIX_BUILD_TOP/recipes"
diff --git a/nixpkgs/pkgs/build-support/flutter/default.nix b/nixpkgs/pkgs/build-support/flutter/default.nix
index 2af03d5f98e3..ff6c6b31006f 100644
--- a/nixpkgs/pkgs/build-support/flutter/default.nix
+++ b/nixpkgs/pkgs/build-support/flutter/default.nix
@@ -10,21 +10,32 @@
 , pkg-config
 , jq
 , yq
-, moreutils
 }:
 
 # absolutely no mac support for now
 
-{ pubGetScript ? "flutter pub get"
+{ pubGetScript ? null
 , flutterBuildFlags ? [ ]
 , targetFlutterPlatform ? "linux"
 , extraWrapProgramArgs ? ""
+, flutterMode ? null
 , ...
 }@args:
 
 let
+  hasEngine = flutter ? engine && flutter.engine != null && flutter.engine.meta.available;
+  flutterMode = args.flutterMode or (if hasEngine then flutter.engine.runtimeMode else "release");
+
+  flutterFlags = lib.optional hasEngine "--local-engine host_${flutterMode}${lib.optionalString (!flutter.engine.isOptimized) "_unopt"}";
+
+  flutterBuildFlags = [
+    "--${flutterMode}"
+  ] ++ (args.flutterBuildFlags or []) ++ flutterFlags;
+
   builderArgs = rec {
     universal = args // {
+      inherit flutterMode flutterFlags flutterBuildFlags;
+
       sdkSetupScript = ''
         # Pub needs SSL certificates. Dart normally looks in a hardcoded path.
         # https://github.com/dart-lang/sdk/blob/3.1.0/runtime/bin/security_context_linux.cc#L48
@@ -47,11 +58,11 @@ let
         ''}/bin/dart"
 
         export HOME="$NIX_BUILD_TOP"
-        flutter config --no-analytics &>/dev/null # mute first-run
-        flutter config --enable-linux-desktop >/dev/null
+        flutter config $flutterFlags --no-analytics &>/dev/null # mute first-run
+        flutter config $flutterFlags --enable-linux-desktop >/dev/null
       '';
 
-      inherit pubGetScript;
+      pubGetScript = args.pubGetScript or "flutter${lib.optionalString hasEngine " --local-engine $flutterMode"} pub get";
 
       sdkSourceBuilders = {
         # https://github.com/dart-lang/pub/blob/68dc2f547d0a264955c1fa551fa0a0e158046494/lib/src/sdk/flutter.dart#L81
@@ -68,16 +79,34 @@ let
             exit 1
           fi
         '';
+        # https://github.com/dart-lang/pub/blob/e1fbda73d1ac597474b82882ee0bf6ecea5df108/lib/src/sdk/dart.dart#L80
+        "dart" = name: runCommand "dart-sdk-${name}" { passthru.packageRoot = "."; } ''
+          for path in '${flutter.dart}/pkg/${name}'; do
+            if [ -d "$path" ]; then
+              ln -s "$path" "$out"
+              break
+            fi
+          done
+
+          if [ ! -e "$out" ]; then
+            echo 1>&2 'The Dart SDK does not contain the requested package: ${name}!'
+            exit 1
+          fi
+        '';
       };
 
       extraPackageConfigSetup = ''
         # https://github.com/flutter/flutter/blob/3.13.8/packages/flutter_tools/lib/src/dart/pub.dart#L755
         if [ "$('${yq}/bin/yq' '.flutter.generate // false' pubspec.yaml)" = "true" ]; then
+          export TEMP_PACKAGES=$(mktemp)
           '${jq}/bin/jq' '.packages |= . + [{
             name: "flutter_gen",
             rootUri: "flutter_gen",
             languageVersion: "2.12",
-          }]' "$out" | '${moreutils}/bin/sponge' "$out"
+          }]' "$out" > "$TEMP_PACKAGES"
+          cp "$TEMP_PACKAGES" "$out"
+          rm "$TEMP_PACKAGES"
+          unset TEMP_PACKAGES
         fi
       '';
     };
@@ -105,7 +134,7 @@ let
 
         mkdir -p build/flutter_assets/fonts
 
-        flutter build linux -v --release --split-debug-info="$debug" ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") flutterBuildFlags)}
+        flutter build linux -v --split-debug-info="$debug" $flutterBuildFlags
 
         runHook postBuild
       '';
@@ -114,7 +143,7 @@ let
       installPhase = universal.installPhase or ''
         runHook preInstall
 
-        built=build/linux/*/release/bundle
+        built=build/linux/*/$flutterMode/bundle
 
         mkdir -p $out/bin
         mv $built $out/app
@@ -156,7 +185,7 @@ let
 
         mkdir -p build/flutter_assets/fonts
 
-        flutter build web -v --release ${builtins.concatStringsSep " " (map (flag: "\"${flag}\"") flutterBuildFlags)}
+        flutter build web -v $flutterBuildFlags
 
         runHook postBuild
       '';
diff --git a/nixpkgs/pkgs/build-support/libredirect/default.nix b/nixpkgs/pkgs/build-support/libredirect/default.nix
index 1ab4a0db827a..c4399189b7a2 100644
--- a/nixpkgs/pkgs/build-support/libredirect/default.nix
+++ b/nixpkgs/pkgs/build-support/libredirect/default.nix
@@ -112,7 +112,7 @@ else stdenv.mkDerivation rec {
 
   meta = with lib; {
     platforms = platforms.unix;
-    description = "An LD_PRELOAD library to intercept and rewrite the paths in glibc calls";
+    description = "LD_PRELOAD library to intercept and rewrite the paths in glibc calls";
     longDescription = ''
       libredirect is an LD_PRELOAD library to intercept and rewrite the paths in
       glibc calls based on the value of $NIX_REDIRECTS, a colon-separated list
diff --git a/nixpkgs/pkgs/build-support/make-pkgconfigitem/default.nix b/nixpkgs/pkgs/build-support/make-pkgconfigitem/default.nix
index d3bcabbb940f..e459184b8d7e 100644
--- a/nixpkgs/pkgs/build-support/make-pkgconfigitem/default.nix
+++ b/nixpkgs/pkgs/build-support/make-pkgconfigitem/default.nix
@@ -4,7 +4,7 @@
 { name # The name of the pc file
   # keywords
   # provide a default description for convenience. it's not important but still required by pkg-config.
-, description ? "A pkg-config file for ${name}"
+, description ? "Pkg-config file for ${name}"
 , url ? ""
 , version ? ""
 , requires ? [ ]
diff --git a/nixpkgs/pkgs/build-support/node/fetch-yarn-deps/default.nix b/nixpkgs/pkgs/build-support/node/fetch-yarn-deps/default.nix
index 7f0e0692f81f..4ef74c0cab88 100644
--- a/nixpkgs/pkgs/build-support/node/fetch-yarn-deps/default.nix
+++ b/nixpkgs/pkgs/build-support/node/fetch-yarn-deps/default.nix
@@ -25,7 +25,8 @@ in {
 
       tar --strip-components=1 -xf ${yarnpkg-lockfile-tar} package/index.js
       mv index.js $out/libexec/yarnpkg-lockfile.js
-      cp ${./.}/common.js ${./.}/index.js $out/libexec/
+      cp ${./common.js} $out/libexec/common.js
+      cp ${./index.js} $out/libexec/index.js
 
       patchShebangs $out/libexec
       makeWrapper $out/libexec/index.js $out/bin/prefetch-yarn-deps \
@@ -53,7 +54,8 @@ in {
 
       tar --strip-components=1 -xf ${yarnpkg-lockfile-tar} package/index.js
       mv index.js $out/libexec/yarnpkg-lockfile.js
-      cp ${./.}/common.js ${./.}/fixup.js $out/libexec/
+      cp ${./common.js} $out/libexec/common.js
+      cp ${./fixup.js} $out/libexec/fixup.js
 
       patchShebangs $out/libexec
       makeWrapper $out/libexec/fixup.js $out/bin/fixup-yarn-lock
@@ -97,7 +99,7 @@ in {
       '';
 
       outputHashMode = "recursive";
-    } // hash_ // (removeAttrs args ["src" "name" "hash" "sha256"]));
+    } // hash_ // (removeAttrs args (["name" "hash" "sha256"] ++ (lib.optional (src == null) "src"))));
 
   in lib.setFunctionArgs f (lib.functionArgs f) // {
     inherit tests;
diff --git a/nixpkgs/pkgs/build-support/php/builders/default.nix b/nixpkgs/pkgs/build-support/php/builders/default.nix
index 209c834367d1..ea9bb3350435 100644
--- a/nixpkgs/pkgs/build-support/php/builders/default.nix
+++ b/nixpkgs/pkgs/build-support/php/builders/default.nix
@@ -2,6 +2,7 @@
 {
   v1 = {
     buildComposerProject = callPackage ./v1/build-composer-project.nix { };
+    buildComposerWithPlugin = callPackage ./v1/build-composer-with-plugin.nix { };
     mkComposerRepository = callPackage ./v1/build-composer-repository.nix { };
     composerHooks = callPackages ./v1/hooks { };
   };
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-project.nix b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-project.nix
index 7e24812d2e76..698391ad1603 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-project.nix
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-project.nix
@@ -1,5 +1,4 @@
 {
-  callPackage,
   nix-update-script,
   stdenvNoCC,
   lib,
@@ -12,8 +11,7 @@ let
 
     let
       phpDrv = finalAttrs.php or php;
-      composer = finalAttrs.composer or phpDrv.packages.composer;
-      composer-local-repo-plugin = callPackage ../../pkgs/composer-local-repo-plugin.nix { };
+      composer = finalAttrs.composer or phpDrv.packages.composer-local-repo-plugin;
     in
     {
       composerLock = previousAttrs.composerLock or null;
@@ -24,7 +22,6 @@ let
 
       nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
         composer
-        composer-local-repo-plugin
         phpDrv
         phpDrv.composerHooks.composerInstallHook
       ];
@@ -74,7 +71,7 @@ let
 
       composerRepository =
         previousAttrs.composerRepository or (phpDrv.mkComposerRepository {
-          inherit composer composer-local-repo-plugin;
+          inherit composer;
           inherit (finalAttrs)
             patches
             pname
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-repository.nix b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-repository.nix
index 54944b91d202..037d8bdeb3eb 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-repository.nix
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-repository.nix
@@ -1,5 +1,4 @@
 {
-  callPackage,
   stdenvNoCC,
   lib,
   php,
@@ -23,8 +22,7 @@ let
 
     let
       phpDrv = finalAttrs.php or php;
-      composer = finalAttrs.composer or phpDrv.packages.composer;
-      composer-local-repo-plugin = callPackage ../../pkgs/composer-local-repo-plugin.nix { };
+      composer = finalAttrs.composer or phpDrv.packages.composer-local-repo-plugin;
     in
     assert (lib.assertMsg (previousAttrs ? src) "mkComposerRepository expects src argument.");
     assert (
@@ -58,7 +56,6 @@ let
 
       nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
         composer
-        composer-local-repo-plugin
         phpDrv
         phpDrv.composerHooks.composerRepositoryHook
       ];
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-with-plugin.nix b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-with-plugin.nix
new file mode 100644
index 000000000000..060b51241e6c
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/build-composer-with-plugin.nix
@@ -0,0 +1,161 @@
+{
+  stdenvNoCC,
+  writeText,
+  lib,
+  makeBinaryWrapper,
+  php,
+  cacert,
+  nix-update-script,
+}:
+
+let
+  composerJsonBuilder =
+    pluginName: pluginVersion:
+    writeText "composer.json" (
+      builtins.toJSON {
+        name = "nix/plugin";
+        description = "Nix Composer plugin";
+        license = "MIT";
+        require = {
+          "${pluginName}" = "${pluginVersion}";
+        };
+        config = {
+          "allow-plugins" = {
+            "${pluginName}" = true;
+          };
+        };
+        repositories = [
+          {
+            type = "path";
+            url = "./src";
+            options = {
+              versions = {
+                "${pluginName}" = "${pluginVersion}";
+              };
+            };
+          }
+        ];
+      }
+    );
+
+  buildComposerWithPluginOverride =
+    finalAttrs: previousAttrs:
+
+    let
+      phpDrv = finalAttrs.php or php;
+      composer = finalAttrs.composer or phpDrv.packages.composer;
+    in
+    {
+      composerLock = previousAttrs.composerLock or null;
+      composerNoDev = previousAttrs.composerNoDev or true;
+      composerNoPlugins = previousAttrs.composerNoPlugins or true;
+      composerNoScripts = previousAttrs.composerNoScripts or true;
+      composerStrictValidation = previousAttrs.composerStrictValidation or true;
+      composerGlobal = true;
+
+      nativeBuildInputs = (previousAttrs.nativeBuildInputs or [ ]) ++ [
+        composer
+        phpDrv
+        makeBinaryWrapper
+      ];
+
+      buildInputs = (previousAttrs.buildInputs or [ ]) ++ [ phpDrv ];
+
+      patches = previousAttrs.patches or [ ];
+      strictDeps = previousAttrs.strictDeps or true;
+
+      # Should we keep these empty phases?
+      configurePhase =
+        previousAttrs.configurePhase or ''
+          runHook preConfigure
+
+          runHook postConfigure
+        '';
+
+      buildPhase =
+        previousAttrs.buildPhase or ''
+          runHook preBuild
+
+          runHook postBuild
+        '';
+
+      doCheck = previousAttrs.doCheck or true;
+
+      checkPhase =
+        previousAttrs.checkPhase or ''
+          runHook preCheck
+
+          runHook postCheck
+        '';
+
+      installPhase =
+        previousAttrs.installPhase or ''
+            runHook preInstall
+
+          makeWrapper ${lib.getExe composer} $out/bin/composer \
+            --prefix COMPOSER_HOME : ${finalAttrs.vendor}
+
+            runHook postInstall
+        '';
+
+      doInstallCheck = previousAttrs.doInstallCheck or false;
+      installCheckPhase =
+        previousAttrs.installCheckPhase or ''
+          runHook preInstallCheck
+
+          composer global show ${finalAttrs.pname}
+
+          runHook postInstallCheck
+        '';
+
+      vendor = previousAttrs.vendor or stdenvNoCC.mkDerivation {
+        pname = "${finalAttrs.pname}-vendor";
+        pluginName = finalAttrs.pname;
+
+        inherit (finalAttrs) version src;
+
+        composerLock = previousAttrs.composerLock or null;
+        composerNoDev = previousAttrs.composerNoDev or true;
+        composerNoPlugins = previousAttrs.composerNoPlugins or true;
+        composerNoScripts = previousAttrs.composerNoScripts or true;
+        composerStrictValidation = previousAttrs.composerStrictValidation or true;
+        composerGlobal = true;
+        composerJson = composerJsonBuilder finalAttrs.pname finalAttrs.version;
+
+        nativeBuildInputs = [
+          cacert
+          composer
+          phpDrv.composerHooks.composerWithPluginVendorHook
+        ];
+
+        dontPatchShebangs = true;
+        doCheck = true;
+        doInstallCheck = true;
+
+        env = {
+          COMPOSER_CACHE_DIR = "/dev/null";
+          COMPOSER_HTACCESS_PROTECT = "0";
+        };
+
+        outputHashMode = "recursive";
+        outputHashAlgo = "sha256";
+        outputHash = finalAttrs.vendorHash;
+      };
+
+      # Projects providing a lockfile from upstream can be automatically updated.
+      passthru = previousAttrs.passthru or { } // {
+        updateScript =
+          previousAttrs.passthru.updateScript
+            or (if finalAttrs.vendor.composerLock == null then nix-update-script { } else null);
+      };
+
+      env = {
+        COMPOSER_CACHE_DIR = "/dev/null";
+        COMPOSER_DISABLE_NETWORK = "1";
+        COMPOSER_MIRROR_PATH_REPOS = "1";
+      };
+
+      meta = previousAttrs.meta or composer.meta;
+    };
+in
+args: (stdenvNoCC.mkDerivation args).overrideAttrs buildComposerWithPluginOverride
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-install-hook.sh b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-install-hook.sh
index a91263422bc8..44e87d06d3a5 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-install-hook.sh
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-install-hook.sh
@@ -83,7 +83,7 @@ composerInstallBuildHook() {
 
     # Since this file cannot be generated in the composer-repository-hook.sh
     # because the file contains hardcoded nix store paths, we generate it here.
-    composer-local-repo-plugin --no-ansi build-local-repo-lock -m "${composerRepository}" .
+    composer build-local-repo-lock -m "${composerRepository}" .
 
     echo "Finished composerInstallBuildHook"
 }
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-repository-hook.sh b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-repository-hook.sh
index c4fa0d52126c..ec9777541fc0 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-repository-hook.sh
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-repository-hook.sh
@@ -63,7 +63,7 @@ composerRepositoryBuildHook() {
     # Build the local composer repository
     # The command 'build-local-repo' is provided by the Composer plugin
     # nix-community/composer-local-repo-plugin.
-    composer-local-repo-plugin --no-ansi build-local-repo-lock ${composerNoDev:+--no-dev} -r repository
+    composer build-local-repo-lock ${composerNoDev:+--no-dev} -r repository
 
     echo "Finished composerRepositoryBuildHook"
 }
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-with-plugin-vendor-hook.sh b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-with-plugin-vendor-hook.sh
new file mode 100644
index 000000000000..0d88d14094ad
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/composer-with-plugin-vendor-hook.sh
@@ -0,0 +1,93 @@
+declare composerLock
+declare version
+declare composerNoDev
+declare composerNoPlugins
+declare composerNoScripts
+declare composerStrictValidation
+
+preConfigureHooks+=(composerWithPluginConfigureHook)
+preBuildHooks+=(composerWithPluginBuildHook)
+preCheckHooks+=(composerWithPluginCheckHook)
+preInstallHooks+=(composerWithPluginInstallHook)
+preInstallCheckHooks+=(composerWithPluginInstallCheckHook)
+
+source @phpScriptUtils@
+
+composerWithPluginConfigureHook() {
+    echo "Executing composerWithPluginConfigureHook"
+
+    mkdir -p $out
+
+    export COMPOSER_HOME=$out
+
+    if [[ -e "$composerLock" ]]; then
+        cp $composerLock $out/composer.lock
+    fi
+
+    cp $composerJson $out/composer.json
+    cp -ar $src $out/src
+
+    if [[ ! -f "$out/composer.lock" ]]; then
+        setComposeRootVersion
+
+        composer \
+            global \
+            --no-install \
+            --no-interaction \
+            --no-progress \
+            ${composerNoDev:+--no-dev} \
+            ${composerNoPlugins:+--no-plugins} \
+            ${composerNoScripts:+--no-scripts} \
+            update
+
+        echo
+        echo -e "\e[31mERROR: No composer.lock found\e[0m"
+        echo
+        echo -e '\e[31mNo composer.lock file found, consider adding one to your repository to ensure reproducible builds.\e[0m'
+        echo -e "\e[31mIn the meantime, a composer.lock file has been generated for you in $out/composer.lock\e[0m"
+        echo
+        echo -e '\e[31mTo fix the issue:\e[0m'
+        echo -e "\e[31m1. Copy the composer.lock file from $out/composer.lock to the project's source:\e[0m"
+        echo -e "\e[31m  cp $out/composer.lock <path>\e[0m"
+        echo -e '\e[31m2. Add the composerLock attribute, pointing to the copied composer.lock file:\e[0m'
+        echo -e '\e[31m  composerLock = ./composer.lock;\e[0m'
+        echo
+
+        exit 1
+    fi
+
+    echo "Finished composerWithPluginConfigureHook"
+}
+
+composerWithPluginBuildHook() {
+    echo "Executing composerWithPluginBuildHook"
+
+    echo "Finished composerWithPluginBuildHook"
+}
+
+composerWithPluginCheckHook() {
+    echo "Executing composerWithPluginCheckHook"
+
+    checkComposerValidate
+
+    echo "Finished composerWithPluginCheckHook"
+}
+
+composerWithPluginInstallHook() {
+    echo "Executing composerWithPluginInstallHook"
+
+    composer \
+        global \
+        --no-interaction \
+        --no-progress \
+        ${composerNoDev:+--no-dev} \
+        ${composerNoPlugins:+--no-plugins} \
+        ${composerNoScripts:+--no-scripts} \
+        install
+
+    echo "Finished composerWithPluginInstallHook"
+}
+
+composerWithPluginInstallCheckHook() {
+    composer global show $pluginName
+}
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/default.nix b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/default.nix
index 4c0ba1b18801..d10ff7806727 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/default.nix
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/default.nix
@@ -42,4 +42,19 @@ in
       phpScriptUtils = lib.getExe php-script-utils;
     };
   } ./composer-install-hook.sh;
+
+  composerWithPluginVendorHook = makeSetupHook {
+    name = "composer-with-plugin-vendor-hook.sh";
+    propagatedBuildInputs = [
+      jq
+      moreutils
+      cacert
+    ];
+    substitutions = {
+      # Specify the stdenv's `diff` by abspath to ensure that the user's build
+      # inputs do not cause us to find the wrong `diff`.
+      cmp = "${lib.getBin buildPackages.diffutils}/bin/cmp";
+      phpScriptUtils = lib.getExe php-script-utils;
+    };
+  } ./composer-with-plugin-vendor-hook.sh;
 }
diff --git a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/php-script-utils.bash b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/php-script-utils.bash
index bba0242e65d1..65c0a3b410f6 100644
--- a/nixpkgs/pkgs/build-support/php/builders/v1/hooks/php-script-utils.bash
+++ b/nixpkgs/pkgs/build-support/php/builders/v1/hooks/php-script-utils.bash
@@ -1,5 +1,6 @@
 declare version
 declare composerStrictValidation
+declare composerGlobal
 
 setComposeRootVersion() {
     set +e # Disable exit on error
@@ -13,7 +14,16 @@ setComposeRootVersion() {
 }
 
 checkComposerValidate() {
-    if ! composer validate --strict --no-ansi --no-interaction --quiet --no-check-all --no-check-lock; then
+    setComposeRootVersion
+
+    if [ "1" == "${composerGlobal-}" ]; then
+      global="global";
+    else
+      global="";
+    fi
+
+    command="composer ${global} validate --strict --quiet --no-interaction --no-check-all --no-check-lock"
+    if ! $command; then
         if [ "1" == "${composerStrictValidation-}" ]; then
             echo
             echo -e "\e[31mERROR: composer files validation failed\e[0m"
@@ -42,7 +52,8 @@ checkComposerValidate() {
         fi
     fi
 
-    if ! composer validate --strict --no-ansi --no-interaction --quiet --no-check-all --check-lock; then
+    command="composer ${global} validate --strict --no-ansi --no-interaction --quiet --no-check-all --check-lock"
+    if ! $command; then
         if [ "1" == "${composerStrictValidation-}" ]; then
             echo
             echo -e "\e[31mERROR: composer files validation failed\e[0m"
diff --git a/nixpkgs/pkgs/build-support/php/pkgs/composer-local-repo-plugin.nix b/nixpkgs/pkgs/build-support/php/pkgs/composer-local-repo-plugin.nix
deleted file mode 100644
index 601640b6f0bb..000000000000
--- a/nixpkgs/pkgs/build-support/php/pkgs/composer-local-repo-plugin.nix
+++ /dev/null
@@ -1,116 +0,0 @@
-{
-  php,
-  callPackage,
-  stdenvNoCC,
-  lib,
-  fetchFromGitHub,
-  makeBinaryWrapper,
-}:
-
-let
-  composer = callPackage ./composer-phar.nix { inherit (php.packages.composer) version pharHash; };
-
-  composerKeys = stdenvNoCC.mkDerivation (finalComposerKeysAttrs: {
-    pname = "composer-keys";
-    version = "fa5a62092f33e094073fbda23bbfc7188df3cbc5";
-
-    src = fetchFromGitHub {
-      owner = "composer";
-      repo = "composer.github.io";
-      rev = "${finalComposerKeysAttrs.version}";
-      hash = "sha256-3Sfn71LDG1jHwuEIU8iEnV3k6D6QTX7KVIKVaNSuCVE=";
-    };
-
-    installPhase = ''
-      runHook preInstall
-
-      mkdir -p $out
-      install releases.pub $out/keys.tags.pub
-      install snapshots.pub $out/keys.dev.pub
-
-      runHook postInstall
-    '';
-  });
-in
-stdenvNoCC.mkDerivation (finalAttrs: {
-  pname = "composer-local-repo-plugin";
-  version = "1.1.0";
-
-  src = fetchFromGitHub {
-    owner = "nix-community";
-    repo = "composer-local-repo-plugin";
-    rev = finalAttrs.version;
-    hash = "sha256-edbn07r/Uc1g0qOuVBZBs6N1bMN5kIfA1b4FCufdw5M=";
-  };
-
-  env = {
-    COMPOSER_CACHE_DIR = "/dev/null";
-    COMPOSER_MIRROR_PATH_REPOS = "1";
-    COMPOSER_HTACCESS_PROTECT = "0";
-    COMPOSER_DISABLE_NETWORK = "1";
-  };
-
-  nativeBuildInputs = [ makeBinaryWrapper ];
-
-  buildInputs = [ composer ];
-
-  configurePhase = ''
-    runHook preConfigure
-
-    export COMPOSER_HOME=${placeholder "out"}
-
-    runHook postConfigure
-  '';
-
-  buildPhase = ''
-    runHook preBuild
-
-    # Configure composer globally
-    composer global init --quiet --no-interaction --no-ansi \
-      --name="nixos/composer" \
-      --homepage "https://nixos.org/" \
-      --description "Composer with nix-community/composer-local-repo-plugin" \
-      --license "MIT"
-
-    composer global config --quiet minimum-stability dev
-    composer global config --quiet prefer-stable true
-    composer global config --quiet apcu-autoloader false
-    composer global config --quiet allow-plugins.nix-community/composer-local-repo-plugin true
-    composer global config --quiet repo.packagist false
-    composer global config --quiet repo.plugin path $src
-
-    # Install the local repository plugin
-    composer global require --quiet --no-ansi --no-interaction nix-community/composer-local-repo-plugin
-
-    runHook postBuild
-  '';
-
-  checkPhase = ''
-    runHook preCheck
-
-    composer global validate --no-ansi
-    composer global show --no-ansi nix-community/composer-local-repo-plugin
-
-    runHook postCheck
-  '';
-
-  installPhase = ''
-    runHook preInstall
-
-    mkdir -p $out
-    cp -ar ${composerKeys}/* $out/
-
-    makeWrapper ${composer}/bin/composer $out/bin/composer-local-repo-plugin \
-      --prefix COMPOSER_HOME : $out
-
-    runHook postInstall
-  '';
-
-  meta = {
-    description = "Composer local repo plugin for Composer";
-    homepage = "https://github.com/nix-community/composer-local-repo-plugin";
-    license = lib.licenses.mit;
-    maintainers = with lib.maintainers; [ drupol ];
-    platforms = lib.platforms.all;
-  };
-})
diff --git a/nixpkgs/pkgs/build-support/php/pkgs/composer-phar.nix b/nixpkgs/pkgs/build-support/php/pkgs/composer-phar.nix
index d278810091ef..b07c25beec55 100644
--- a/nixpkgs/pkgs/build-support/php/pkgs/composer-phar.nix
+++ b/nixpkgs/pkgs/build-support/php/pkgs/composer-phar.nix
@@ -1,6 +1,5 @@
 {
   _7zz,
-  cacert,
   curl,
   fetchurl,
   git,
@@ -37,7 +36,6 @@ stdenvNoCC.mkDerivation (finalAttrs: {
       --prefix PATH : ${
         lib.makeBinPath [
           _7zz
-          cacert
           curl
           git
           unzip
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/build-crate.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/build-crate.nix
index bbb26606a6a4..7484b3ad0290 100644
--- a/nixpkgs/pkgs/build-support/rust/build-rust-crate/build-crate.nix
+++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/build-crate.nix
@@ -51,7 +51,7 @@
     # configure & source common build functions
     LIB_RUSTC_OPTS="${libRustcOpts}"
     BIN_RUSTC_OPTS="${binRustcOpts}"
-    LIB_EXT="${stdenv.hostPlatform.extensions.sharedLibrary or ""}"
+    LIB_EXT="${stdenv.hostPlatform.extensions.library}"
     LIB_PATH="${libPath}"
     LIB_NAME="${libName}"
 
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/configure-crate.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/configure-crate.nix
index 6b8827160262..4077ee5ced8e 100644
--- a/nixpkgs/pkgs/build-support/rust/build-rust-crate/configure-crate.nix
+++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/configure-crate.nix
@@ -224,7 +224,7 @@ in ''
 
      CRATENAME=$(echo ${crateName} | sed -e "s/\(.*\)-sys$/\U\1/" -e "s/-/_/g")
      grep -P "^cargo:(?!(rustc-|warning=|rerun-if-changed=|rerun-if-env-changed))" target/build/${crateName}.opt \
-       | awk -F= "/^cargo:/ { sub(/^cargo:/, \"\", \$1); gsub(/-/, \"_\", \$1); print \"export \" toupper(\"DEP_$(echo $CRATENAME)_\" \$1) \"=\" \$2 }" > target/env
+       | awk -F= "/^cargo:/ { sub(/^cargo:/, \"\", \$1); gsub(/-/, \"_\", \$1); print \"export \" toupper(\"DEP_$(echo $CRATENAME)_\" \$1) \"=\" \"\\\"\"\$2\"\\\"\" }" > target/env
      set -e
   fi
   runHook postConfigure
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix
index 4a7fd114829a..dfe28cc334b5 100644
--- a/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix
+++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix
@@ -49,7 +49,9 @@ let
           filename =
             if lib.any (x: x == "lib" || x == "rlib") dep.crateType
             then "${dep.metadata}.rlib"
-            else "${dep.metadata}${stdenv.hostPlatform.extensions.sharedLibrary}";
+            # Adjust lib filename for crates of type proc-macro. Proc macros are compiled/run on the build platform architecture.
+            else if (lib.attrByPath [ "procMacro" ] false dep) then "${dep.metadata}${stdenv.buildPlatform.extensions.library}"
+            else "${dep.metadata}${stdenv.hostPlatform.extensions.library}";
         in
         " --extern ${opts}${name}=${dep.lib}/lib/lib${extern}-${filename}"
       )
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/install-crate.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/install-crate.nix
index f4a4dcdb0d94..7c7c102833d8 100644
--- a/nixpkgs/pkgs/build-support/rust/build-rust-crate/install-crate.nix
+++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/install-crate.nix
@@ -41,7 +41,7 @@ if !buildTests then ''
   fi
   if [ -e target/lib ]; then
     find target/lib/ -type f \! -name '*.rlib' \
-      -a \! -name '*${stdenv.hostPlatform.extensions.sharedLibrary}' \
+      -a \! -name '*${stdenv.hostPlatform.extensions.library}' \
       -a \! -name '*.d' \
       -executable \
       -print0 | xargs --no-run-if-empty --null install --target $out/tests;
diff --git a/nixpkgs/pkgs/build-support/rust/build-rust-crate/test/default.nix b/nixpkgs/pkgs/build-support/rust/build-rust-crate/test/default.nix
index 1ecef4c8e327..522eedfede7f 100644
--- a/nixpkgs/pkgs/build-support/rust/build-rust-crate/test/default.nix
+++ b/nixpkgs/pkgs/build-support/rust/build-rust-crate/test/default.nix
@@ -479,7 +479,7 @@ let
             # `-undefined dynamic_lookup` as otherwise the compilation fails.
             $CC -shared \
               ${lib.optionalString stdenv.isDarwin "-undefined dynamic_lookup"} \
-              -o $out/lib/${name}${stdenv.hostPlatform.extensions.sharedLibrary} ${src}
+              -o $out/lib/${name}${stdenv.hostPlatform.extensions.library} ${src}
           '';
           b = compile "libb" ''
             #include <stdio.h>
diff --git a/nixpkgs/pkgs/build-support/rust/hooks/cargo-build-hook.sh b/nixpkgs/pkgs/build-support/rust/hooks/cargo-build-hook.sh
index 0ac9bf51a833..26dde914f22a 100644
--- a/nixpkgs/pkgs/build-support/rust/hooks/cargo-build-hook.sh
+++ b/nixpkgs/pkgs/build-support/rust/hooks/cargo-build-hook.sh
@@ -39,7 +39,7 @@ cargoBuildHook() {
     set -x
     @setEnv@ cargo build -j $NIX_BUILD_CORES \
         --target @rustHostPlatformSpec@ \
-        --frozen \
+        --offline \
         ${cargoBuildProfileFlag} \
         ${cargoBuildNoDefaultFeaturesFlag} \
         ${cargoBuildFeaturesFlag} \
diff --git a/nixpkgs/pkgs/build-support/rust/hooks/cargo-check-hook.sh b/nixpkgs/pkgs/build-support/rust/hooks/cargo-check-hook.sh
index 971a140ec178..96b87dbf15b4 100644
--- a/nixpkgs/pkgs/build-support/rust/hooks/cargo-check-hook.sh
+++ b/nixpkgs/pkgs/build-support/rust/hooks/cargo-check-hook.sh
@@ -29,7 +29,7 @@ cargoCheckHook() {
     fi
 
     argstr="${cargoCheckProfileFlag} ${cargoCheckNoDefaultFeaturesFlag} ${cargoCheckFeaturesFlag}
-        --target @rustHostPlatformSpec@ --frozen ${cargoTestFlags}"
+        --target @rustHostPlatformSpec@ --offline ${cargoTestFlags}"
 
     (
         set -x
diff --git a/nixpkgs/pkgs/build-support/rust/hooks/cargo-nextest-hook.sh b/nixpkgs/pkgs/build-support/rust/hooks/cargo-nextest-hook.sh
index 29ba18a6a1e3..16d32513a0d0 100644
--- a/nixpkgs/pkgs/build-support/rust/hooks/cargo-nextest-hook.sh
+++ b/nixpkgs/pkgs/build-support/rust/hooks/cargo-nextest-hook.sh
@@ -29,7 +29,7 @@ cargoNextestHook() {
     fi
 
     argstr="${cargoCheckProfileFlag} ${cargoCheckNoDefaultFeaturesFlag} ${cargoCheckFeaturesFlag}
-        --target @rustHostPlatformSpec@ --frozen ${cargoTestFlags}"
+        --target @rustHostPlatformSpec@ --offline ${cargoTestFlags}"
 
     (
         set -x
diff --git a/nixpkgs/pkgs/build-support/rust/hooks/maturin-build-hook.sh b/nixpkgs/pkgs/build-support/rust/hooks/maturin-build-hook.sh
index 028441d18160..b3cc1ced7964 100644
--- a/nixpkgs/pkgs/build-support/rust/hooks/maturin-build-hook.sh
+++ b/nixpkgs/pkgs/build-support/rust/hooks/maturin-build-hook.sh
@@ -11,7 +11,7 @@ maturinBuildHook() {
     set -x
     @setEnv@ maturin build \
         --jobs=$NIX_BUILD_CORES \
-        --frozen \
+        --offline \
         --target @rustTargetPlatformSpec@ \
         --manylinux off \
         --strip \
diff --git a/nixpkgs/pkgs/build-support/setup-hooks/strip.sh b/nixpkgs/pkgs/build-support/setup-hooks/strip.sh
index ce41e6ea0562..49a350af1fa5 100644
--- a/nixpkgs/pkgs/build-support/setup-hooks/strip.sh
+++ b/nixpkgs/pkgs/build-support/setup-hooks/strip.sh
@@ -74,13 +74,17 @@ stripDirs() {
         echo "stripping (with command $cmd and flags $stripFlags) in $paths"
         local striperr
         striperr="$(mktemp --tmpdir="$TMPDIR" 'striperr.XXXXXX')"
-        # Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
-        find $paths -type f "${excludeFlags[@]}" -a '!' -path "$prefix/lib/debug/*" -print0 |
-            # Make sure we process files under symlinks only once. Otherwise
-            # 'strip` can corrupt files when writes to them in parallel:
-            #   https://github.com/NixOS/nixpkgs/issues/246147#issuecomment-1657072039
-            xargs -r -0 -n1 -- realpath -z | sort -u -z |
+        # Make sure we process files only once. `strip`ping the same file through different
+        # links in parallel can corrupt it:
+        #   https://github.com/NixOS/nixpkgs/issues/246147#issuecomment-1657072039
 
+        # Do not strip lib/debug. This is a directory used by setup-hooks/separate-debug-info.sh.
+        # Print out each file's device and inode (which will be the same if two files are hardlinked
+        # or are the same file found through different symlinks), followed by its path...
+        find $paths -type f "${excludeFlags[@]}" -a '!' -path "$prefix/lib/debug/*" -printf '%D-%i,%p\0' |
+            # ... sort/uniq by device/inode, then cut them out and keep the path, ...
+            sort -t, -k1,1 -u -z | cut -d, -f2- -z |
+            # and finally strip each unique path in parallel.
             xargs -r -0 -n1 -P "$NIX_BUILD_CORES" -- $cmd $stripFlags 2>"$striperr" || exit_code=$?
         # xargs exits with status code 123 if some but not all of the
         # processes fail. We don't care if some of the files couldn't
diff --git a/nixpkgs/pkgs/build-support/singularity-tools/default.nix b/nixpkgs/pkgs/build-support/singularity-tools/default.nix
index c9e53a4cb706..cd10a9960421 100644
--- a/nixpkgs/pkgs/build-support/singularity-tools/default.nix
+++ b/nixpkgs/pkgs/build-support/singularity-tools/default.nix
@@ -45,7 +45,7 @@ rec {
     , diskSize ? 1024
     , runScript ? "#!${stdenv.shell}\nexec /bin/sh"
     , runAsRoot ? null
-    , memSize ? 512
+    , memSize ? 1024
     , singularity ? defaultSingularity
     }:
     let
diff --git a/nixpkgs/pkgs/build-support/src-only/default.nix b/nixpkgs/pkgs/build-support/src-only/default.nix
index 2b0db0e267aa..cd8572629cad 100644
--- a/nixpkgs/pkgs/build-support/src-only/default.nix
+++ b/nixpkgs/pkgs/build-support/src-only/default.nix
@@ -13,7 +13,7 @@ let
 in
 stdenv.mkDerivation (args // {
   name = "${name}-source";
-  installPhase = "cp -r . $out";
+  installPhase = "cp -pr --reflink=auto -- . $out";
   outputs = [ "out" ];
   separateDebugInfo = false;
   dontUnpack = false;
diff --git a/nixpkgs/pkgs/build-support/testers/default.nix b/nixpkgs/pkgs/build-support/testers/default.nix
index 362622d9b7ee..dbf9a6d6cb05 100644
--- a/nixpkgs/pkgs/build-support/testers/default.nix
+++ b/nixpkgs/pkgs/build-support/testers/default.nix
@@ -1,6 +1,10 @@
 { pkgs, pkgsLinux, buildPackages, lib, callPackage, runCommand, stdenv, substituteAll, testers }:
 # Documentation is in doc/builders/testers.chapter.md
 {
+  # See https://nixos.org/manual/nixpkgs/unstable/#tester-lycheeLinkCheck
+  # or doc/builders/testers.chapter.md
+  inherit (callPackage ./lychee.nix {}) lycheeLinkCheck;
+
   # See https://nixos.org/manual/nixpkgs/unstable/#tester-testBuildFailure
   # or doc/builders/testers.chapter.md
   testBuildFailure = drv: drv.overrideAttrs (orig: {
diff --git a/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tester.nix b/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tester.nix
index b8ae884ba7b0..3d4ded812f75 100644
--- a/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tester.nix
+++ b/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tester.nix
@@ -14,7 +14,7 @@ runCommand testName {
     buildInputs = [ package ];
     inherit moduleNames version versionCheck;
     meta = {
-      description = "Test whether ${package.name} exposes pkg-config modules ${lib.concatStringsSep ", " moduleNames}.";
+      description = "Test whether ${package.name} exposes pkg-config modules ${lib.concatStringsSep ", " moduleNames}";
     }
     # Make sure licensing info etc is preserved, as this is a concern for e.g. cache.nixos.org,
     # as hydra can't check this meta info in dependencies.
@@ -45,7 +45,7 @@ runCommand testName {
         if [[ "$moduleVersion" == "$version" ]]; then
           echo "✅ pkg-config module $moduleName exists and has version $moduleVersion"
         else
-          echo "❌ pkg-config module $moduleName exists and has version $moduleVersion when $version was expected"
+          echo "${if versionCheck then "❌" else "ℹ️"} pkg-config module $moduleName exists at version $moduleVersion != $version (drv version)"
           ((versionMismatch+=1))
         fi
         printf '%s\t%s\n' "$moduleName" "$version" >> "$out"
@@ -55,7 +55,7 @@ runCommand testName {
       fi
     done
 
-    if [[ $notFound -eq 0 ]] && ([[ $versionMismatch -eq 0 ]] || [[ "$versionCheck" == false ]]); then
+    if [[ $notFound -eq 0 ]] && ([[ $versionMismatch -eq 0 ]] || [[ -z "$versionCheck" ]]); then
       exit 0
     fi
     if [[ $notFound -ne 0 ]]; then
diff --git a/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tests.nix b/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tests.nix
index bf992d040b4b..adad93507984 100644
--- a/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tests.nix
+++ b/nixpkgs/pkgs/build-support/testers/hasPkgConfigModules/tests.nix
@@ -11,10 +11,15 @@ lib.recurseIntoAttrs {
 
   miniz-versions-mismatch = testers.testBuildFailure (testers.hasPkgConfigModules {
     package = miniz;
-    version = "1.2.3";
+    version = "1.2.3";  # Deliberately-incorrect version number
     versionCheck = true;
   });
 
+  miniz-no-versionCheck = testers.hasPkgConfigModules {
+    package = miniz;
+    version = "1.2.3";  # Deliberately-incorrect version number
+  };
+
   zlib-has-zlib = testers.hasPkgConfigModules {
     package = zlib;
     moduleNames = [ "zlib" ];
diff --git a/nixpkgs/pkgs/build-support/testers/lychee.nix b/nixpkgs/pkgs/build-support/testers/lychee.nix
new file mode 100644
index 000000000000..80088dc4f963
--- /dev/null
+++ b/nixpkgs/pkgs/build-support/testers/lychee.nix
@@ -0,0 +1,69 @@
+deps@{ formats, lib, lychee, stdenv, writeShellApplication }:
+let
+  inherit (lib) mapAttrsToList throwIf;
+  inherit (lib.strings) hasInfix hasPrefix escapeNixString;
+
+  toURL = v:
+    let s = "${v}";
+    in if hasPrefix builtins.storeDir s
+    then # lychee requires that paths on the file system are prefixed with file://
+      "file://${s}"
+    else s;
+
+  withCheckedName = name:
+    throwIf (hasInfix " " name) ''
+      lycheeLinkCheck: remap patterns must not contain spaces.
+      A space marks the end of the regex in lychee.toml.
+
+      Please change attribute name 'remap.${escapeNixString name}'
+    '';
+
+  # See https://nixos.org/manual/nixpkgs/unstable/#tester-lycheeLinkCheck
+  # or doc/builders/testers.chapter.md
+  lycheeLinkCheck = {
+    site,
+    remap ? { },
+    lychee ? deps.lychee,
+    extraConfig ? { },
+  }:
+    stdenv.mkDerivation (finalAttrs: {
+      name = "lychee-link-check";
+      inherit site;
+      nativeBuildInputs = [ finalAttrs.passthru.lychee ];
+      configFile = (formats.toml {}).generate "lychee.toml" finalAttrs.passthru.config;
+
+      # These can be overriden with overrideAttrs if needed.
+      passthru = {
+        inherit lychee remap;
+        config = {
+          include_fragments = true;
+        } // lib.optionalAttrs (finalAttrs.passthru.remap != { }) {
+          remap =
+            mapAttrsToList
+              (name: value: withCheckedName name "${name} ${toURL value}")
+              finalAttrs.passthru.remap;
+        } // extraConfig;
+        online = writeShellApplication {
+          name = "run-lychee-online";
+          runtimeInputs = [ finalAttrs.passthru.lychee ];
+          # Comment out to run shellcheck:
+          checkPhase = "";
+          text = ''
+            site=${finalAttrs.site}
+            configFile=${finalAttrs.configFile}
+            echo Checking links on $site
+            exec lychee --config $configFile $site "$@"
+          '';
+        };
+      };
+      buildCommand = ''
+        echo Checking internal links on $site
+        lychee --offline --config $configFile $site
+        touch $out
+      '';
+    });
+
+in
+{
+  inherit lycheeLinkCheck;
+}
diff --git a/nixpkgs/pkgs/build-support/testers/test/default.nix b/nixpkgs/pkgs/build-support/testers/test/default.nix
index da67711156be..a815fe63e416 100644
--- a/nixpkgs/pkgs/build-support/testers/test/default.nix
+++ b/nixpkgs/pkgs/build-support/testers/test/default.nix
@@ -12,6 +12,8 @@ let
 
 in
 lib.recurseIntoAttrs {
+  lycheeLinkCheck = lib.recurseIntoAttrs pkgs.lychee.tests;
+
   hasPkgConfigModules = pkgs.callPackage ../hasPkgConfigModules/tests.nix { };
 
   runNixOSTest-example = pkgs-with-overlay.testers.runNixOSTest ({ lib, ... }: {
diff --git a/nixpkgs/pkgs/build-support/trivial-builders/test/references/default.nix b/nixpkgs/pkgs/build-support/trivial-builders/test/references/default.nix
index 928cc1d9461f..85df57ea210c 100644
--- a/nixpkgs/pkgs/build-support/trivial-builders/test/references/default.nix
+++ b/nixpkgs/pkgs/build-support/trivial-builders/test/references/default.nix
@@ -93,21 +93,27 @@ let
     };
   });
 in
-testers.nixosTest {
-  name = "nixpkgs-trivial-builders";
-  nodes.machine = { ... }: {
+testers.runNixOSTest ({ config, lib, ... }:
+let
+  # Use the testScriptBin from guest pkgs.
+  # The attribute path to access the guest version of testScriptBin is
+  # tests.trivial-builders.references.config.node.pkgs.tests.trivial-builders.references.testScriptBin
+  # which is why passthru.guestTestScriptBin is provided.
+  guestTestScriptBin = config.node.pkgs.tests.trivial-builders.references.testScriptBin;
+in
+{
+  name = "nixpkgs-trivial-builders-references";
+  nodes.machine = { config, lib, pkgs, ... }: {
     virtualisation.writableStore = true;
 
     # Test runs without network, so we don't substitute and prepare our deps
     nix.settings.substituters = lib.mkForce [ ];
-    environment.etc."pre-built-paths".source = writeText "pre-built-paths" (
-      builtins.toJSON [ testScriptBin ]
-    );
+    system.extraDependencies = [ guestTestScriptBin ];
   };
   testScript =
     ''
       machine.succeed("""
-        ${lib.getExe testScriptBin} 2>/dev/console
+        ${lib.getExe guestTestScriptBin} 2>/dev/console
       """)
     '';
   passthru = {
@@ -118,6 +124,7 @@ testers.nixosTest {
       samples
       testScriptBin
       ;
+    inherit guestTestScriptBin;
   };
   meta = {
     maintainers = with lib.maintainers; [
@@ -125,4 +132,4 @@ testers.nixosTest {
       ShamrockLee
     ];
   };
-}
+})
diff --git a/nixpkgs/pkgs/build-support/writers/scripts.nix b/nixpkgs/pkgs/build-support/writers/scripts.nix
index 06d763ca9d6a..93fac09c07ba 100644
--- a/nixpkgs/pkgs/build-support/writers/scripts.nix
+++ b/nixpkgs/pkgs/build-support/writers/scripts.nix
@@ -635,7 +635,7 @@ rec {
 
     nuget-source = mkNugetSource {
       name = "${fname}-nuget-source";
-      description = "A Nuget source with the dependencies for ${fname}";
+      description = "Nuget source with the dependencies for ${fname}";
       deps = [ _nugetDeps ];
     };