treewide: remove FHSEnv

To remove the FHSenv, we have to patch rules_python to use
/usr/bin/env to resolve the path to bash instead of hardcoding it.
Additionally, we now bring a Nix-compatible Bazel 8.

Change-Id: Id51e7748eea6dd77185f43a52fe45b5110ba4a2b
Reviewed-on: https://review.monogon.dev/c/monogon/+/4427
Tested-by: Jenkins CI
Reviewed-by: Jan Schär <jan@monogon.tech>
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/third_party/nix/pkgs/bazel/BUILD.bazel b/third_party/nix/pkgs/bazel/BUILD.bazel
deleted file mode 100644
index e69de29..0000000
--- a/third_party/nix/pkgs/bazel/BUILD.bazel
+++ /dev/null
diff --git a/third_party/nix/pkgs/bazel/bazel-inner.sh b/third_party/nix/pkgs/bazel/bazel-inner.sh
deleted file mode 100755
index 7b978bc..0000000
--- a/third_party/nix/pkgs/bazel/bazel-inner.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/bash
-function get_workspace_root() {
-  workspace_dir="${PWD}"
-  while [[ "${workspace_dir}" != / ]]; do
-    if [[ -e "${workspace_dir}/WORKSPACE" || -e "${workspace_dir}/WORKSPACE.bazel" || -e "${workspace_dir}/MODULE.bazel" ]]; then
-      readonly workspace_dir
-      return
-    fi
-    workspace_dir="$(dirname "${workspace_dir}")"
-  done
-  readonly workspace_dir=""
-}
-
-get_workspace_root
-readonly wrapper="${workspace_dir}/tools/bazel"
-if [ -f "${wrapper}" ]; then
-  exec -a "$0" "${wrapper}" "$@"
-fi
-exec -a "$0" "${BAZEL_REAL}" "$@"
diff --git a/third_party/nix/pkgs/bazel/default.nix b/third_party/nix/pkgs/bazel/default.nix
deleted file mode 100644
index c181db3..0000000
--- a/third_party/nix/pkgs/bazel/default.nix
+++ /dev/null
@@ -1,24 +0,0 @@
-{ pkgs }: with pkgs;
-stdenv.mkDerivation {
-  name = "bazel";
-  src = builtins.fetchurl {
-    url = "https://github.com/bazelbuild/bazel/releases/download/8.3.1/bazel-8.3.1-linux-x86_64";
-    sha256 = "0k3067d06b8160day48afskr42c41bz0qgb3pk9mjpr4hj57w90p";
-  };
-  unpackPhase = ''
-    true
-  '';
-  nativeBuildInputs = [ makeWrapper ];
-  buildPhase = ''
-    mkdir -p $out/bin
-    cp $src $out/bin/.bazel-inner
-    chmod +x $out/bin/.bazel-inner
-
-    cp ${./bazel-inner.sh} $out/bin/bazel
-    chmod +x $out/bin/bazel
-
-    # Use wrapProgram to set the actual bazel path
-    wrapProgram $out/bin/bazel --set BAZEL_REAL $out/bin/.bazel-inner
-  '';
-  dontStrip = true;
-}
diff --git a/third_party/nix/pkgs/bazel_8/LICENSE b/third_party/nix/pkgs/bazel_8/LICENSE
new file mode 100644
index 0000000..6d47368
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2003-2025 Eelco Dolstra and the Nixpkgs/NixOS contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/third_party/nix/pkgs/bazel_8/README.md b/third_party/nix/pkgs/bazel_8/README.md
new file mode 100644
index 0000000..000f0b8
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/README.md
@@ -0,0 +1,6 @@
+# Bazel 8 Nix Package
+
+This build is based on https://github.com/NixOS/nixpkgs/pull/400941, the only
+difference being the addition of `bash` to the default shell environment. As 
+soon as the PR is merged, we can replace it with a small override to inject
+bash into the dependencies.
\ No newline at end of file
diff --git a/third_party/nix/pkgs/bazel_8/bazel-execlog.sh b/third_party/nix/pkgs/bazel_8/bazel-execlog.sh
new file mode 100644
index 0000000..7a6fd6c
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/bazel-execlog.sh
@@ -0,0 +1,3 @@
+#!@runtimeShell@
+
+exec @binJava@ -jar @out@/share/parser_deploy.jar "$@"
diff --git a/third_party/nix/pkgs/bazel_8/build-support/bazelDerivation.nix b/third_party/nix/pkgs/bazel_8/build-support/bazelDerivation.nix
new file mode 100644
index 0000000..0d6b3ca
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/build-support/bazelDerivation.nix
@@ -0,0 +1,72 @@
+{ stdenv
+, lndir
+, lib
+,
+}:
+
+args@{ bazel
+, registry ? null
+, bazelRepoCache ? null
+, bazelVendorDeps ? null
+, startupArgs ? [ ]
+, commandArgs ? [ ]
+, bazelPreBuild ? ""
+, bazelPostBuild ? ""
+, serverJavabase ? null
+, targets
+, command
+, ...
+}:
+
+stdenv.mkDerivation (
+  {
+    preBuildPhases = [ "preBuildPhase" ];
+    preBuildPhase =
+      (lib.optionalString (bazelRepoCache != null) ''
+        # repo_cache needs to be writeable even in air-gapped builds
+        mkdir repo_cache
+        ${lndir}/bin/lndir -silent ${bazelRepoCache}/repo_cache repo_cache
+      '')
+
+      + (lib.optionalString (bazelVendorDeps != null) ''
+        mkdir vendor_dir
+        ${lndir}/bin/lndir -silent ${bazelVendorDeps}/vendor_dir vendor_dir
+
+        # pin all deps to avoid re-fetch attempts by Bazel
+        rm vendor_dir/VENDOR.bazel
+        find vendor_dir -maxdepth 1 -type d -printf "pin(\"@@%P\")\n" > vendor_dir/VENDOR.bazel
+      '')
+      # keep preBuildPhase always defined as it is listed in preBuildPhases
+      + ''
+        true
+      '';
+    buildPhase = ''
+      runHook preBuild
+
+      export HOME=$(mktemp -d)
+
+      ${bazelPreBuild}
+
+      ${bazel}/bin/bazel ${
+        lib.strings.concatStringsSep " " (
+          lib.optional (serverJavabase != null) "--server_javabase=${serverJavabase}"
+          ++ [ "--batch" ]
+          ++ startupArgs
+        )
+      } ${command} ${
+        lib.strings.concatStringsSep " " (
+          lib.optional (registry != null) "--registry=file://${registry}"
+          ++ lib.optional (bazelRepoCache != null) "--repository_cache=repo_cache"
+          ++ lib.optional (bazelVendorDeps != null) "--vendor_dir=vendor_dir"
+          ++ commandArgs
+        )
+      } ${lib.strings.concatStringsSep " " targets}
+
+      ${bazelPostBuild}
+
+      runHook postBuild
+    '';
+
+  }
+    // args
+)
diff --git a/third_party/nix/pkgs/bazel_8/build-support/bazelPackage.nix b/third_party/nix/pkgs/bazel_8/build-support/bazelPackage.nix
new file mode 100644
index 0000000..ee33b6e
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/build-support/bazelPackage.nix
@@ -0,0 +1,164 @@
+{ callPackage
+, gnugrep
+, lib
+, autoPatchelfHook
+, stdenv
+,
+}:
+
+{ name
+, src
+, sourceRoot ? null
+, version ? null
+, targets
+, bazel
+, startupArgs ? [ ]
+, commandArgs ? [ ]
+, env ? { }
+, serverJavabase ? null
+, registry ? null
+, bazelRepoCacheFOD ? {
+    outputHash = null;
+    outputHashAlgo = "sha256";
+  }
+, bazelVendorDepsFOD ? {
+    outputHash = null;
+    outputHashAlgo = "sha256";
+  }
+, installPhase
+, buildInputs ? [ ]
+, nativeBuildInputs ? [ ]
+, autoPatchelfIgnoreMissingDeps ? null
+,
+}:
+let
+  # FOD produced by `bazel fetch`
+  # Repo cache contains content-addressed external Bazel dependencies without any patching
+  # Potentially this can be nixified via --experimental_repository_resolved_file
+  # (Note: file itself isn't reproducible because it has lots of extra info and order
+  #        isn't stable too. Parsing it into nix fetch* commands isn't trivial but might be possible)
+  bazelRepoCache =
+    if bazelRepoCacheFOD.outputHash == null then
+      null
+    else
+      (callPackage ./bazelDerivation.nix { } {
+        name = "bazelRepoCache";
+        inherit (bazelRepoCacheFOD) outputHash outputHashAlgo;
+        inherit
+          src
+          version
+          sourceRoot
+          env
+          buildInputs
+          nativeBuildInputs
+          ;
+        inherit registry;
+        inherit
+          bazel
+          targets
+          startupArgs
+          serverJavabase
+          ;
+        command = "fetch";
+        outputHashMode = "recursive";
+        commandArgs = [ "--repository_cache repo_cache" ] ++ commandArgs;
+        bazelPreBuild = ''
+          mkdir repo_cache
+        '';
+        installPhase = ''
+          mkdir -p $out/repo_cache
+          cp -r --reflink=auto repo_cache/* $out/repo_cache
+        '';
+      });
+  # Stage1: FOD produced by `bazel vendor`, Stage2: eventual patchelf or other tuning
+  # Vendor deps contains unpacked&patches external dependencies, this may need Nix-specific
+  # patching to address things like
+  # - broken symlinks
+  # - symlinks or other references to absolute nix store paths which isn't allowed for FOD
+  # - autoPatchelf for externally-fetched binaries
+  #
+  # Either repo cache or vendor deps should be enough to build a given package
+  bazelVendorDeps =
+    if bazelVendorDepsFOD.outputHash == null then
+      null
+    else
+      (
+        let
+          stage1 = callPackage ./bazelDerivation.nix { } {
+            name = "bazelVendorDepsStage1";
+            inherit (bazelVendorDepsFOD) outputHash outputHashAlgo;
+            inherit
+              src
+              version
+              sourceRoot
+              env
+              buildInputs
+              nativeBuildInputs
+              ;
+            inherit registry;
+            inherit
+              bazel
+              targets
+              startupArgs
+              serverJavabase
+              ;
+            dontFixup = true;
+            command = "vendor";
+            outputHashMode = "recursive";
+            commandArgs = [ "--vendor_dir vendor_dir" ] ++ commandArgs;
+            bazelPreBuild = ''
+              mkdir vendor_dir
+            '';
+            bazelPostBuild = ''
+              # remove symlinks that point to locations under bazel_src/
+              find vendor_dir -type l -lname "$HOME/*" -exec rm '{}' \;
+              # remove symlinks to temp build directory on darwin
+              find vendor_dir -type l -lname "/private/var/tmp/*" -exec rm '{}' \;
+              # remove broken symlinks
+              find vendor_dir -xtype l -exec rm '{}' \;
+
+              # remove .marker files referencing NIX_STORE as those references aren't allowed in FOD
+              (${gnugrep}/bin/grep -rI "$NIX_STORE/" vendor_dir --files-with-matches --include="*.marker" --null || true) \
+                | xargs -0 --no-run-if-empty rm
+            '';
+            installPhase = ''
+              mkdir -p $out/vendor_dir
+              cp -r --reflink=auto vendor_dir/* $out/vendor_dir
+            '';
+
+          };
+        in
+        stdenv.mkDerivation {
+          name = "bazelVendorDeps";
+          buildInputs = lib.optional (!stdenv.hostPlatform.isDarwin) autoPatchelfHook ++ buildInputs;
+          inherit autoPatchelfIgnoreMissingDeps;
+          src = stage1;
+          installPhase = ''
+            cp -r . $out
+          '';
+        }
+      );
+
+  package = callPackage ./bazelDerivation.nix { } {
+    inherit
+      name
+      src
+      version
+      sourceRoot
+      env
+      buildInputs
+      nativeBuildInputs
+      ;
+    inherit registry bazelRepoCache bazelVendorDeps;
+    inherit
+      bazel
+      targets
+      startupArgs
+      serverJavabase
+      commandArgs
+      ;
+    inherit installPhase;
+    command = "build";
+  };
+in
+package // { passthru = { inherit bazelRepoCache bazelVendorDeps; }; }
diff --git a/third_party/nix/pkgs/bazel_8/build-support/patching.nix b/third_party/nix/pkgs/bazel_8/build-support/patching.nix
new file mode 100644
index 0000000..f43065b
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/build-support/patching.nix
@@ -0,0 +1,24 @@
+{ stdenv
+,
+}:
+{
+  # If there's a need to patch external dependencies managed by Bazel
+  # one option is to configure patches on Bazel level. Bazel doesn't
+  # allow patches to be in absolute paths so this helper will produce
+  # sources patch that adds given file to given location
+  addFilePatch =
+    { path
+    , file
+    ,
+    }:
+    stdenv.mkDerivation {
+      name = "add_file.patch";
+      dontUnpack = true;
+      buildPhase = ''
+        mkdir -p $(dirname "${path}")
+        cp ${file} "${path}"
+        diff -u /dev/null "${path}" >result.patch || true  # diff exit code is non-zero if there's a diff
+      '';
+      installPhase = ''cp result.patch $out'';
+    };
+}
diff --git a/third_party/nix/pkgs/bazel_8/defaultShell.nix b/third_party/nix/pkgs/bazel_8/defaultShell.nix
new file mode 100644
index 0000000..0deb516
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/defaultShell.nix
@@ -0,0 +1,41 @@
+{ lib
+, makeBinaryWrapper
+, writeShellApplication
+, bash
+, stdenv
+,
+}:
+{ defaultShellUtils }:
+let
+  defaultShellPath = lib.makeBinPath defaultShellUtils;
+
+  bashWithDefaultShellUtilsSh = writeShellApplication {
+    name = "bash";
+    runtimeInputs = defaultShellUtils;
+    # Empty PATH in Nixpkgs Bash is translated to /no-such-path
+    # On other distros empty PATH search fallback is looking in standard
+    # locations like /bin,/usr/bin
+    # For Bazel many rules rely on such search finding some common utils,
+    # so we provide them in case rules or arguments didn't specify a precise PATH
+    text = ''
+      if [[ "$PATH" == "/no-such-path" ]]; then
+        export PATH=${defaultShellPath}
+      fi
+      exec ${bash}/bin/bash "$@"
+    '';
+  };
+
+in
+{
+  inherit defaultShellUtils defaultShellPath;
+  # Script-based interpreters in shebangs aren't guaranteed to work,
+  # especially on MacOS. So let's produce a binary
+  bashWithDefaultShellUtils = stdenv.mkDerivation {
+    name = "bash";
+    src = bashWithDefaultShellUtilsSh;
+    nativeBuildInputs = [ makeBinaryWrapper ];
+    buildPhase = ''
+      makeWrapper ${bashWithDefaultShellUtilsSh}/bin/bash $out/bin/bash
+    '';
+  };
+}
diff --git a/third_party/nix/pkgs/bazel_8/examples.nix b/third_party/nix/pkgs/bazel_8/examples.nix
new file mode 100644
index 0000000..18b3647
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/examples.nix
@@ -0,0 +1,116 @@
+{ fetchFromGitHub
+, lib
+, bazel_8
+, libgcc
+, cctools
+, stdenv
+, jdk_headless
+, callPackage
+, zlib
+,
+}:
+let
+  bazelPackage = callPackage ./build-support/bazelPackage.nix { };
+  registry = fetchFromGitHub {
+    owner = "bazelbuild";
+    repo = "bazel-central-registry";
+    rev = "9342d3ec42ebafc2c06c33aa9d83b25ed984ebb1";
+    sha256 = "sha256-VT63Y8w9BawBXl5xgujG4Gv3SEGbUADGVsNPdUoDvsY=";
+  };
+  src = fetchFromGitHub {
+    owner = "bazelbuild";
+    repo = "examples";
+    rev = "568db753be213cc4be6c599e54ca64061ddbe6da";
+    sha256 = "sha256-F+iKi82uGWmJ+ICpITePdsA1SkncavSdgLkOKMr5LwM=";
+  };
+in
+{
+  java = bazelPackage {
+    inherit src registry;
+    sourceRoot = "source/java-tutorial";
+    name = "java-tutorial";
+    targets = [ "//:ProjectRunner" ];
+    bazel = bazel_8;
+    commandArgs = [
+      "--extra_toolchains=@@rules_java++toolchains+local_jdk//:all"
+      "--tool_java_runtime_version=local_jdk_21"
+    ];
+    env = {
+      JAVA_HOME = jdk_headless.home;
+      USE_BAZEL_VERSION = bazel_8.version;
+    };
+    installPhase = ''
+      mkdir $out
+      cp bazel-bin/ProjectRunner.jar $out/
+    '';
+    nativeBuildInputs = lib.optional (stdenv.hostPlatform.isDarwin) cctools;
+    bazelRepoCacheFOD = {
+      outputHash = lib.attrsets.attrByPath [ stdenv.hostPlatform.system ] null {
+        x86_64-linux = "sha256-64Ze+t0UYR2qQNECWes27SjzdkP+z5eJsCAO+OR+h/o=";
+        x86_64-darwin = lib.fakeHash;
+        aarch64-linux = "sha256-vEcOTdJM2YYle3PijKwroyM7LpfyK/3k/egRKDbjsmU=";
+        aarch64-darwin = "sha256-ya85EJikYXWpjtlgNu7i0DqtACgZBsppGEv3SVoJ6JA=";
+      };
+      outputHashAlgo = "sha256";
+    };
+  };
+  cpp = bazelPackage {
+    inherit src registry;
+    sourceRoot = "source/cpp-tutorial/stage3";
+    name = "cpp-tutorial";
+    targets = [ "//main:hello-world" ];
+    bazel = bazel_8;
+    installPhase = ''
+      mkdir $out
+      cp bazel-bin/main/hello-world $out/
+    '';
+    nativeBuildInputs = lib.optional (stdenv.hostPlatform.isDarwin) cctools;
+    commandArgs = lib.optionals (stdenv.hostPlatform.isDarwin) [
+      "--host_cxxopt=-xc++"
+      "--cxxopt=-xc++"
+    ];
+    env = {
+      USE_BAZEL_VERSION = bazel_8.version;
+    };
+    bazelRepoCacheFOD = {
+      outputHash =
+        {
+          x86_64-linux = "sha256-oPPWQdflAPMxF9YPazC//r0R3Sh6fUmNQe0oLM5EBUI=";
+          aarch64-linux = "sha256-oPPWQdflAPMxF9YPazC//r0R3Sh6fUmNQe0oLM5EBUI=";
+          aarch64-darwin = "sha256-oPPWQdflAPMxF9YPazC//r0R3Sh6fUmNQe0oLM5EBUI=";
+          x86_64-darwin = lib.fakeHash;
+        }.${stdenv.hostPlatform.system};
+      outputHashAlgo = "sha256";
+    };
+  };
+  rust = bazelPackage {
+    inherit src registry;
+    sourceRoot = "source/rust-examples/01-hello-world";
+    name = "rust-examples-01-hello-world";
+    targets = [ "//:bin" ];
+    bazel = bazel_8;
+    env = {
+      USE_BAZEL_VERSION = bazel_8.version;
+    };
+    installPhase = ''
+      mkdir $out
+      cp bazel-bin/bin $out/hello-world
+    '';
+    buildInputs = [
+      zlib
+      libgcc
+    ];
+    nativeBuildInputs = lib.optional (stdenv.hostPlatform.isDarwin) cctools;
+    autoPatchelfIgnoreMissingDeps = [ "librustc_driver-*.so" ];
+    bazelVendorDepsFOD = {
+      outputHash =
+        {
+          aarch64-linux = "sha256-2xopm/OCg9A1LqoW1ZesQc5pF/vX0ToIj1JYMtweVR0=";
+          x86_64-linux = "sha256-v987hMC6w2Lwr/PZn2zGHhHmXzecI2koLjOmGz0Mzng=";
+          aarch64-darwin = "sha256-sS7PzLI44dX7P0PY/68YjRSDkNJ6w5BklJNsXPHuOPc=";
+          x86_64-darwin = lib.fakeHash;
+        }.${stdenv.hostPlatform.system};
+      outputHashAlgo = "sha256";
+    };
+  };
+}
diff --git a/third_party/nix/pkgs/bazel_8/package.nix b/third_party/nix/pkgs/bazel_8/package.nix
new file mode 100644
index 0000000..7a27697
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/package.nix
@@ -0,0 +1,355 @@
+{ stdenv
+, callPackage
+, # nix tooling and utilities
+  darwin
+, lib
+, fetchzip
+, fetchpatch
+, makeWrapper
+, writeTextFile
+, replaceVars
+, # native build inputs
+  runtimeShell
+, zip
+, unzip
+, bash
+, coreutils
+, which
+, gawk
+, gnused
+, gnutar
+, gnugrep
+, gzip
+, findutils
+, diffutils
+, gnupatch
+, file
+, installShellFiles
+, python3
+, # Apple dependencies
+  cctools
+, # Allow to independently override the jdks used to build and run respectively
+  jdk_headless
+, # Toggle for hacks for running bazel under buildBazelPackage:
+  # Always assume all markers valid (this is needed because we remove markers; they are non-deterministic).
+  # Also, don't clean up environment variables (so that NIX_ environment variables are passed to compilers).
+  version ? "8.3.1"
+,
+}:
+
+let
+  inherit (callPackage ./build-support/patching.nix { }) addFilePatch;
+  inherit (stdenv.hostPlatform) isDarwin isAarch64;
+
+  bazelSystem = if isDarwin then "darwin" else "linux";
+
+  # on aarch64 Darwin, `uname -m` returns "arm64"
+  bazelArch = if isDarwin && isAarch64 then "arm64" else stdenv.hostPlatform.parsed.cpu.name;
+
+  src = fetchzip {
+    url = "https://github.com/bazelbuild/bazel/releases/download/${version}/bazel-${version}-dist.zip";
+    hash = "sha256-Hiny31S+YF7JdKjxzCyKdw3J/3OdDwsKeOkppfvWrNI=";
+    stripRoot = false;
+  };
+
+  defaultShellUtils =
+    # Keep this list conservative. For more exotic tools, prefer to use
+    # @rules_nixpkgs to pull in tools from the nix repository. Example:
+    #
+    # WORKSPACE:
+    #
+    #     nixpkgs_git_repository(
+    #         name = "nixpkgs",
+    #         revision = "def5124ec8367efdba95a99523dd06d918cb0ae8",
+    #     )
+    #
+    #     # This defines an external Bazel workspace.
+    #     nixpkgs_package(
+    #         name = "bison",
+    #         repositories = { "nixpkgs": "@nixpkgs//:default.nix" },
+    #     )
+    #
+    # some/BUILD.bazel:
+    #
+    #     genrule(
+    #        ...
+    #        cmd = "$(location @bison//:bin/bison) -other -args",
+    #        tools = [
+    #            ...
+    #            "@bison//:bin/bison",
+    #        ],
+    #     )
+    [
+      coreutils
+      diffutils
+      file
+      findutils
+      gawk
+      gnugrep
+      gnupatch
+      gnused
+      gnutar
+      gzip
+      unzip
+      which
+      zip
+      bash
+    ];
+  defaultShell = callPackage ./defaultShell.nix { } { inherit defaultShellUtils; };
+
+  commandArgs =
+    [
+      "--nobuild_python_zip"
+      "--features=-module_maps"
+      "--host_features=-module_maps"
+      "--announce_rc"
+      "--verbose_failures"
+      "--curses=no"
+    ]
+    ++ lib.optionals (isDarwin) [
+      "--macos_sdk_version=${stdenv.hostPlatform.darwinMinVersion}"
+      "--cxxopt=-isystem"
+      "--cxxopt=${lib.getDev stdenv.cc.libcxx}/include/c++/v1"
+      "--host_cxxopt=-isystem"
+      "--host_cxxopt=${lib.getDev stdenv.cc.libcxx}/include/c++/v1"
+      "--copt=-isystem"
+      "--copt=${lib.getDev darwin.libresolv}/include"
+      "--host_copt=-isystem"
+      "--host_copt=${lib.getDev darwin.libresolv}/include"
+    ];
+
+in
+stdenv.mkDerivation rec {
+  pname = "bazel";
+  inherit version src;
+
+  darwinPatches = [
+    # Bazel integrates with apple IOKit to inhibit and track system sleep.
+    # Inside the darwin sandbox, these API calls are blocked, and bazel
+    # crashes. It seems possible to allow these APIs inside the sandbox, but it
+    # feels simpler to patch bazel not to use it at all. So our bazel is
+    # incapable of preventing system sleep, which is a small price to pay to
+    # guarantee that it will always run in any nix context.
+    #
+    # See also ./bazel_darwin_sandbox.patch in bazel_5. That patch uses
+    # NIX_BUILD_TOP env var to conditionnally disable sleep features inside the
+    # sandbox.
+    #
+    # If you want to investigate the sandbox profile path,
+    # IORegisterForSystemPower can be allowed with
+    #
+    #     propagatedSandboxProfile = ''
+    #       (allow iokit-open (iokit-user-client-class "RootDomainUserClient"))
+    #     '';
+    #
+    # I do not know yet how to allow IOPMAssertion{CreateWithName,Release}
+    ./patches/darwin_sleep.patch
+
+    # Fix DARWIN_XCODE_LOCATOR_COMPILE_COMMAND by removing multi-arch support.
+    # Nixpkgs toolcahins do not support that (yet?) and get confused.
+    # Also add an explicit /usr/bin prefix that will be patched below.
+    (replaceVars ./patches/xcode.patch {
+      usrBinEnv = "${coreutils}/bin/env";
+      clangDarwin = "${stdenv.cc}/bin/clang";
+      codesign = "${darwin.sigtool}/bin/codesign";
+    })
+
+    # Revert preference for apple_support over rules_cc toolchain for now
+    # will need to figure out how to build with apple_support toolchain later
+    ./patches/apple_cc_toolchain.patch
+
+    # On Darwin, the last argument to gcc is coming up as an empty string. i.e: ''
+    # This is breaking the build of any C target. This patch removes the last
+    # argument if it's found to be an empty string.
+    ./patches/trim-last-argument-to-gcc-if-empty.patch
+
+    # fdopen() compilation fix
+    (fetchpatch {
+      url = "https://github.com/madler/zlib/commit/4bd9a71f3539b5ce47f0c67ab5e01f3196dc8ef9.patch";
+      hash = "sha256-wlZY0/XqND5Fk+SJkUCUj7XhGVwUJw/VqVGAlDdqOhs=";
+      stripLen = 1;
+      extraPrefix = "third_party/zlib/";
+    })
+  ];
+
+  patches = lib.optionals isDarwin darwinPatches ++ [
+    # patch that propagates rules_* patches below
+    # patches need to be within source root and can't be absolute paths in Nix store
+    # so rules_* patches are injected via addFilePatch
+    ./patches/deps_patches.patch
+    (addFilePatch {
+      path = "b/third_party/rules_python.patch";
+      file = replaceVars ./patches/rules_python.patch {
+        usrBinEnv = "${coreutils}/bin/env";
+      };
+    })
+    (addFilePatch {
+      path = "b/third_party/rules_java.patch";
+      file = replaceVars ./patches/rules_java.patch {
+        defaultBash = "${defaultShell.bashWithDefaultShellUtils}/bin/bash";
+      };
+    })
+    # Suggested for upstream in https://github.com/bazelbuild/bazel/pull/25936
+    ./patches/build_execlog_parser.patch
+    # Part of suggestion for upstream in https://github.com/bazelbuild/bazel/pull/25934
+    ./patches/env_bash.patch
+    # Suggested for upstream in https://github.com/bazelbuild/bazel/pull/25935
+    ./patches/gen_completion.patch
+
+    # --experimental_strict_action_env (which may one day become the default
+    # see bazelbuild/bazel#2574) hardcodes the default
+    # action environment to a non hermetic value (e.g. "/usr/local/bin").
+    # This is non hermetic on non-nixos systems. On NixOS, bazel cannot find the required binaries.
+    # So we are replacing this bazel paths by defaultShellPath,
+    # improving hermeticity and making it work in nixos.
+    (replaceVars ./patches/strict_action_env.patch {
+      strictActionEnvPatch = defaultShell.defaultShellPath;
+    })
+
+    (replaceVars ./patches/default_bash.patch {
+      defaultBash = "${defaultShell.bashWithDefaultShellUtils}/bin/bash";
+    })
+
+    (replaceVars ./patches/md5sum.patch {
+      md5sum = "${coreutils}/bin/md5sum";
+    })
+
+    # Nix build sandbox can configure custom PATH but doesn't have
+    # /usr/bin/env which is unfortunate https://github.com/NixOS/nixpkgs/issues/6227
+    # and we need to do a silly patch
+    (replaceVars ./patches/usr_bin_env.patch {
+      usrBinEnv = "${coreutils}/bin/env";
+    })
+
+    # Provide default JRE for Bazel process by setting --server_javabase=
+    # in a new default system bazelrc file
+    (replaceVars ./patches/bazel_rc.patch {
+      bazelSystemBazelRCPath = replaceVars ./system.bazelrc {
+        serverJavabase = jdk_headless;
+      };
+    })
+  ];
+
+  meta = with lib; {
+    homepage = "https://github.com/bazelbuild/bazel/";
+    description = "Build tool that builds code quickly and reliably";
+    sourceProvenance = with sourceTypes; [
+      fromSource
+      binaryBytecode # source bundles dependencies as jars
+    ];
+    license = licenses.asl20;
+    teams = [ lib.teams.bazel ];
+    mainProgram = "bazel";
+    platforms = lib.platforms.linux ++ lib.platforms.darwin;
+  };
+
+  nativeBuildInputs =
+    [
+      makeWrapper
+      jdk_headless
+      python3
+      unzip
+      which
+
+      # Shell completion
+      installShellFiles
+      python3.pkgs.absl-py # Needed to build fish completion
+    ]
+    # Needed for execlog
+    ++ lib.optional (!stdenv.hostPlatform.isDarwin) stdenv.cc
+    ++ lib.optional (stdenv.hostPlatform.isDarwin) cctools;
+
+  buildPhase = ''
+    runHook preBuild
+    export HOME=$(mktemp -d)
+
+    # If EMBED_LABEL isn't set, it'd be auto-detected from CHANGELOG.md
+    # and `git rev-parse --short HEAD` which would result in
+    # "3.7.0- (@non-git)" due to non-git build and incomplete changelog.
+    # Actual bazel releases use scripts/release/common.sh which is based
+    # on branch/tag information which we don't have with tarball releases.
+    # Note that .bazelversion is always correct and is based on bazel-*
+    # executable name, version checks should work fine
+    export EMBED_LABEL="${version}"
+
+    echo "Stage 1 - Running bazel bootstrap script"
+    export EXTRA_BAZEL_ARGS="${lib.strings.concatStringsSep " " commandArgs}"
+
+    ${bash}/bin/bash ./compile.sh
+
+    # XXX: get rid of this, or move it to another stage.
+    # It is plain annoying when builds fail.
+    echo "Stage 2 - Generate bazel completions"
+    ${bash}/bin/bash ./scripts/generate_bash_completion.sh \
+        --bazel=./output/bazel \
+        --output=./output/bazel-complete.bash \
+        --prepend=./scripts/bazel-complete-header.bash \
+        --prepend=./scripts/bazel-complete-template.bash
+    ${python3}/bin/python3 ./scripts/generate_fish_completion.py \
+        --bazel=./output/bazel \
+        --output=./output/bazel-complete.fish
+
+    runHook postBuild
+  '';
+
+  installPhase = ''
+    runHook preInstall
+
+    # Bazel binary contains zip archive, which contains text files and a jar
+    # both of which can have store references that might be obscured to Nix
+    # builder in packaged form, so we unpack and extract those references
+
+    # Note: grep isn't necessarily 100% accurate, other approaches could be
+    #       to disassemble Jar (slow) or hardcode known references
+    mkdir -p $out/nix-support
+    INSTALL_BASE=$(./output/bazel --batch info install_base)
+    find "$INSTALL_BASE" -type f -exec \
+       ${gnugrep}/bin/grep --text --only-matching --no-filename "$NIX_STORE/[^/]*" '{}' \; \
+    | sort -u >> $out/nix-support/depends
+
+    mkdir -p $out/bin
+
+    # official wrapper scripts that searches for $WORKSPACE_ROOT/tools/bazel if
+    # it can’t find something in tools, it calls
+    # $out/bin/bazel-{version}-{os_arch} The binary _must_ exist with this
+    # naming if your project contains a .bazelversion file.
+    cp ./scripts/packages/bazel.sh $out/bin/bazel
+    versioned_bazel="$out/bin/bazel-${version}-${bazelSystem}-${bazelArch}"
+    mv ./output/bazel "$versioned_bazel"
+    wrapProgram "$versioned_bazel" --suffix PATH : ${defaultShell.defaultShellPath}
+
+    mkdir $out/share
+    cp ./output/parser_deploy.jar $out/share/parser_deploy.jar
+    substitute ${./bazel-execlog.sh} $out/bin/bazel-execlog \
+      --subst-var out \
+      --subst-var-by runtimeShell ${runtimeShell} \
+      --subst-var-by javaBin ${jdk_headless}/bin/java
+    chmod +x $out/bin/bazel-execlog
+
+    # shell completion files
+    installShellCompletion --bash \
+      --name bazel.bash \
+      ./output/bazel-complete.bash
+    installShellCompletion --zsh \
+      --name _bazel \
+      ./scripts/zsh_completion/_bazel
+    installShellCompletion --fish \
+      --name bazel.fish \
+      ./output/bazel-complete.fish
+  '';
+
+  postFixup =
+    # verify that bazel binary still works post-fixup
+    ''
+      USE_BAZEL_VERSION=${version} $out/bin/bazel --batch info release
+    '';
+
+  # Bazel binary includes zip archive at the end that `strip` would end up discarding
+  stripExclude = [ "bin/.bazel-${version}-*-wrapped" ];
+
+  passthru = {
+    tests = {
+      inherit (callPackage ./examples.nix { }) cpp java rust;
+    };
+  };
+}
diff --git a/third_party/nix/pkgs/bazel_8/patches/apple_cc_toolchain.patch b/third_party/nix/pkgs/bazel_8/patches/apple_cc_toolchain.patch
new file mode 100644
index 0000000..32d5b5b
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/apple_cc_toolchain.patch
@@ -0,0 +1,19 @@
+diff --git a/MODULE.bazel b/MODULE.bazel
+index 11a6075175..f53f0c732b 100644
+--- a/MODULE.bazel
++++ b/MODULE.bazel
+@@ -35,10 +35,10 @@ bazel_dep(name = "with_cfg.bzl", version = "0.6.0")
+ bazel_dep(name = "abseil-cpp", version = "20240722.0.bcr.2")
+ bazel_dep(name = "rules_shell", version = "0.2.0")
+ 
+-# Depend on apple_support first and then rules_cc so that the Xcode toolchain
+-# from apple_support wins over the generic Unix toolchain from rules_cc.
+-bazel_dep(name = "apple_support", version = "1.18.1")
++# Not Depend on apple_support first and then rules_cc so that the Xcode toolchain
++# from apple_support not wins over the generic Unix toolchain from rules_cc.
+ bazel_dep(name = "rules_cc", version = "0.0.17")
++bazel_dep(name = "apple_support", version = "1.18.1")
+ 
+ # repo_name needs to be used, until WORKSPACE mode is to be supported in bazel_tools
+ bazel_dep(name = "protobuf", version = "29.0", repo_name = "com_google_protobuf")
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/bazel_rc.patch b/third_party/nix/pkgs/bazel_8/patches/bazel_rc.patch
new file mode 100644
index 0000000..a599ac3
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/bazel_rc.patch
@@ -0,0 +1,13 @@
+diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
+index 8f8f15685f..a7ae52d1e4 100644
+--- a/src/main/cpp/option_processor.cc
++++ b/src/main/cpp/option_processor.cc
+@@ -56,7 +56,7 @@ OptionProcessor::OptionProcessor(
+     : workspace_layout_(workspace_layout),
+       startup_options_(std::move(default_startup_options)),
+       parse_options_called_(false),
+-      system_bazelrc_path_(BAZEL_SYSTEM_BAZELRC_PATH) {}
++      system_bazelrc_path_("@bazelSystemBazelRCPath@") {}
+ 
+ OptionProcessor::OptionProcessor(
+     const WorkspaceLayout* workspace_layout,
diff --git a/third_party/nix/pkgs/bazel_8/patches/build_execlog_parser.patch b/third_party/nix/pkgs/bazel_8/patches/build_execlog_parser.patch
new file mode 100644
index 0000000..552bc3b
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/build_execlog_parser.patch
@@ -0,0 +1,28 @@
+diff --git a/compile.sh b/compile.sh
+index 4712355d48..feec286704 100755
+--- a/compile.sh
++++ b/compile.sh
+@@ -76,6 +76,13 @@ bazel_build "src:bazel_nojdk${EXE_EXT}" \
+   --host_platform=@platforms//host \
+   --platforms=@platforms//host \
+   || fail "Could not build Bazel"
++
++bazel_build src/tools/execlog:parser_deploy.jar \
++  --action_env=PATH \
++  --host_platform=@platforms//host \
++  --platforms=@platforms//host \
++  || fail "Could not build parser_deploy.jar"
++
+ bazel_bin_path="$(get_bazel_bin_path)/src/bazel_nojdk${EXE_EXT}"
+ [ -e "$bazel_bin_path" ] \
+   || fail "Could not find freshly built Bazel binary at '$bazel_bin_path'"
+@@ -84,5 +91,8 @@ cp -f "$bazel_bin_path" "output/bazel${EXE_EXT}" \
+ chmod 0755 "output/bazel${EXE_EXT}"
+ BAZEL="$(pwd)/output/bazel${EXE_EXT}"
+ 
++cp "$(get_bazel_bin_path)/src/tools/execlog/parser_deploy.jar" output/ \
++  || fail "Could not copy 'parser_deploy.jar' to 'output/"
++
+ clear_log
+ display "Build successful! Binary is here: ${BAZEL}"
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/darwin_sleep.patch b/third_party/nix/pkgs/bazel_8/patches/darwin_sleep.patch
new file mode 100644
index 0000000..731ede8
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/darwin_sleep.patch
@@ -0,0 +1,56 @@
+diff --git a/src/main/native/darwin/sleep_prevention_jni.cc b/src/main/native/darwin/sleep_prevention_jni.cc
+index 67c35b201e..e50a58320e 100644
+--- a/src/main/native/darwin/sleep_prevention_jni.cc
++++ b/src/main/native/darwin/sleep_prevention_jni.cc
+@@ -33,31 +33,13 @@ static int g_sleep_state_stack = 0;
+ static IOPMAssertionID g_sleep_state_assertion = kIOPMNullAssertionID;
+ 
+ int portable_push_disable_sleep() {
+-  std::lock_guard<std::mutex> lock(g_sleep_state_mutex);
+-  BAZEL_CHECK_GE(g_sleep_state_stack, 0);
+-  if (g_sleep_state_stack == 0) {
+-    BAZEL_CHECK_EQ(g_sleep_state_assertion, kIOPMNullAssertionID);
+-    CFStringRef reasonForActivity = CFSTR("build.bazel");
+-    IOReturn success = IOPMAssertionCreateWithName(
+-        kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity,
+-        &g_sleep_state_assertion);
+-    BAZEL_CHECK_EQ(success, kIOReturnSuccess);
+-  }
+-  g_sleep_state_stack += 1;
+-  return 0;
++  // Unreliable, disable for now
++  return -1;
+ }
+ 
+ int portable_pop_disable_sleep() {
+-  std::lock_guard<std::mutex> lock(g_sleep_state_mutex);
+-  BAZEL_CHECK_GT(g_sleep_state_stack, 0);
+-  g_sleep_state_stack -= 1;
+-  if (g_sleep_state_stack == 0) {
+-    BAZEL_CHECK_NE(g_sleep_state_assertion, kIOPMNullAssertionID);
+-    IOReturn success = IOPMAssertionRelease(g_sleep_state_assertion);
+-    BAZEL_CHECK_EQ(success, kIOReturnSuccess);
+-    g_sleep_state_assertion = kIOPMNullAssertionID;
+-  }
+-  return 0;
++  // Unreliable, disable for now
++  return -1;
+ }
+ 
+ }  // namespace blaze_jni
+diff --git a/src/main/native/darwin/system_suspension_monitor_jni.cc b/src/main/native/darwin/system_suspension_monitor_jni.cc
+index 3483aa7935..51782986ec 100644
+--- a/src/main/native/darwin/system_suspension_monitor_jni.cc
++++ b/src/main/native/darwin/system_suspension_monitor_jni.cc
+@@ -83,10 +83,7 @@ void portable_start_suspend_monitoring() {
+     // Register to receive system sleep notifications.
+     // Testing needs to be done manually. Use the logging to verify
+     // that sleeps are being caught here.
+-    suspend_state.connect_port = IORegisterForSystemPower(
+-        &suspend_state, &notifyPortRef, SleepCallBack, &notifierObject);
+-    BAZEL_CHECK_NE(suspend_state.connect_port, MACH_PORT_NULL);
+-    IONotificationPortSetDispatchQueue(notifyPortRef, queue);
++    // XXX: Unreliable, disable for now
+ 
+     // Register to deal with SIGCONT.
+     // We register for SIGCONT because we can't catch SIGSTOP.
diff --git a/third_party/nix/pkgs/bazel_8/patches/default_bash.patch b/third_party/nix/pkgs/bazel_8/patches/default_bash.patch
new file mode 100644
index 0000000..a43a9f0
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/default_bash.patch
@@ -0,0 +1,22 @@
+diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+index a982b782e1..d49b047074 100644
+--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
++++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+@@ -89,13 +89,13 @@ public class BazelRuleClassProvider {
+     public boolean useStrictActionEnv;
+   }
+ 
+-  private static final PathFragment FALLBACK_SHELL = PathFragment.create("/bin/bash");
++  private static final PathFragment FALLBACK_SHELL = PathFragment.create("@defaultBash@");
+ 
+   public static final ImmutableMap<OS, PathFragment> SHELL_EXECUTABLE =
+       ImmutableMap.<OS, PathFragment>builder()
+           .put(OS.WINDOWS, PathFragment.create("c:/msys64/usr/bin/bash.exe"))
+-          .put(OS.FREEBSD, PathFragment.create("/usr/local/bin/bash"))
+-          .put(OS.OPENBSD, PathFragment.create("/usr/local/bin/bash"))
++          .put(OS.FREEBSD, PathFragment.create("@defaultBash@"))
++          .put(OS.OPENBSD, PathFragment.create("@defaultBash@"))
+           .put(OS.UNKNOWN, FALLBACK_SHELL)
+           .buildOrThrow();
+ 
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/deps_patches.patch b/third_party/nix/pkgs/bazel_8/patches/deps_patches.patch
new file mode 100644
index 0000000..bf0aad9
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/deps_patches.patch
@@ -0,0 +1,24 @@
+diff --git a/MODULE.bazel b/MODULE.bazel
+--- MODULE.bazel
++++ MODULE.bazel
+@@ -24,12 +24,20 @@
+ bazel_dep(name = "zstd-jni", version = "1.5.6-9")
+ bazel_dep(name = "blake3", version = "1.5.1.bcr.1")
+ bazel_dep(name = "zlib", version = "1.3.1.bcr.5")
+ bazel_dep(name = "rules_java", version = "8.12.0")
++single_version_override(
++    module_name = "rules_java",
++    patches = ["//third_party:rules_java.patch"],
++)
+ bazel_dep(name = "rules_graalvm", version = "0.11.1")
+ bazel_dep(name = "rules_proto", version = "7.0.2")
+ bazel_dep(name = "rules_jvm_external", version = "6.0")
+ bazel_dep(name = "rules_python", version = "0.40.0")
++single_version_override(
++    module_name = "rules_python",
++    patches = ["//third_party:rules_python.patch"],
++)
+ bazel_dep(name = "rules_testing", version = "0.6.0")
+ bazel_dep(name = "googletest", version = "1.15.2", repo_name = "com_google_googletest")
+ bazel_dep(name = "with_cfg.bzl", version = "0.6.0")
+ bazel_dep(name = "abseil-cpp", version = "20240722.0.bcr.2")
diff --git a/third_party/nix/pkgs/bazel_8/patches/env_bash.patch b/third_party/nix/pkgs/bazel_8/patches/env_bash.patch
new file mode 100644
index 0000000..cc20d10
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/env_bash.patch
@@ -0,0 +1,22 @@
+diff --git a/src/zip_files.sh b/src/zip_files.sh
+index 1422a6c659..920c1019d2 100755
+--- a/src/zip_files.sh
++++ b/src/zip_files.sh
+@@ -1,4 +1,4 @@
+-#!/bin/bash
++#!/usr/bin/env bash
+ 
+ # Copyright 2019 The Bazel Authors. All rights reserved.
+ #
+
+diff --git a/src/package-bazel.sh b/src/package-bazel.sh
+index 56e94db400..2c614af6c2 100755
+--- a/src/package-bazel.sh
++++ b/src/package-bazel.sh
+@@ -1,4 +1,4 @@
+-#!/bin/bash
++#!/usr/bin/env bash
+ #
+ # Copyright 2015 The Bazel Authors. All rights reserved.
+ #
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/gen_completion.patch b/third_party/nix/pkgs/bazel_8/patches/gen_completion.patch
new file mode 100644
index 0000000..c3af229
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/gen_completion.patch
@@ -0,0 +1,26 @@
+diff --git a/scripts/generate_bash_completion.sh b/scripts/generate_bash_completion.sh
+index 778810570c..84d2d49a0d 100755
+--- a/scripts/generate_bash_completion.sh
++++ b/scripts/generate_bash_completion.sh
+@@ -68,7 +68,7 @@ mkdir "${tempdir}/root"
+ 
+ server_javabase_flag=
+ [ -z "${javabase}" ] || server_javabase_flag="--server_javabase=${javabase}"
+-"${bazel}" --output_user_root="${tempdir}/root" ${server_javabase_flag} \
++"${bazel}" --batch --output_user_root="${tempdir}/root" ${server_javabase_flag} \
+     help completion >>"${tempdir}/output"
+ 
+ [ -z "${append}" ] || cat ${append} >>"${tempdir}/output"
+diff --git a/scripts/generate_fish_completion.py b/scripts/generate_fish_completion.py
+index bafe28979f..a941d8f7f9 100644
+--- a/scripts/generate_fish_completion.py
++++ b/scripts/generate_fish_completion.py
+@@ -102,7 +102,7 @@ class BazelCompletionWriter(object):
+ 
+   def _get_bazel_output(self, args):
+     return subprocess.check_output(
+-        (self._bazel, '--output_user_root={}'.format(self._output_user_root)) +
++        (self._bazel, '--batch', '--output_user_root={}'.format(self._output_user_root)) +
+         tuple(args),
+         universal_newlines=True)
+ 
diff --git a/third_party/nix/pkgs/bazel_8/patches/md5sum.patch b/third_party/nix/pkgs/bazel_8/patches/md5sum.patch
new file mode 100644
index 0000000..fc49581
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/md5sum.patch
@@ -0,0 +1,22 @@
+diff --git a/src/BUILD b/src/BUILD
+index f61b90738a..2c3a54d36c 100644
+--- a/src/BUILD
++++ b/src/BUILD
+@@ -38,12 +38,12 @@ md5_cmd = "set -e -o pipefail && %s $(SRCS) | %s | %s > $@"
+     }) + embedded_tools_target,
+     outs = ["install_base_key" + suffix],
+     cmd = select({
+-        "//src/conditions:darwin": md5_cmd % ("/sbin/md5", "/sbin/md5", "head -c 32"),
+-        "//src/conditions:freebsd": md5_cmd % ("/sbin/md5", "/sbin/md5", "head -c 32"),
++        "//src/conditions:darwin": md5_cmd % ("@md5sum@", "@md5sum@", "head -c 32"),
++        "//src/conditions:freebsd": md5_cmd % ("@md5sum@", "@md5sum@", "head -c 32"),
+         # We avoid using the `head` tool's `-c` option, since it does not exist
+         # on OpenBSD.
+-        "//src/conditions:openbsd": md5_cmd % ("/bin/md5", "/bin/md5", "dd bs=32 count=1"),
+-        "//conditions:default": md5_cmd % ("md5sum", "md5sum", "head -c 32"),
++        "//src/conditions:openbsd": md5_cmd % ("@md5sum@", "@md5sum@", "dd bs=32 count=1"),
++        "//conditions:default": md5_cmd % ("@md5sum@", "@md5sum@", "head -c 32"),
+     }),
+ ) for suffix, embedded_tools_target in {
+     "_jdk_allmodules": [":embedded_tools_jdk_allmodules"],
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/rules_java.patch b/third_party/nix/pkgs/bazel_8/patches/rules_java.patch
new file mode 100644
index 0000000..ba2fd2f
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/rules_java.patch
@@ -0,0 +1,11 @@
+diff --git java/bazel/rules/java_stub_template.txt java/bazel/rules/java_stub_template.txt
+index 115b46e..56d2ff7 100644
+--- java/bazel/rules/java_stub_template.txt
++++ java/bazel/rules/java_stub_template.txt
+@@ -1,4 +1,4 @@
+-#!/usr/bin/env bash
++#!@defaultBash@
+ # Copyright 2014 The Bazel Authors. All rights reserved.
+ #
+ # Licensed under the Apache License, Version 2.0 (the "License");
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/rules_python.patch b/third_party/nix/pkgs/bazel_8/patches/rules_python.patch
new file mode 100644
index 0000000..a63f44e
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/rules_python.patch
@@ -0,0 +1,14 @@
+diff --git python/private/runtime_env_toolchain.bzl python/private/runtime_env_toolchain.bzl
+--- python/private/runtime_env_toolchain.bzl
++++ python/private/runtime_env_toolchain.bzl
+@@ -42,7 +42,7 @@
+         name = "_runtime_env_py3_runtime",
+         interpreter = "//python/private:runtime_env_toolchain_interpreter.sh",
+         python_version = "PY3",
+-        stub_shebang = "#!/usr/bin/env python3",
++        stub_shebang = "#!@usrBinEnv@ python3",
+         visibility = ["//visibility:private"],
+         tags = ["manual"],
+     )
+
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/strict_action_env.patch b/third_party/nix/pkgs/bazel_8/patches/strict_action_env.patch
new file mode 100644
index 0000000..1402c20
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/strict_action_env.patch
@@ -0,0 +1,13 @@
+diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+index a70b5559bc..10bdffe961 100644
+--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
++++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+@@ -466,7 +466,7 @@ public class BazelRuleClassProvider {
+       // Note that --action_env does not propagate to the host config, so it is not a viable
+       // workaround when a genrule is itself built in the host config (e.g. nested genrules). See
+       // #8536.
+-      return "/bin:/usr/bin:/usr/local/bin";
++      return "@strictActionEnvPatch@";
+     }
+
+     String newPath = "";
diff --git a/third_party/nix/pkgs/bazel_8/patches/trim-last-argument-to-gcc-if-empty.patch b/third_party/nix/pkgs/bazel_8/patches/trim-last-argument-to-gcc-if-empty.patch
new file mode 100644
index 0000000..b93b252
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/trim-last-argument-to-gcc-if-empty.patch
@@ -0,0 +1,37 @@
+From 177b4720d6fbaa7fdd17e5e11b2c79ac8f246786 Mon Sep 17 00:00:00 2001
+From: "Wael M. Nasreddine" <wael.nasreddine@gmail.com>
+Date: Thu, 27 Jun 2019 21:08:51 -0700
+Subject: [PATCH] Trim last argument to gcc if empty, on Darwin
+
+On Darwin, the last argument to GCC is coming up as an empty string.
+This is breaking the build of proto_library targets. However, I was not
+able to reproduce with the example cpp project[0].
+
+This commit removes the last argument if it's an empty string. This is
+not a problem on Linux.
+
+[0]: https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage3
+---
+ tools/cpp/osx_cc_wrapper.sh.tpl | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/tools/cpp/osx_cc_wrapper.sh.tpl b/tools/cpp/osx_cc_wrapper.sh.tpl
+index 4c85cd9b6b..6c611e3d25 100644
+--- a/tools/cpp/osx_cc_wrapper.sh.tpl
++++ b/tools/cpp/osx_cc_wrapper.sh.tpl
+@@ -53,7 +53,11 @@ done
+ %{env}
+ 
+ # Call the C++ compiler
+-%{cc} "$@"
++if [[ ${*: -1} = "" ]]; then
++    %{cc} "${@:0:$#}"
++else
++    %{cc} "$@"
++fi
+ 
+ function get_library_path() {
+     for libdir in ${LIB_DIRS}; do
+-- 
+2.19.2
+
diff --git a/third_party/nix/pkgs/bazel_8/patches/usr_bin_env.patch b/third_party/nix/pkgs/bazel_8/patches/usr_bin_env.patch
new file mode 100644
index 0000000..0718b75
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/usr_bin_env.patch
@@ -0,0 +1,32 @@
+diff --git a/src/zip_builtins.sh b/src/zip_builtins.sh
+index d78ca5526a..c7d8f251cc 100755
+--- a/src/zip_builtins.sh
++++ b/src/zip_builtins.sh
+@@ -1,4 +1,4 @@
+-#!/usr/bin/env bash
++#!@usrBinEnv@ bash
+ 
+ # Copyright 2020 The Bazel Authors. All rights reserved.
+ #
+
+diff --git a/src/zip_files.sh b/src/zip_files.sh
+index 1422a6c659..4b1c221784 100755
+--- a/src/zip_files.sh
++++ b/src/zip_files.sh
+@@ -1,4 +1,4 @@
+-#!/usr/bin/env bash
++#!@usrBinEnv@ bash
+ 
+ # Copyright 2019 The Bazel Authors. All rights reserved.
+ #
+
+diff --git a/src/package-bazel.sh b/src/package-bazel.sh
+index 56e94db400..65fef20988 100755
+--- a/src/package-bazel.sh
++++ b/src/package-bazel.sh
+@@ -1,4 +1,4 @@
+-#!/usr/bin/env bash
++#!@usrBinEnv@ bash
+ #
+ # Copyright 2015 The Bazel Authors. All rights reserved.
+ #
diff --git a/third_party/nix/pkgs/bazel_8/patches/xcode.patch b/third_party/nix/pkgs/bazel_8/patches/xcode.patch
new file mode 100644
index 0000000..52931a3
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/patches/xcode.patch
@@ -0,0 +1,31 @@
+diff --git a/scripts/bootstrap/compile.sh b/scripts/bootstrap/compile.sh
+index 1bad14cba7..d312fe08bb 100755
+--- a/scripts/bootstrap/compile.sh
++++ b/scripts/bootstrap/compile.sh
+@@ -402,7 +402,7 @@ cp $OUTPUT_DIR/libblaze.jar ${ARCHIVE_DIR}
+ # TODO(b/28965185): Remove when xcode-locator is no longer required in embedded_binaries.
+ log "Compiling xcode-locator..."
+ if [[ $PLATFORM == "darwin" ]]; then
+-  run /usr/bin/xcrun --sdk macosx clang -mmacosx-version-min=10.13 -fobjc-arc -framework CoreServices -framework Foundation -o ${ARCHIVE_DIR}/xcode-locator tools/osx/xcode_locator.m
++  run @clangDarwin@ -mmacosx-version-min=10.13 -fobjc-arc -framework CoreServices -framework Foundation -o ${ARCHIVE_DIR}/xcode-locator tools/osx/xcode_locator.m
+ else
+   cp tools/osx/xcode_locator_stub.sh ${ARCHIVE_DIR}/xcode-locator
+ fi
+diff --git a/tools/osx/BUILD b/tools/osx/BUILD
+index 0358fb0ffe..1e6eae1f33 100644
+--- a/tools/osx/BUILD
++++ b/tools/osx/BUILD
+@@ -27,9 +27,9 @@ exports_files([
+ ])
+ 
+ DARWIN_XCODE_LOCATOR_COMPILE_COMMAND = """
+-  /usr/bin/xcrun --sdk macosx clang -mmacosx-version-min=10.13 -fobjc-arc -framework CoreServices \
+-      -framework Foundation -arch arm64 -arch x86_64 -Wl,-no_adhoc_codesign -Wl,-no_uuid -o $@ $< && \
+-  env -i codesign --identifier $@ --force --sign - $@
++  @clangDarwin@ -mmacosx-version-min=10.13 -fobjc-arc -framework CoreServices \
++      -framework Foundation -Wl,-no_adhoc_codesign -Wl,-no_uuid -o $@ $< && \
++  @usrBinEnv@ @codesign@ --identifier $@ --force --sign - $@
+ """
+ 
+ genrule(
+
diff --git a/third_party/nix/pkgs/bazel_8/system.bazelrc b/third_party/nix/pkgs/bazel_8/system.bazelrc
new file mode 100644
index 0000000..6684e33
--- /dev/null
+++ b/third_party/nix/pkgs/bazel_8/system.bazelrc
@@ -0,0 +1,4 @@
+startup --server_javabase=@serverJavabase@
+
+# load default location for the system wide configuration
+try-import /etc/bazel.bazelrc