Embed @platforms into the Bazel binary

This PR implements the change discussed at https://docs.google.com/document/d/1EArrWYUDugqJzBcb0-OxY5BH1FFPYV3jxLIXbJpD9RY/edit?pli=1#heading=h.5mcn15i0e1ch.

Closes https://github.com/bazelbuild/bazel/pull/8600.
https://github.com/bazelbuild/bazel/issues/6516

This is encore of https://github.com/bazelbuild/bazel/commit/324dc44e6bafb487331724ae83d67bc18ed8a8aa with these changes:

* removing WORKSPACE file from the platforms_archive - repository rules always create new WORKSPACE file and that messes up the timestamps after re-archiving. This fixes the determinism test.
* after @aehlig kindly explained the semantics of external repositories, I discovered that overriding @platforms doesn't work when there is a load statement somewhere above the override, which is the common case. Therefore I had to move bundled platforms to the workspace suffix and had to use maybe pattern.
* because of maybe pattern I have to mock the bzl file in Bazel unit tests.

RELNOTES:
PiperOrigin-RevId: 253193463
diff --git a/src/BUILD b/src/BUILD
index c0e70b8..e7c6827 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -328,10 +328,11 @@
     name = "package-zip" + suffix,
     srcs = ([":embedded_tools" + suffix + ".zip"] if embed else []) + [
         # The script assumes that the embedded tools zip (if exists) is the
-        # first item here, the deploy jar the second and install base key is the
-        # third
+        # first item here, the deploy jar the second, install base key is the
+        # third, and platforms archive is the fourth.
         "//src/main/java/com/google/devtools/build/lib:bazel/BazelServer_deploy.jar",
         "install_base_key" + suffix,
+        ":platforms_archive",
         ":libunix",
         "//src/main/tools:build-runfiles",
         "//src/main/tools:process-wrapper",
@@ -355,6 +356,15 @@
     ("_nojdk", True),
 ]]
 
