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_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;
+    };
+  };
+}