+genrule(
+    name = "platforms_archive",
+    srcs = ["@platforms//:srcs"],
+    outs = ["platforms.zip"],
+    # Removing the WORKSPACE file since local_repository creates it no matter what and leaving
+    # it there would make the zip nondeterministic.
+    cmd = "zip -qX $@ $$(echo $(SRCS) | xargs -n 1 | grep -v '.*/WORKSPACE$$' | sort | xargs)",
+)
+
 [genrule(
     name = "bazel-bin" + suffix,
     srcs = [
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/local_config_platform.WORKSPACE b/src/main/java/com/google/devtools/build/lib/bazel/repository/local_config_platform.WORKSPACE
index d0ce5b7..6793501 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/local_config_platform.WORKSPACE
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/local_config_platform.WORKSPACE
@@ -1 +1,12 @@
-local_config_platform(name = "local_config_platform")
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+
+maybe(
+    local_repository,
+    "platforms",
+    path = __embedded_dir__ + "/platforms",
+)
+
+maybe(
+    local_config_platform,
+    "local_config_platform",
+)
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 d1de9db..1d5a322 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
@@ -406,7 +406,7 @@
           builder.addRuleDefinition(new LocalConfigPlatformRule());
 
           try {
-            builder.addWorkspaceFilePrefix(
+            builder.addWorkspaceFileSuffix(
                 ResourceFileLoader.loadResource(
                     LocalConfigPlatformRule.class, "local_config_platform.WORKSPACE"));
           } catch (IOException e) {
diff --git a/src/package-bazel.sh b/src/package-bazel.sh
index 632b133..0ebefed 100755
--- a/src/package-bazel.sh
+++ b/src/package-bazel.sh
@@ -16,16 +16,15 @@
 
 set -euo pipefail
 
-# This script bootstraps building a Bazel binary without Bazel then
-# use this compiled Bazel to bootstrap Bazel itself. It can also
-# be provided with a previous version of Bazel to bootstrap Bazel
-# itself.
+# This script creates the Bazel archive that Bazel client unpacks and then
+# starts the server from.
 
 WORKDIR=$(pwd)
 OUT=$1
 EMBEDDED_TOOLS=$2
 DEPLOY_JAR=$3
 INSTALL_BASE_KEY=$4
+PLATFORMS_ARCHIVE=$5
 shift 4
 
 TMP_DIR=${TMPDIR:-/tmp}
@@ -66,4 +65,16 @@
   (cd ${PACKAGE_DIR}/embedded_tools && unzip -q "${WORKDIR}/${EMBEDDED_TOOLS}")
 fi
 
+# Unzip platforms.zip into platforms/, move files up from external/platforms
+# subdirectory, and cleanup after itself.
+( \
+  cd ${PACKAGE_DIR} && \
+    unzip -q -d platforms platforms.zip && \
+    rm platforms.zip && \
+    cd platforms && \
+    mv external/platforms/* . && \
+    rmdir -p external/platforms \
+)
+touch -t 198001010000.00 ${PACKAGE_DIR}/platforms/WORKSPACE
+
 (cd ${PACKAGE_DIR} && find . -type f | sort | zip -q9DX@ "${WORKDIR}/${OUT}")
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index f6cae4e..edf34a1 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -383,6 +383,11 @@
     config.create("/bazel_tools_workspace/WORKSPACE", "workspace(name = 'bazel_tools')");
     config.create("/bazel_tools_workspace/tools/build_defs/repo/BUILD");
     config.create(
+        "/bazel_tools_workspace/tools/build_defs/repo/utils.bzl",
+        "def maybe(repo_rule, name, **kwargs):",
+        "  if name not in native.existing_rules():",
+        "    repo_rule(name = name, **kwargs)");
+    config.create(
         "/bazel_tools_workspace/tools/build_defs/repo/http.bzl",
         "def http_archive(**kwargs):",
         "  pass",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 834d8ff..aa7d269 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -238,6 +238,11 @@
     mockToolsConfig.create("/bazel_tools_workspace/WORKSPACE", "workspace(name = 'bazel_tools')");
     mockToolsConfig.create("/bazel_tools_workspace/tools/build_defs/repo/BUILD");
     mockToolsConfig.create(
+        "/bazel_tools_workspace/tools/build_defs/repo/utils.bzl",
+        "def maybe(repo_rule, name, **kwargs):",
+        "  if name not in native.existing_rules():",
+        "    repo_rule(name = name, **kwargs)");
+    mockToolsConfig.create(
         "/bazel_tools_workspace/tools/build_defs/repo/http.bzl",
         "def http_archive(**kwargs):",
         "  pass",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
index 8122469..ea96da0 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
@@ -114,6 +114,11 @@
     mockToolsConfig.create("/bazel_tools_workspace/WORKSPACE", "workspace(name = 'bazel_tools')");
     mockToolsConfig.create("/bazel_tools_workspace/tools/build_defs/repo/BUILD");
     mockToolsConfig.create(
+        "/bazel_tools_workspace/tools/build_defs/repo/utils.bzl",
+        "def maybe(repo_rule, name, **kwargs):",
+        "  if name not in native.existing_rules():",
+        "    repo_rule(name = name, **kwargs)");
+    mockToolsConfig.create(
         "/bazel_tools_workspace/tools/build_defs/repo/http.bzl",
         "def http_archive(**kwargs):",
         "  pass",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
index a3370c1..49b4148 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
@@ -86,6 +86,11 @@
         "",
         "def http_file(**kwargs):",
         "  pass");
+    FileSystemUtils.writeIsoLatin1(
+        tools.getRelative("tools/build_defs/repo/utils.bzl"),
+        "def maybe(repo_rule, name, **kwargs):",
+        "  if name not in native.existing_rules():",
+        "    repo_rule(name = name, **kwargs)");
   }
 
   private void fetchExternalRepo(RepositoryName externalRepo) {
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 1a773ce..e085eee 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -1104,6 +1104,13 @@
 )
 
 sh_test(
+    name = "platforms_test",
+    srcs = ["platforms_test.sh"],
+    data = [":test-deps"],
+    deps = ["@bazel_tools//tools/bash/runfiles"],
+)
+
+sh_test(
     name = "platform_mapping_test",
     srcs = ["platform_mapping_test.sh"],
     data = [":test-deps"],
diff --git a/src/test/shell/bazel/platforms_test.sh b/src/test/shell/bazel/platforms_test.sh
new file mode 100755
index 0000000..6db625a
--- /dev/null
+++ b/src/test/shell/bazel/platforms_test.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+#
+# Copyright 2019 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Test related to @platforms embedded repository
+#
+
+# --- begin runfiles.bash initialization ---
+set -euo pipefail
+if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  if [[ -f "$0.runfiles_manifest" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+  elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+    export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+  elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+    export RUNFILES_DIR="$0.runfiles"
+  fi
+fi
+if [[ -f "${RUNFILES_DIR:-/dev/null}/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
+  source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
+  source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+            "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+else
+  echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+  exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+source "$(rlocation "io_bazel/src/test/shell/integration_test_setup.sh")" \
+  || { echo "integration_test_setup.sh not found!" >&2; exit 1; }
+
+function test_platforms_repository_builds_itself() {
+  # We test that a built-in @platforms repository is buildable.
+  bazel build @platforms//:all &> $TEST_log \
+      || fail "Build failed unexpectedly"
+}
+
+function test_platforms_can_be_overridden() {
+  # We test that a custom repository can override @platforms in their
+  # WORKSPACE file.
+  mkdir -p platforms_can_be_overridden || fail "couldn't create directory"
+  touch platforms_can_be_overridden/BUILD || \ fail "couldn't touch BUILD file"
+  cat > platforms_can_be_overridden/WORKSPACE <<EOF
+local_repository(
+  name = 'platforms',
+  path = '../override',
+)
+EOF
+
+  mkdir -p override || fail "couldn't create override directory"
+  touch override/WORKSPACE || fail "couldn't touch override/WORKSPACE"
+  cat > override/BUILD <<EOF
+filegroup(name = 'yolo')
+EOF
+
+  cd platforms_can_be_overridden || fail "couldn't cd into workspace"
+  bazel build @platforms//:yolo &> $TEST_log || \
+    fail "Bazel failed to build @platforms"
+}
+
+run_suite "platform mapping test"
+
diff --git a/src/test/shell/integration/discard_graph_edges_test.sh b/src/test/shell/integration/discard_graph_edges_test.sh
index aab82d9..926dc79 100755
--- a/src/test/shell/integration/discard_graph_edges_test.sh
+++ b/src/test/shell/integration/discard_graph_edges_test.sh
@@ -271,7 +271,7 @@
   package_count="$(extract_histogram_count "$histo_file" \
       'devtools\.build\.lib\..*\.Package$')"
   # A few packages aren't cleared.
-  [[ "$package_count" -le 16 ]] \
+  [[ "$package_count" -le 17 ]] \
       || fail "package count $package_count too high"
   glob_count="$(extract_histogram_count "$histo_file" "GlobValue$")"
   [[ "$glob_count" -le 1 ]] \