Add hermetic Clang toolchain based on Skia's

This moves things around to align them with how Skia has
organized them.

I tested these locally from a Linux host with:
    bazel build //...
    bazel test //...
    bazel build //... --config=asan
    bazel build //... --config=msan
    bazel build //... --config=android-arm
    bazel build //... --config=android-arm-rbe
    bazel build //... --config=android-arm64
    bazel build //... --config=android-arm64-rbe

Windows bazelisk asset added in
https://skia-review.googlesource.com/c/skia/+/581378

Suggested Review order:
 - .bazelrc and toolchain/BUILD.bazel to see change to Platforms
 - toolchain/download_clang_linux_amd64.bzl, which was mostly
   copied from Skia
 - toolchain/clang_linux_amd64_toolchain_config.bzl which
   was mostly copied from Skia.
 - toolchain/download_ndk_linux_amd64.bzl which was renamed
   and re-worked to emphasize that the ndk can only run
   on execution platforms that are 64 bit Linux.
 - bazel/get_workspace_status.sh, which appeared unnecessary
   given that skcms does not push docker images.
 - infra/bazel.py to see the change to use bazelisk (so we
   can make use of 5.3.0+ to make the Linux toolchain).
 - All other changes

Change-Id: Icc87b999c0727f5d3511981d74d45415a0ba4261
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/580588
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
diff --git a/.bazelrc b/.bazelrc
index 262ef4a..9f58736 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,38 +1,44 @@
-# SkCMS-specific configs
+# https://bazel.build/concepts/platforms-intro#cxx
+# This forces Bazel's C++ rules use platforms to select toolchains instead of the default
+# --crosstool_top, --compiler, etc.
+build --incompatible_enable_cc_toolchain_resolution
 
-build:clang --action_env=CC=clang
-build:clang --action_env=CXX=clang++
+# We do not want Bazel to detect any C++ toolchains on the remote machine
+# https://github.com/bazelbuild/bazel/blob/4ccc21f2f089971e5f4032397764a4be3549c40a/tools/cpp/cc_configure.bzl#L47
+build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
+
+# This tells Bazel that the host platform can use the hermetic toolchain. This is a small lie until
+# Windows support is added.
+build --host_platform=//bazel/platform:host_with_hermetic_toolchain
+
+# Register our toolchains. We do this here, and not in WORKSPACE.bazel because
+# --extra_toolchains has priority over register_toolchains and we conditionally add some toolchains
+# for RBE.
+build --extra_toolchains=//toolchain:linux_amd64_clang_toolchain
+build --extra_toolchains=//toolchain:linux_amd64_ndk_arm32_toolchain
+build --extra_toolchains=//toolchain:linux_amd64_ndk_arm64_toolchain
+
+# SkCMS-specific configs
 
 build:asan --copt -fsanitize=address
 build:asan --linkopt -fsanitize=address
 
-build:msan --config=clang
 build:msan --copt -fsanitize=memory
 build:msan --linkopt -fsanitize=memory
 
-# This sets various variables used to stamp Docker container images.
-build --workspace_status_command=bazel/get_workspace_status.sh
-
 # Depending on how many machines are in the remote execution instance, setting
 # this higher can make builds faster by allowing more jobs to run in parallel.
 # Setting it too high can result in jobs that timeout, however, while waiting
 # for a remote machine to execute them.
 build:remote --jobs=50
 
-# Set several flags related to specifying the platform, toolchain and java
-# properties.
-# These flags should only be used as is for the rbe-ubuntu16-04 container
-# and need to be adapted to work with other toolchain containers.
-#build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
-#build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
-build:remote --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
-
 # Starting with Bazel 0.27.0 strategies do not need to be explicitly
 # defined. See https://github.com/bazelbuild/bazel/issues/7480
 build:remote --define=EXECUTOR=remote
 
 # Enable remote execution so actions are performed on the remote systems.
 build:remote --remote_executor=grpcs://remotebuildexecution.googleapis.com
+build:remote --google_default_credentials=true
 
 # Enforce stricter environment rules, which eliminates some non-hermetic
 # behavior and therefore improves both the remote cache hit rate and the
@@ -47,40 +53,31 @@
 
 # Linux RBE configuration.
 build:linux-rbe --config=remote
-build:linux-rbe --crosstool_top=@rbe_linux_toolchains//cc:toolchain
 build:linux-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
 build:linux-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
-build:linux-rbe --host_platform=@rbe_linux_toolchains//config:platform
+# TODO(kjlubick, lovisolo) We should be building on RBE with the hermetic toolchain
+#   which requires updating the RBE image to have a new enough libtinfo.so.6
 build:linux-rbe --platforms=@rbe_linux_toolchains//config:platform
 
 # Windows RBE configuration.
+# Can only (currently) be invoked from a host machine that is on Windows.
 build:windows-rbe --config=remote
-build:windows-rbe --crosstool_top=@rbe_windows_toolchains//cc:toolchain
 build:windows-rbe --extra_toolchains=@rbe_windows_toolchains//config:cc-toolchain
 build:windows-rbe --extra_execution_platforms=@rbe_windows_toolchains//config:platform
-build:windows-rbe --host_platform=@rbe_windows_toolchains//config:platform
 build:windows-rbe --platforms=@rbe_windows_toolchains//config:platform
 
 # Android configuration for 32-bit ARM (armeabi-v7a ABI).
-build:android-arm --crosstool_top=//toolchain:ndk_toolchain
-build:android-arm --cpu=armeabi-v7a
-build:android-arm --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+build:android-arm --platforms=//bazel/platform:android_arm32
 
 # Android RBE configuration for 32-bit ARM (armeabi-v7a ABI).
-build:android-arm-rbe --config=remote
-build:android-arm-rbe --config=android-arm
+build:android-arm-rbe --config=remote --config=android-arm
 build:android-arm-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
 build:android-arm-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
-build:android-arm-rbe --platforms=@rbe_linux_toolchains//config:platform
 
 # Android configuration for 64-bit ARM (arm64-v8a ABI).
-build:android-arm64 --crosstool_top=//toolchain:ndk_toolchain
-build:android-arm64 --cpu=arm64-v8a
-build:android-arm64 --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+build:android-arm64 --platforms=//bazel/platform:android_arm64
 
 # Android RBE configuration for 64-bit ARM (arm64-v8a ABI).
-build:android-arm64-rbe --config=remote
-build:android-arm64-rbe --config=android-arm64
+build:android-arm64-rbe --config=remote --config=android-arm64
 build:android-arm64-rbe --extra_toolchains=@rbe_linux_toolchains//config:cc-toolchain
 build:android-arm64-rbe --extra_execution_platforms=@rbe_linux_toolchains//config:platform
-build:android-arm64-rbe --platforms=@rbe_linux_toolchains//config:platform
diff --git a/.bazelversion b/.bazelversion
index ee74734..e230c83 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-4.1.0
+5.3.0
\ No newline at end of file
diff --git a/BAZEL.md b/BAZEL.md
index 75fa199..1c55230 100644
--- a/BAZEL.md
+++ b/BAZEL.md
@@ -50,7 +50,7 @@
 ### Building and testing locally
 
 The below instructions are based on the
-[Build on Windows](https://docs.bazel.build/versions/main/windows.html#build-on-windows)
+[Build on Windows](https://bazel.build/configure/windows#using)
 section of the Bazel documentation.
 
 #### With Build Tools for Visual Studio 2019
@@ -96,7 +96,7 @@
 ```
 > bazel build //... --compiler=clang-cl
 
-> bazel test //... --compiler=clanmg-cl --enable_runfiles
+> bazel test //... --compiler=clang-cl --enable_runfiles
 ```
 
 If the above commands fail because Bazel cannot find your LLVM installation, set
@@ -119,43 +119,8 @@
 
 ## RBE Credentials
 
-Note that running remote builds requires a service account key with the correct permissions.
-
-Instructions:
-
-Step 1: Create service account under the skia-public GCP project, if you don't have one already:
-
 ```
-$ gcloud iam service-accounts create somegoogler-rbe \
-      --description "somegoogler's RBE service account" \
-      --project skia-public
+gcloud auth application-default login
 ```
 
-Step 2: Grant your service account the
-[Remote Build Execution Artifact Creator](https://cloud.google.com/remote-build-execution/docs/access-control#granting_the_ability_to_run_builds_remotely)
-role under the skia-rbe GCP project, which is where Skia's
-[RBE instance](https://pantheon.corp.google.com/apis/api/remotebuildexecution.googleapis.com/overview?project=skia-rbe)
-lives:
-
-```
-$ gcloud projects add-iam-policy-binding skia-rbe \
-      --role roles/remotebuildexecution.artifactCreator \
-      --member serviceAccount:somegoogler-rbe@skia-public.iam.gserviceaccount.com
-```
-
-Step 3: Create a JSON service account key:
-
-```
-$ gcloud iam service-accounts keys create path/to/somegoogler-rbe.json \
-      --project skia-public \
-      --iam-account somegoogler-rbe@skia-public.iam.gserviceaccount.com
-```
-
-Step 4: Create a .bazelrc file in your home directory with the following contents:
-
-```
-build:remote --google_credentials=path/to/somegoogler-rbe.json
-```
-
-Note that service account keys expire after 3 months, so you might have to repeat this step if
-you run into permission issues.
+Settings in .bazelrc should look to use those default Google cloud credentials.
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE.bazel
similarity index 89%
rename from WORKSPACE
rename to WORKSPACE.bazel
index 7e81868..19945c0 100644
--- a/WORKSPACE
+++ b/WORKSPACE.bazel
@@ -4,23 +4,21 @@
 
 local_repository(
   name = "rbe_linux_toolchains",
-  path = "bazel/toolchains/linux-bazel-4.2.1",
+  path = "bazel/rbe/linux-bazel-4.2.1",
 )
 
 local_repository(
   name = "rbe_windows_toolchains",
-  path = "bazel/toolchains/windows-bazel-4.2.1",
+  path = "bazel/rbe/windows-bazel-4.2.1",
 )
 
 ############
 # Android. #
 ############
 
-load("//toolchain:download_toolchains.bzl", "download_toolchains")
+load("//toolchain:download_toolchains.bzl", "download_toolchains_for_skcms")
 
-download_toolchains(
-    android_ndk_repository_name = "android_ndk",
-)
+download_toolchains_for_skcms("clang_linux_amd64", "ndk_linux_amd64")
 
 ##################################
 # Docker rules and dependencies. #
diff --git a/bazel/get_workspace_status.sh b/bazel/get_workspace_status.sh
deleted file mode 100755
index 6fbe6cd..0000000
--- a/bazel/get_workspace_status.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash -e
-
-# This script is intended to be passed to Bazel using the --workspace_status_command command-line
-# flag. It defines various key/value pairs, such as the Git hash or clean/dirty status, which can be
-# used from BUILD files, e.g. to tag Docker images.
-#
-# See https://docs.bazel.build/versions/master/user-manual.html#flag--workspace_status_command.
-
-# Default values used if we are outside of a Git checkout, e.g. when building inside a tryjob.
-STABLE_GIT_REVISION=unversioned
-STABLE_GIT_STATUS=unversioned
-
-# If we are inside a Git checkout, then obtain the Git revision and the clean/dirty status.
-if git status > /dev/null 2> /dev/null; then
-  STABLE_GIT_REVISION=`git rev-parse HEAD`
-
-  # Check whether there are any uncommitted changes.
-  #
-  # Based on:
-  # https://skia.googlesource.com/buildbot/+/cdbd6dc7cd9e06604042bb53a6179a77b4c83c25/bash/docker_build.sh#53
-  STABLE_GIT_STATUS=clean
-  # Detect if we have unchecked in local changes, or if we're not on the main branch (possibly at
-  # an older revision).
-  git fetch > /dev/null
-  # diff-index requires update-index --refresh; see:
-  # https://stackoverflow.com/questions/36367190/git-diff-files-output-changes-after-git-status/36439778#36439778
-  if git update-index --refresh > /dev/null ; then
-    if ! git diff-index --quiet HEAD -- ; then
-      # Repository is dirty due to modified files.
-      STABLE_GIT_STATUS=dirty
-    elif ! git merge-base --is-ancestor HEAD origin/main ; then
-      # Repository is dirty because we're not on the main branch (possibly an older revision).
-      STABLE_GIT_STATUS=dirty
-    fi
-  else
-    # Repository is dirty due to checked out files.
-    STABLE_GIT_STATUS=dirty
-  fi
-fi
-
-BUILD_DATETIME=`date -u +%Y-%m-%dT%H_%M_%SZ`
-
-echo "BUILD_DATETIME $BUILD_DATETIME"
-echo "STABLE_GIT_REVISION $STABLE_GIT_REVISION"
-echo "STABLE_GIT_STATUS $STABLE_GIT_STATUS"
-
-# If the format of this ever changes then please also update k8s_checker/main.go.
-echo "STABLE_DOCKER_TAG ${BUILD_DATETIME}-${USER}-${STABLE_GIT_REVISION:0:7}-${STABLE_GIT_STATUS}"
diff --git a/bazel/platform/BUILD.bazel b/bazel/platform/BUILD.bazel
new file mode 100644
index 0000000..aacf1bd
--- /dev/null
+++ b/bazel/platform/BUILD.bazel
@@ -0,0 +1,46 @@
+# https://bazel.build/concepts/platforms-intro
+# https://bazel.build/docs/platforms
+platform(
+    name = "android_arm32",
+    constraint_values = [
+        "@platforms//os:android",  # https://github.com/bazelbuild/platforms/blob/main/os/BUILD
+        "@platforms//cpu:armv7",  # https://github.com/bazelbuild/platforms/blob/main/cpu/BUILD
+    ],
+)
+
+platform(
+    name = "android_arm64",
+    constraint_values = [
+        "@platforms//os:android",
+        "@platforms//cpu:arm64",
+    ],
+)
+
+platform(
+    name = "linux_x64_hermetic",
+    constraint_values = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+        ":use_hermetic_toolchain",
+    ],
+)
+
+platform(
+    name = "host_with_hermetic_toolchain",
+    constraint_values = [
+        ":use_hermetic_toolchain",
+    ],
+    parents = ["@local_config_platform//:host"],
+)
+
+# This constraint allows us to force Bazel to resolve our hermetic toolchain to build
+# the target and not a default one (e.g. on the Linux RBE instance). We do this by
+# adding the constraint to our platforms that describe the target we want Bazel to build for.
+# https://bazel.build/reference/be/platform#constraint_setting
+constraint_setting(name = "skcms_hermetic_toolchain")
+
+constraint_value(
+    name = "use_hermetic_toolchain",
+    constraint_setting = ":skcms_hermetic_toolchain",
+    visibility = ["//visibility:public"],
+)
diff --git a/bazel/toolchains/README.md b/bazel/rbe/README.md
similarity index 84%
rename from bazel/toolchains/README.md
rename to bazel/rbe/README.md
index 28d1882..25fe0ff 100644
--- a/bazel/toolchains/README.md
+++ b/bazel/rbe/README.md
@@ -31,7 +31,7 @@
 
 #### Step 2
 
-Generate a new `//bazel/toolchains/linux-bazel-<BAZEL VERSION>` directory with the
+Generate a new `//bazel/rbe/linux-bazel-<BAZEL VERSION>` directory with the
 `rbe_configs_gen` CLI tool:
 
 ```
@@ -40,22 +40,22 @@
       --bazel_version=<BAZEL VERSION> \
       --toolchain_container=gcr.io/skia-public/rbe-container-skcms-linux@sha256:<HASH OF MOST RECENT IMAGE> \
       --output_src_root=<PATH TO REPOSITORY CHECKOUT> \
-      --output_config_path=bazel/toolchains/linux-bazel-<BAZEL VERSION> \
+      --output_config_path=bazel/rbe/linux-bazel-<BAZEL VERSION> \
       --generate_java_configs=false \
       --exec_os=linux \
       --target_os=linux
 ```
 
 If `rbe_configs_gen` fails, try deleting all files under
-`//bazel/toolchains/linux-bazel-<BAZEL VERSION>` (if it exists) and re-run `rbe_configs_gen`.
+`//bazel/rbe/linux-bazel-<BAZEL VERSION>` (if it exists) and re-run `rbe_configs_gen`.
 
 #### Step 3
 
-Add an empty `//bazel/toolchains/linux-bazel-<BAZEL VERSION>/WORKSPACE` file.
+Add an empty `//bazel/rbe/linux-bazel-<BAZEL VERSION>/WORKSPACE` file.
 
 #### Step 4
 
-Open file `//bazel/toolchains/linux-bazel-<BAZEL VERSION>/config/BUILD`, look for the `toolchain`
+Open file `//bazel/rbe/linux-bazel-<BAZEL VERSION>/config/BUILD`, look for the `toolchain`
 rule named `cc-toolchain`, and change the `toolchain` attribute as follows:
 
 ```
@@ -63,7 +63,7 @@
 toolchain(
     name = "cc-toolchain",
     ...
-    toolchain = "//bazel/toolchains/linux-bazel-4.2.1/cc:cc-compiler-k8",
+    toolchain = "//bazel/rbe/linux-bazel-4.2.1/cc:cc-compiler-k8",
     ...
 )
 
@@ -78,7 +78,7 @@
 
 #### Step 5
 
-Open file `//bazel/toolchains/linux-bazel-<BAZEL VERSION>/config/BUILD`, look for the `platform`
+Open file `//bazel/rbe/linux-bazel-<BAZEL VERSION>/config/BUILD`, look for the `platform`
 rule named `platform`, and make change the `exec_properties` attribute as follows:
 
 ```
@@ -137,7 +137,7 @@
       --bazel_version=<BAZEL VERSION> \
       --toolchain_container=gcr.io/skia-public/rbe-container-skia-windows@sha256:<HASH OF MOST RECENT IMAGE> \
       --output_src_root=<PATH TO REPOSITORY CHECKOUT> \
-      --output_config_path=bazel/toolchains/windows-bazel-<BAZEL VERSION> \
+      --output_config_path=bazel/rbe/windows-bazel-<BAZEL VERSION> \
       --generate_java_configs=false \
       --exec_os=windows \
       --target_os=windows
diff --git a/bazel/toolchains/linux-bazel-4.2.1/LICENSE b/bazel/rbe/linux-bazel-4.2.1/LICENSE
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/LICENSE
rename to bazel/rbe/linux-bazel-4.2.1/LICENSE
diff --git a/bazel/toolchains/linux-bazel-4.2.1/WORKSPACE b/bazel/rbe/linux-bazel-4.2.1/WORKSPACE
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/WORKSPACE
rename to bazel/rbe/linux-bazel-4.2.1/WORKSPACE
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/BUILD b/bazel/rbe/linux-bazel-4.2.1/cc/BUILD
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/BUILD
rename to bazel/rbe/linux-bazel-4.2.1/cc/BUILD
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/WORKSPACE b/bazel/rbe/linux-bazel-4.2.1/cc/WORKSPACE
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/WORKSPACE
rename to bazel/rbe/linux-bazel-4.2.1/cc/WORKSPACE
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl b/bazel/rbe/linux-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
rename to bazel/rbe/linux-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/builtin_include_directory_paths b/bazel/rbe/linux-bazel-4.2.1/cc/builtin_include_directory_paths
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/builtin_include_directory_paths
rename to bazel/rbe/linux-bazel-4.2.1/cc/builtin_include_directory_paths
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/cc_toolchain_config.bzl b/bazel/rbe/linux-bazel-4.2.1/cc/cc_toolchain_config.bzl
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/cc_toolchain_config.bzl
rename to bazel/rbe/linux-bazel-4.2.1/cc/cc_toolchain_config.bzl
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/cc_wrapper.sh b/bazel/rbe/linux-bazel-4.2.1/cc/cc_wrapper.sh
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/cc_wrapper.sh
rename to bazel/rbe/linux-bazel-4.2.1/cc/cc_wrapper.sh
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/module.modulemap b/bazel/rbe/linux-bazel-4.2.1/cc/module.modulemap
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/module.modulemap
rename to bazel/rbe/linux-bazel-4.2.1/cc/module.modulemap
diff --git a/bazel/toolchains/linux-bazel-4.2.1/cc/tools/cpp/empty.cc b/bazel/rbe/linux-bazel-4.2.1/cc/tools/cpp/empty.cc
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/cc/tools/cpp/empty.cc
rename to bazel/rbe/linux-bazel-4.2.1/cc/tools/cpp/empty.cc
diff --git a/bazel/toolchains/linux-bazel-4.2.1/config/BUILD b/bazel/rbe/linux-bazel-4.2.1/config/BUILD
similarity index 100%
rename from bazel/toolchains/linux-bazel-4.2.1/config/BUILD
rename to bazel/rbe/linux-bazel-4.2.1/config/BUILD
diff --git a/bazel/toolchains/windows-bazel-4.2.1/LICENSE b/bazel/rbe/windows-bazel-4.2.1/LICENSE
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/LICENSE
rename to bazel/rbe/windows-bazel-4.2.1/LICENSE
diff --git a/bazel/toolchains/windows-bazel-4.2.1/WORKSPACE b/bazel/rbe/windows-bazel-4.2.1/WORKSPACE
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/WORKSPACE
rename to bazel/rbe/windows-bazel-4.2.1/WORKSPACE
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/BUILD b/bazel/rbe/windows-bazel-4.2.1/cc/BUILD
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/BUILD
rename to bazel/rbe/windows-bazel-4.2.1/cc/BUILD
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/WORKSPACE b/bazel/rbe/windows-bazel-4.2.1/cc/WORKSPACE
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/WORKSPACE
rename to bazel/rbe/windows-bazel-4.2.1/cc/WORKSPACE
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl b/bazel/rbe/windows-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
rename to bazel/rbe/windows-bazel-4.2.1/cc/armeabi_cc_toolchain_config.bzl
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_clangcl b/bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_clangcl
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_clangcl
rename to bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_clangcl
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_mingw b/bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_mingw
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_mingw
rename to bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_mingw
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_msvc b/bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_msvc
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/builtin_include_directory_paths_msvc
rename to bazel/rbe/windows-bazel-4.2.1/cc/builtin_include_directory_paths_msvc
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/clang_installation_error.bat b/bazel/rbe/windows-bazel-4.2.1/cc/clang_installation_error.bat
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/clang_installation_error.bat
rename to bazel/rbe/windows-bazel-4.2.1/cc/clang_installation_error.bat
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/get_env.bat b/bazel/rbe/windows-bazel-4.2.1/cc/get_env.bat
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/get_env.bat
rename to bazel/rbe/windows-bazel-4.2.1/cc/get_env.bat
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/msys_gcc_installation_error.bat b/bazel/rbe/windows-bazel-4.2.1/cc/msys_gcc_installation_error.bat
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/msys_gcc_installation_error.bat
rename to bazel/rbe/windows-bazel-4.2.1/cc/msys_gcc_installation_error.bat
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/vc_installation_error_arm.bat b/bazel/rbe/windows-bazel-4.2.1/cc/vc_installation_error_arm.bat
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/vc_installation_error_arm.bat
rename to bazel/rbe/windows-bazel-4.2.1/cc/vc_installation_error_arm.bat
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/vc_installation_error_arm64.bat b/bazel/rbe/windows-bazel-4.2.1/cc/vc_installation_error_arm64.bat
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/vc_installation_error_arm64.bat
rename to bazel/rbe/windows-bazel-4.2.1/cc/vc_installation_error_arm64.bat
diff --git a/bazel/toolchains/windows-bazel-4.2.1/cc/windows_cc_toolchain_config.bzl b/bazel/rbe/windows-bazel-4.2.1/cc/windows_cc_toolchain_config.bzl
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/cc/windows_cc_toolchain_config.bzl
rename to bazel/rbe/windows-bazel-4.2.1/cc/windows_cc_toolchain_config.bzl
diff --git a/bazel/toolchains/windows-bazel-4.2.1/config/BUILD b/bazel/rbe/windows-bazel-4.2.1/config/BUILD
similarity index 100%
rename from bazel/toolchains/windows-bazel-4.2.1/config/BUILD
rename to bazel/rbe/windows-bazel-4.2.1/config/BUILD
diff --git a/infra/bots/bazel.py b/infra/bots/bazel.py
index 99b1e37..a9a8c68 100644
--- a/infra/bots/bazel.py
+++ b/infra/bots/bazel.py
@@ -51,8 +51,9 @@
   with tempfile.TemporaryDirectory(prefix="bazel-cache-",
                                    dir=os.environ["TMPDIR"]) as cache_dir:
     def bazel(args):
-      cmd = ["C:\\b\\s\\w\\ir\\bazel_win\\bazel.exe"] if target == "windows" \
-            else ["bazel", "--output_user_root=" + cache_dir]
+      cmd = ["C:\\b\\s\\w\\ir\\bazelisk\\bazelisk.exe"] if target == "windows" \
+            else ["bazelisk", "--output_user_root=" + cache_dir]
+      print("Running", cmd)
       call(cmd + args)
 
     try:
@@ -66,7 +67,7 @@
 
       # Run the requested Bazel command.
       os.chdir("skcms")
-      bazel([build_or_test, "//...", "--config=" + config, "--google_default_credentials"])
+      bazel([build_or_test, "//...", "--config=" + config])
 
     finally:
       # Kill the Bazel server, so as not to leave any children processes
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index a7755d6..9d3c5cb 100644
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -189,9 +189,9 @@
           "version": "version:2@3.8.10.chromium.19"
         },
         {
-          "name": "skia/bots/bazel",
-          "path": "bazel",
-          "version": "version:3"
+          "name": "skia/bots/bazelisk",
+          "path": "bazelisk",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -213,7 +213,7 @@
         "PATH": [
           "cipd_bin_packages/cpython3",
           "cipd_bin_packages/cpython3/bin",
-          "bazel/bin"
+          "bazelisk"
         ],
         "VPYTHON_VIRTUALENV_ROOT": [
           "cache/vpython"
@@ -237,9 +237,9 @@
           "version": "version:2@3.8.10.chromium.19"
         },
         {
-          "name": "skia/bots/bazel",
-          "path": "bazel",
-          "version": "version:3"
+          "name": "skia/bots/bazelisk",
+          "path": "bazelisk",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -261,7 +261,7 @@
         "PATH": [
           "cipd_bin_packages/cpython3",
           "cipd_bin_packages/cpython3/bin",
-          "bazel/bin"
+          "bazelisk"
         ],
         "VPYTHON_VIRTUALENV_ROOT": [
           "cache/vpython"
@@ -284,8 +284,8 @@
           "version": "version:11"
         },
         {
-          "name": "skia/bots/bazel_win",
-          "path": "bazel_win",
+          "name": "skia/bots/bazelisk_win_amd64",
+          "path": "bazelisk",
           "version": "version:0"
         }
       ],
@@ -297,7 +297,7 @@
         "windows"
       ],
       "env_prefixes": {
-        "PATH": ["win_toolchain/sys64"]
+        "PATH": ["win_toolchain/sys64", "bazelisk"]
       },
       "dimensions": ["os:Windows-Server-17763", "gpu:none", "pool:Skia"],
       "max_attempts": 1,
@@ -317,8 +317,8 @@
           "version": "version:11"
         },
         {
-          "name": "skia/bots/bazel_win",
-          "path": "bazel_win",
+          "name": "skia/bots/bazelisk_win_amd64",
+          "path": "bazelisk",
           "version": "version:0"
         }
       ],
@@ -330,7 +330,7 @@
         "windows"
       ],
       "env_prefixes": {
-        "PATH": ["win_toolchain/sys64"]
+        "PATH": ["win_toolchain/sys64", "bazelisk"]
       },
       "dimensions": ["os:Windows-Server-17763", "gpu:none", "pool:Skia"],
       "max_attempts": 1,
@@ -356,9 +356,9 @@
           "version": "version:16"
         },
         {
-          "name": "skia/bots/bazel",
-          "path": "bazel",
-          "version": "version:3"
+          "name": "skia/bots/bazelisk",
+          "path": "bazelisk",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -383,7 +383,7 @@
         "PATH": [
           "cipd_bin_packages/cpython3",
           "cipd_bin_packages/cpython3/bin",
-          "bazel/bin"
+          "bazelisk"
         ],
         "VPYTHON_VIRTUALENV_ROOT": [
           "cache/vpython"
@@ -412,9 +412,9 @@
           "version": "version:16"
         },
         {
-          "name": "skia/bots/bazel",
-          "path": "bazel",
-          "version": "version:3"
+          "name": "skia/bots/bazelisk",
+          "path": "bazelisk",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -439,7 +439,7 @@
         "PATH": [
           "cipd_bin_packages/cpython3",
           "cipd_bin_packages/cpython3/bin",
-          "bazel/bin"
+          "bazelisk"
         ],
         "VPYTHON_VIRTUALENV_ROOT": [
           "cache/vpython"
diff --git a/toolchain/BUILD.bazel b/toolchain/BUILD.bazel
index 091c575..e69dd59 100644
--- a/toolchain/BUILD.bazel
+++ b/toolchain/BUILD.bazel
@@ -1,17 +1,104 @@
-load(":ndk_cc_toolchain_config.bzl", "ndk_cc_toolchain_config")
+load(":ndk_linux_arm64_toolchain_config.bzl", "ndk_cc_toolchain_config")
+load(":clang_linux_amd64_toolchain_config.bzl", "provide_linux_amd64_toolchain_config")
 
 package(default_visibility = ["//visibility:public"])
 
-# https://bazel.build/reference/be/c-cpp#cc_toolchain_suite
-cc_toolchain_suite(
-    name = "ndk_toolchain",
-    toolchains = {
-        "arm64-v8a": ":ndk_arm64-v8a_toolchain",
-        "armeabi-v7a": ":ndk_armeabi-v7a_toolchain",
-    },
-    tags = [
-      "manual",  # Exclude it from wildcard queries, e.g. "bazel build //...".
+toolchain(
+    name = "linux_amd64_clang_toolchain",
+    # Where should we run this toolchain?
+    exec_compatible_with = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
     ],
+    # What can this toolchain build?
+    target_compatible_with = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+        # We want to be able to explicitly tell Bazel to use this toolchain, and not the
+        # default one on a user's machine or on the RBE worker. Thus we need an extra constraint
+        # that we can use to differentiate the "stock" C++ toolchain from our hermetic one and
+        # force that use by specifying the target platform.
+        "//bazel/platform:use_hermetic_toolchain",
+    ],
+    toolchain = ":linux_amd64_clang",
+    # https://github.com/bazelbuild/rules_cc/blob/8bb0eb5c5ccd96b91753bb112096bb6993d16d13/cc/BUILD#L32-L36
+    toolchain_type = "@rules_cc//cc:toolchain_type",
+)
+
+toolchain(
+    name = "linux_amd64_ndk_arm64_toolchain",
+    exec_compatible_with = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+    ],
+    target_compatible_with = [
+        "@platforms//os:android",
+        "@platforms//cpu:arm64",
+    ],
+    toolchain = ":linux_amd64_ndk_arm64",
+    toolchain_type = "@rules_cc//cc:toolchain_type",
+)
+
+toolchain(
+    name = "linux_amd64_ndk_arm32_toolchain",
+    exec_compatible_with = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+    ],
+    target_compatible_with = [
+        "@platforms//os:android",
+        "@platforms//cpu:armv7",
+    ],
+    toolchain = ":linux_amd64_ndk_arm32",
+    toolchain_type = "@rules_cc//cc:toolchain_type",
+)
+
+filegroup(name = "not_implemented")
+
+###################################
+# clang on linux amd64 toolchain. #
+###################################
+
+provide_linux_amd64_toolchain_config(
+    name = "linux_amd64_toolchain_config",
+)
+
+filegroup(
+    name = "archive_linux_amd64_files",
+    srcs = [
+        "linux_amd64_trampolines/ar.sh",
+        "@clang_linux_amd64//:archive_files",
+    ],
+)
+
+filegroup(
+    name = "compile_linux_amd64_files",
+    srcs = [
+        "linux_amd64_trampolines/clang.sh",
+        "@clang_linux_amd64//:compile_files",
+    ],
+)
+
+filegroup(
+    name = "link_linux_amd64_files",
+    srcs = [
+        # Bazel assumes it is talking to Clang when linking.
+        "linux_amd64_trampolines/clang.sh",
+        "@clang_linux_amd64//:link_files",
+    ],
+)
+
+cc_toolchain(
+    name = "linux_amd64_clang",
+    all_files = ":not_implemented",
+    ar_files = ":archive_linux_amd64_files",
+    compiler_files = ":compile_linux_amd64_files",
+    dwp_files = ":not_implemented",
+    linker_files = ":link_linux_amd64_files",
+    objcopy_files = ":not_implemented",
+    strip_files = ":not_implemented",
+    supports_param_files = False,
+    toolchain_config = ":linux_amd64_toolchain_config",
 )
 
 ############################
@@ -20,28 +107,27 @@
 
 # https://bazel.build/reference/be/c-cpp#cc_toolchain
 cc_toolchain(
-    name = "ndk_arm64-v8a_toolchain",
-    all_files = ":ndk_arm64-v8a_toolchain_all_files",
-    ar_files = ":ndk_arm64-v8a_toolchain_all_files",
-    compiler_files = ":ndk_arm64-v8a_toolchain_all_files",
-    dwp_files = ":ndk_arm64-v8a_toolchain_all_files",
-    dynamic_runtime_lib = "@android_ndk//:arm64-v8a_dynamic_runtime_libraries",
-    linker_files = ":ndk_arm64-v8a_toolchain_all_files",
-    objcopy_files = ":ndk_arm64-v8a_toolchain_all_files",
-    strip_files = ":ndk_arm64-v8a_toolchain_all_files",
-    static_runtime_lib = "@android_ndk//:arm64-v8a_static_runtime_libraries",
+    name = "linux_amd64_ndk_arm64",
+    all_files = ":not_implemented",
+    ar_files = ":ndk_arm64_v8a_toolchain_all_files",
+    compiler_files = ":ndk_arm64_v8a_toolchain_all_files",
+    dwp_files = ":not_implemented",
+    dynamic_runtime_lib = "@ndk_linux_amd64//:arm64_v8a_dynamic_runtime_libraries",
+    linker_files = ":ndk_arm64_v8a_toolchain_all_files",
+    objcopy_files = ":not_implemented",
+    static_runtime_lib = "@ndk_linux_amd64//:arm64_v8a_static_runtime_libraries",
+    strip_files = ":not_implemented",
     supports_param_files = False,
-    toolchain_identifier = "ndk-arm64-v8a-toolchain",
-    toolchain_config = ":ndk_arm64-v8a_toolchain_config",
+    toolchain_config = ":ndk_arm64_v8a_toolchain_config",
 )
 
 filegroup(
-    name = "ndk_arm64-v8a_toolchain_all_files",
-    srcs = glob(["trampolines/*.sh"]) + ["@android_ndk//:arm64-v8a_all_files"],
+    name = "ndk_arm64_v8a_toolchain_all_files",
+    srcs = glob(["android_trampolines/*.sh"]) + ["@ndk_linux_amd64//:arm64_v8a_all_files"],
 )
 
 ndk_cc_toolchain_config(
-    name = "ndk_arm64-v8a_toolchain_config",
+    name = "ndk_arm64_v8a_toolchain_config",
     cpu = "arm64-v8a",
 )
 
@@ -51,27 +137,26 @@
 
 # https://bazel.build/reference/be/c-cpp#cc_toolchain
 cc_toolchain(
-    name = "ndk_armeabi-v7a_toolchain",
-    all_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    ar_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    compiler_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    dwp_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    dynamic_runtime_lib = "@android_ndk//:armeabi-v7a_dynamic_runtime_libraries",
-    linker_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    objcopy_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    strip_files = ":ndk_armeabi-v7a_toolchain_all_files",
-    static_runtime_lib = "@android_ndk//:armeabi-v7a_static_runtime_libraries",
+    name = "linux_amd64_ndk_arm32",
+    all_files = ":not_implemented",
+    ar_files = ":ndk_armeabi_v7a_toolchain_all_files",
+    compiler_files = ":ndk_armeabi_v7a_toolchain_all_files",
+    dwp_files = ":not_implemented",
+    dynamic_runtime_lib = "@ndk_linux_amd64//:armeabi_v7a_dynamic_runtime_libraries",
+    linker_files = ":ndk_armeabi_v7a_toolchain_all_files",
+    objcopy_files = ":not_implemented",
+    static_runtime_lib = "@ndk_linux_amd64//:armeabi_v7a_static_runtime_libraries",
+    strip_files = ":not_implemented",
     supports_param_files = False,
-    toolchain_identifier = "ndk-armeabi-v7a-toolchain",
-    toolchain_config = ":ndk_armeabi-v7a_toolchain_config",
+    toolchain_config = ":ndk_armeabi_v7a_toolchain_config",
 )
 
 filegroup(
-    name = "ndk_armeabi-v7a_toolchain_all_files",
-    srcs = glob(["trampolines/*.sh"]) + ["@android_ndk//:armeabi-v7a_all_files"],
+    name = "ndk_armeabi_v7a_toolchain_all_files",
+    srcs = glob(["android_trampolines/*.sh"]) + ["@ndk_linux_amd64//:armeabi_v7a_all_files"],
 )
 
 ndk_cc_toolchain_config(
-    name = "ndk_armeabi-v7a_toolchain_config",
+    name = "ndk_armeabi_v7a_toolchain_config",
     cpu = "armeabi-v7a",
 )
diff --git a/toolchain/README.md b/toolchain/README.md
index dc278ef..e587b10 100644
--- a/toolchain/README.md
+++ b/toolchain/README.md
@@ -12,7 +12,7 @@
 installation.
 
 This directory provides a `download_android_ndk` repository rule, which downloads the Android NDK
-under `external/android_ndk`, and a C++ toolchain suite that targets 32- and 64-bit ARM.
+under `external/ndk_linux_amd64`, and a C++ toolchain suite that targets 32- and 64-bit ARM.
 
 ## Design
 
@@ -32,6 +32,6 @@
 The
 [`cc_common.create_cc_toolchain_config_info`](https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info)
 function expects tool paths to point to files under the directory in which it is invoked. This
-means we cannot directly reference tools under `external/android_ndk`. The solution is to use
+means we cannot directly reference tools under `external/ndk_linux_amd64`. The solution is to use
 "trampoline" scripts that pass through any command-line arguments to the NDK binaries under
 `external/android_sdk`.
diff --git a/toolchain/android_trampolines/aarch64-linux-android-ar.sh b/toolchain/android_trampolines/aarch64-linux-android-ar.sh
new file mode 100755
index 0000000..b51f0bf
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-ar.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-dwp.sh b/toolchain/android_trampolines/aarch64-linux-android-dwp.sh
new file mode 100755
index 0000000..5392836
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-dwp.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-dwp $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-ld.sh b/toolchain/android_trampolines/aarch64-linux-android-ld.sh
new file mode 100755
index 0000000..1a2e2eb
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-ld.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-nm.sh b/toolchain/android_trampolines/aarch64-linux-android-nm.sh
new file mode 100755
index 0000000..d042102
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-nm.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-nm $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-objcopy.sh b/toolchain/android_trampolines/aarch64-linux-android-objcopy.sh
new file mode 100755
index 0000000..6493e60
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-objcopy.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-objdump.sh b/toolchain/android_trampolines/aarch64-linux-android-objdump.sh
new file mode 100755
index 0000000..e11bbbc
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-objdump.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump $@
diff --git a/toolchain/android_trampolines/aarch64-linux-android-strip.sh b/toolchain/android_trampolines/aarch64-linux-android-strip.sh
new file mode 100755
index 0000000..1d96b13
--- /dev/null
+++ b/toolchain/android_trampolines/aarch64-linux-android-strip.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-ar.sh b/toolchain/android_trampolines/arm-linux-androideabi-ar.sh
new file mode 100755
index 0000000..a492872
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-ar.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-dwp.sh b/toolchain/android_trampolines/arm-linux-androideabi-dwp.sh
new file mode 100755
index 0000000..5432509
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-dwp.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-dwp $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-ld.sh b/toolchain/android_trampolines/arm-linux-androideabi-ld.sh
new file mode 100755
index 0000000..d980e96
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-ld.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ld $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-nm.sh b/toolchain/android_trampolines/arm-linux-androideabi-nm.sh
new file mode 100755
index 0000000..e166d0f
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-nm.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-nm $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-objcopy.sh b/toolchain/android_trampolines/arm-linux-androideabi-objcopy.sh
new file mode 100755
index 0000000..4c89c55
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-objcopy.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-objdump.sh b/toolchain/android_trampolines/arm-linux-androideabi-objdump.sh
new file mode 100755
index 0000000..ba09a2f
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-objdump.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump $@
diff --git a/toolchain/android_trampolines/arm-linux-androideabi-strip.sh b/toolchain/android_trampolines/arm-linux-androideabi-strip.sh
new file mode 100755
index 0000000..1b33c8b
--- /dev/null
+++ b/toolchain/android_trampolines/arm-linux-androideabi-strip.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip $@
diff --git a/toolchain/android_trampolines/clang.sh b/toolchain/android_trampolines/clang.sh
new file mode 100755
index 0000000..8377329
--- /dev/null
+++ b/toolchain/android_trampolines/clang.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+external/ndk_linux_amd64/toolchains/llvm/prebuilt/linux-x86_64/bin/clang $@
diff --git a/toolchain/trampolines/gen_trampolines/gen_trampolines.go b/toolchain/android_trampolines/gen_trampolines/gen_trampolines.go
similarity index 97%
rename from toolchain/trampolines/gen_trampolines/gen_trampolines.go
rename to toolchain/android_trampolines/gen_trampolines/gen_trampolines.go
index 68e66a3..c873a91 100644
--- a/toolchain/trampolines/gen_trampolines/gen_trampolines.go
+++ b/toolchain/android_trampolines/gen_trampolines/gen_trampolines.go
@@ -5,7 +5,7 @@
 //
 // Trampoline scripts are necessary because the `cc_common.create_cc_toolchain_config_info`[1]
 // built-in Bazel function expects tool paths to point to files under the directory in which it is
-// invoked, thus we cannot directly reference tools under `external/android_ndk`. The solution is
+// invoked, thus we cannot directly reference tools under `external/ndk_linux_amd64`. The solution is
 // to use trampoline scripts that pass through any command-line arguments to the NDK binaries under
 // `external/android_sdk`.
 //
@@ -20,7 +20,7 @@
 	"path/filepath"
 )
 
-const bazelNdkPath = "external/android_ndk"
+const bazelNdkPath = "external/ndk_linux_amd64"
 
 // Paths relative to the Android NDK root directory. These paths can be determined by inspecting
 // the Android NDK ZIP file downloaded by the `download_toolchains` macro defined in
diff --git a/toolchain/clang_linux_amd64_toolchain_config.bzl b/toolchain/clang_linux_amd64_toolchain_config.bzl
new file mode 100644
index 0000000..d4da451
--- /dev/null
+++ b/toolchain/clang_linux_amd64_toolchain_config.bzl
@@ -0,0 +1,338 @@
+"""
+This file specifies a clang toolchain that can run on a Linux host which doesn't depend on any
+installed packages from the host machine.
+
+See download_clang_linux_amd64.bzl for more details on the creation of the toolchain.
+
+It uses the usr subfolder of the built toolchain as a sysroot
+
+This is largely copied from Skia's clang toolchain.
+"""
+
+load(
+    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+    "action_config",
+    "feature",
+    "flag_group",
+    "flag_set",
+    "tool",
+    "variable_with_value",
+)
+
+# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl
+load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+
+# The location of the created clang toolchain.
+EXTERNAL_TOOLCHAIN = "external/clang_linux_amd64"
+
+def _linux_amd64_toolchain_info(ctx):
+    action_configs = _make_action_configs()
+    features = []
+    features += _make_default_flags()
+    features += _make_diagnostic_flags()
+
+    # https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info
+    # Note, this rule is defined in Java code, not Starlark
+    # https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        # This is important because the linker will complain if the libc shared libraries are not
+        # under this directory. Because we extract the libc libraries to
+        # EXTERNAL_TOOLCHAIN/lib, and the various headers and shared libraries to
+        # EXTERNAL_TOOLCHAIN/usr, we make the top level folder the sysroot so the linker can
+        # find the referenced libraries (e.g. EXTERNAL_TOOLCHAIN/usr/lib/x86_64-linux-gnu/libc.so
+        # is just a text file that refers to "/lib/x86_64-linux-gnu/libc.so.6" and
+        # "/lib64/ld-linux-x86-64.so.2" which will use the sysroot as the root).
+        builtin_sysroot = EXTERNAL_TOOLCHAIN,
+        # These are required, but do nothing
+        abi_libc_version = "",
+        abi_version = "",
+        compiler = "",
+        host_system_name = "",
+        target_cpu = "",
+        target_libc = "",
+        target_system_name = "",
+        toolchain_identifier = "",
+    )
+
+provide_linux_amd64_toolchain_config = rule(
+    attrs = {},
+    provides = [CcToolchainConfigInfo],
+    implementation = _linux_amd64_toolchain_info,
+)
+
+def _make_action_configs():
+    """
+    This function sets up the tools needed to perform the various compile/link actions.
+
+    Bazel normally restricts us to referring to (and therefore running) executables/scripts
+    that are in this directory (That is EXEC_ROOT/toolchain). However, the executables we want
+    to run are brought in via WORKSPACE.bazel and are located in EXEC_ROOT/external/clang....
+    Therefore, we make use of "trampoline scripts" that will call the binaries from the
+    toolchain directory.
+
+    These action_configs also let us dynamically specify arguments from the Bazel
+    environment if necessary (see cpp_link_static_library_action).
+    """
+
+    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=435;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
+    clang_tool = tool(path = "linux_amd64_trampolines/clang.sh")
+    lld_tool = tool(path = "linux_amd64_trampolines/lld.sh")
+    ar_tool = tool(path = "linux_amd64_trampolines/ar.sh")
+
+    # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da
+    assemble_action = action_config(
+        action_name = ACTION_NAMES.assemble,
+        tools = [clang_tool],
+    )
+    c_compile_action = action_config(
+        action_name = ACTION_NAMES.c_compile,
+        tools = [clang_tool],
+    )
+    cpp_compile_action = action_config(
+        action_name = ACTION_NAMES.cpp_compile,
+        tools = [clang_tool],
+    )
+    linkstamp_compile_action = action_config(
+        action_name = ACTION_NAMES.linkstamp_compile,
+        tools = [clang_tool],
+    )
+    preprocess_assemble_action = action_config(
+        action_name = ACTION_NAMES.preprocess_assemble,
+        tools = [clang_tool],
+    )
+
+    cpp_link_dynamic_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_dynamic_library,
+        tools = [lld_tool],
+    )
+    cpp_link_executable_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_executable,
+        # Bazel assumes it is talking to clang when building an executable. There are
+        # "-Wl" flags on the command: https://releases.llvm.org/6.0.1/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-Wl
+        tools = [clang_tool],
+    )
+    cpp_link_nodeps_dynamic_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+        tools = [lld_tool],
+    )
+
+    # This is the same rule as
+    # https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl#L143
+    # By default, there are no flags or libraries passed to the llvm-ar tool, so
+    # we need to specify them. The variables mentioned by expand_if_available are defined
+    # https://docs.bazel.build/versions/main/cc-toolchain-config-reference.html#cctoolchainconfiginfo-build-variables
+    cpp_link_static_library_action = action_config(
+        action_name = ACTION_NAMES.cpp_link_static_library,
+        flag_sets = [
+            flag_set(
+                flag_groups = [
+                    flag_group(
+                        # https://llvm.org/docs/CommandGuide/llvm-ar.html
+                        # replace existing files or insert them if they already exist,
+                        # create the file if it doesn't already exist
+                        # symbol table should be added
+                        # Deterministic timestamps should be used
+                        flags = ["rcsD", "%{output_execpath}"],
+                        # Despite the name, output_execpath just refers to linker output,
+                        # e.g. libFoo.a
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+            flag_set(
+                flag_groups = [
+                    flag_group(
+                        iterate_over = "libraries_to_link",
+                        flag_groups = [
+                            flag_group(
+                                flags = ["%{libraries_to_link.name}"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["%{libraries_to_link.object_files}"],
+                                iterate_over = "libraries_to_link.object_files",
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file_group",
+                                ),
+                            ),
+                        ],
+                        expand_if_available = "libraries_to_link",
+                    ),
+                ],
+            ),
+            flag_set(
+                flag_groups = [
+                    flag_group(
+                        flags = ["@%{linker_param_file}"],
+                        expand_if_available = "linker_param_file",
+                    ),
+                ],
+            ),
+        ],
+        tools = [ar_tool],
+    )
+
+    action_configs = [
+        assemble_action,
+        c_compile_action,
+        cpp_compile_action,
+        cpp_link_dynamic_library_action,
+        cpp_link_executable_action,
+        cpp_link_nodeps_dynamic_library_action,
+        cpp_link_static_library_action,
+        linkstamp_compile_action,
+        preprocess_assemble_action,
+    ]
+    return action_configs
+
+def _make_default_flags():
+    """Here we define the flags for certain actions that are always applied.
+
+    For any flag that might be conditionally applied, it should be defined in //bazel/copts.bzl.
+
+    Flags that are set here will be unconditionally applied to everything we compile with
+    this toolchain, even third_party deps.
+    """
+
+    # Note: These values must be kept in sync with those defined in cmake_exporter.go.
+    cxx_compile_includes = flag_set(
+        actions = [
+            ACTION_NAMES.c_compile,
+            ACTION_NAMES.cpp_compile,
+        ],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    # THIS ORDER MATTERS GREATLY. If these are in the wrong order, the
+                    # #include_next directives will fail to find the files, causing a compilation
+                    # error (or, without -no-canonical-prefixes, a mysterious case where files
+                    # are included with an absolute path and fail the build).
+                    "-isystem",
+                    EXTERNAL_TOOLCHAIN + "/include/c++/v1",
+                    "-isystem",
+                    EXTERNAL_TOOLCHAIN + "/usr/include",
+                    "-isystem",
+                    EXTERNAL_TOOLCHAIN + "/lib/clang/13.0.0/include",
+                    "-isystem",
+                    EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-gnu",
+                    # We do not want clang to search in absolute paths for files. This makes
+                    # Bazel think we are using an outside resource and fail the compile.
+                    "-no-canonical-prefixes",
+                ],
+            ),
+        ],
+    )
+
+    cpp_compile_flags = flag_set(
+        actions = [
+            ACTION_NAMES.cpp_compile,
+        ],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    "-std=c++17",
+                ],
+            ),
+        ],
+    )
+
+    link_exe_flags = flag_set(
+        actions = [ACTION_NAMES.cpp_link_executable],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    "-fuse-ld=lld",
+                    # We chose to use the llvm runtime, not the gcc one because it is already
+                    # included in the clang binary
+                    "--rtlib=compiler-rt",
+                    "-std=c++17",
+                    # We statically include these libc++ libraries so they do not need to be
+                    # on a developer's machine (they can be tricky to get).
+                    EXTERNAL_TOOLCHAIN + "/lib/libc++.a",
+                    EXTERNAL_TOOLCHAIN + "/lib/libc++abi.a",
+                    EXTERNAL_TOOLCHAIN + "/lib/libunwind.a",
+                    # Dynamically Link in the other parts of glibc (not needed in glibc 2.34+)
+                    "-lpthread",
+                    "-lm",
+                    "-ldl",
+                ],
+            ),
+        ],
+    )
+    return [feature(
+        "default_flags",
+        enabled = True,
+        flag_sets = [
+            cxx_compile_includes,
+            cpp_compile_flags,
+            link_exe_flags,
+        ],
+    )]
+
+def _make_diagnostic_flags():
+    """Here we define the flags that can be turned on via features to yield debug info."""
+    cxx_diagnostic = flag_set(
+        actions = [
+            ACTION_NAMES.c_compile,
+            ACTION_NAMES.cpp_compile,
+        ],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    "--trace-includes",
+                    "-v",
+                ],
+            ),
+        ],
+    )
+
+    link_diagnostic = flag_set(
+        actions = [ACTION_NAMES.cpp_link_executable],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    "-Wl,--verbose",
+                    "-v",
+                ],
+            ),
+        ],
+    )
+
+    link_search_dirs = flag_set(
+        actions = [ACTION_NAMES.cpp_link_executable],
+        flag_groups = [
+            flag_group(
+                flags = [
+                    "--print-search-dirs",
+                ],
+            ),
+        ],
+    )
+    return [
+        # Running a Bazel command with --features diagnostic will cause the compilation and
+        # link steps to be more verbose.
+        feature(
+            "diagnostic",
+            enabled = False,
+            flag_sets = [
+                cxx_diagnostic,
+                link_diagnostic,
+            ],
+        ),
+        # Running a Bazel command with --features print_search_dirs will cause the link to fail
+        # but directories searched for libraries, etc will be displayed.
+        feature(
+            "print_search_dirs",
+            enabled = False,
+            flag_sets = [
+                link_search_dirs,
+            ],
+        ),
+    ]
diff --git a/toolchain/download_clang_linux_amd64.bzl b/toolchain/download_clang_linux_amd64.bzl
new file mode 100644
index 0000000..29b8f9d
--- /dev/null
+++ b/toolchain/download_clang_linux_amd64.bzl
@@ -0,0 +1,151 @@
+"""
+This file assembles a toolchain for an amd64 Linux host using the Clang Compiler and glibc.
+
+It downloads the necessary headers, executables, and pre-compiled static/shared libraries to
+the external subfolder of the Bazel cache (the same place third party deps are downloaded with
+http_archive or similar functions in WORKSPACE.bazel). These will be able to be used via our
+custom c++ toolchain configuration (see //toolchain/linux_amd64_toolchain_config.bzl)
+
+Most files are downloaded as .deb files from packages.debian.org (with us acting as the dependency
+resolver) and extracted to
+  [outputRoot (aka Bazel cache)]/[outputUserRoot]/[outputBase]/external/clang_linux_amd64
+  (See https://bazel.build/docs/output_directories#layout-diagram)
+which will act as our sysroot.
+"""
+
+load("//toolchain:utils.bzl", "gcs_mirror_url")
+
+# From https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz.sha256
+clang_prefix = "clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04/"
+clang_sha256 = "2c2fb857af97f41a5032e9ecadf7f78d3eff389a5cd3c9ec620d24f134ceb3c8"
+clang_url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"
+
+debs_to_install = [
+    # These three comprise glibc. libc6 has the shared libraries, like libc itself, the math library
+    # (libm), etc. linux-libc-dev has the header files specific to linux. libc6-dev has the libc
+    # system headers (e.g. malloc.h, math.h).
+    {
+        # From https://packages.debian.org/bullseye/amd64/libc6/download
+        "sha256": "a6263062b476cee1052972621d473b159debec6e424f661eda88248b00331d79",
+        "url": "https://ftp.debian.org/debian/pool/main/g/glibc/libc6_2.31-13+deb11u4_amd64.deb",
+    },
+    {
+        # From https://packages.debian.org/bullseye/amd64/linux-libc-dev/download
+        "sha256": "e89023a5fc58c30ebb8cbb82de77f872baeafe7a5449f574b03cea478f7e9e6d",
+        "url": "https://ftp.debian.org/debian/pool/main/l/linux/linux-libc-dev_5.10.140-1_amd64.deb",
+    },
+    {
+        # From https://packages.debian.org/bullseye/amd64/libc6-dev/download
+        "sha256": "5f368eb89d102ccd23529a02fb17aaa1c15e7612506e22ef0c559b71f5049a91",
+        "url": "https://ftp.debian.org/debian/pool/main/g/glibc/libc6-dev_2.31-13+deb11u4_amd64.deb",
+    },
+]
+
+def _download_and_extract_deb(ctx, deb, sha256, prefix, output = ""):
+    """Downloads a debian file and extracts the data into the provided output directory"""
+
+    # https://bazel.build/rules/lib/repository_ctx#download_and_extract
+    # A .deb file has a data.tar.xz and a control.tar.xz, but the important contents
+    # (i.e. the headers or libs) are in the data.tar.xz
+    ctx.download_and_extract(
+        url = gcs_mirror_url(deb, sha256),
+        output = "tmp",
+        sha256 = sha256,
+    )
+
+    # https://bazel.build/rules/lib/repository_ctx#extract
+    ctx.extract(
+        archive = "tmp/data.tar.xz",
+        output = output,
+        stripPrefix = prefix,
+    )
+
+    # Clean up
+    ctx.delete("tmp")
+
+def _download_clang_linux_amd64_impl(ctx):
+    # Download the clang toolchain (the extraction can take a while)
+    # https://bazel.build/rules/lib/repository_ctx#download_and_extract
+    ctx.download_and_extract(
+        url = gcs_mirror_url(clang_url, clang_sha256),
+        output = "",
+        stripPrefix = clang_prefix,
+        sha256 = clang_sha256,
+    )
+
+    # Extract all the debs into our sysroot. This is very similar to installing them, except their
+    # dependencies are not installed automatically.
+    for deb in debs_to_install:
+        _download_and_extract_deb(
+            ctx,
+            deb["url"],
+            deb["sha256"],
+            ".",
+        )
+
+    # Create a BUILD.bazel file that makes the files downloaded into the toolchain visible.
+    # We have separate groups for each task because doing less work (sandboxing fewer files
+    # or uploading less data to RBE) makes compiles go faster. We try to strike a balance
+    # between minimal specifications and not having to edit this file often with our use
+    # of globs.
+    # https://bazel.build/rules/lib/repository_ctx#file
+    ctx.file(
+        "BUILD.bazel",
+        content = """
+# DO NOT EDIT THIS BAZEL FILE DIRECTLY
+# Generated from ctx.file action in download_linux_amd64_toolchain.bzl
+filegroup(
+    name = "archive_files",
+    srcs = [
+        "bin/llvm-ar",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "compile_files",
+    srcs = [
+        "bin/clang",
+    ] + glob(
+        include = [
+            "include/c++/v1/**",
+            "usr/include/**",
+            "lib/clang/13.0.0/include/**",
+            "usr/include/x86_64-linux-gnu/**",
+        ],
+        allow_empty = False,
+    ),
+    visibility = ["//visibility:public"],
+)
+
+filegroup(
+    name = "link_files",
+    srcs = [
+        "bin/clang",
+        "bin/ld.lld",
+        "bin/lld",
+        "lib/libc++.a",
+        "lib/libc++abi.a",
+        "lib/libunwind.a",
+        "lib64/ld-linux-x86-64.so.2",
+    ] + glob(
+        include = [
+            "lib/clang/13.0.0/lib/**",
+            "lib/x86_64-linux-gnu/**",
+            "usr/lib/x86_64-linux-gnu/**",
+        ],
+        allow_empty = False,
+    ),
+    visibility = ["//visibility:public"],
+)
+""",
+        executable = False,
+    )
+
+# https://bazel.build/rules/repository_rules
+download_clang_linux_amd64 = repository_rule(
+    implementation = _download_clang_linux_amd64_impl,
+    attrs = {},
+    doc = "Downloads clang, and all supporting headers, executables, " +
+          "and shared libraries required to build skcms on a Linux amd64 host",
+)
diff --git a/toolchain/download_ndk_linux_amd64.bzl b/toolchain/download_ndk_linux_amd64.bzl
new file mode 100644
index 0000000..1002900
--- /dev/null
+++ b/toolchain/download_ndk_linux_amd64.bzl
@@ -0,0 +1,20 @@
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# Path to the Android NDK from the point of view of the cc_toolchain rule.
+# Note how this matches the name in http_archive.
+NDK_PATH = "external/ndk_linux_amd64"
+
+def download_ndk_linux_amd64(name):
+    """Downloads the Android NDK under external/ndk_linux_amd64."""
+
+    # Archive taken from https://github.com/android/ndk/wiki/Unsupported-Downloads#r21e.
+    http_archive(
+        name = "ndk_linux_amd64",
+        urls = [
+            "https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip",
+            "https://storage.googleapis.com/skia-world-readable/bazel/ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e.zip",
+        ],
+        sha256 = "ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e",
+        strip_prefix = "android-ndk-r21e",
+        build_file = Label("//toolchain:ndk.BUILD"),
+    )
diff --git a/toolchain/download_toolchains.bzl b/toolchain/download_toolchains.bzl
index 444483a..a2690b6 100644
--- a/toolchain/download_toolchains.bzl
+++ b/toolchain/download_toolchains.bzl
@@ -1,42 +1,34 @@
 """
-This module defines the download_android_ndk repository rule.
+This file exports the various toolchains for the hosts that we support building skcms on.
+
+Supported:
+ - Linux amd64 (targeting Linux amd64 and Android)
+
+Planned:
+ - Windows amd64
+ - Mac M1 and Intel
+
 """
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
-_download_android_ndk_rule_name = "android_ndk"
+load(":download_clang_linux_amd64.bzl", "download_clang_linux_amd64")
+load(":download_ndk_linux_amd64.bzl", "download_ndk_linux_amd64")
 
-def _download_android_ndk(name):
-    """Downloads the Android NDK under external/android_ndk.
+name_toolchain = {
+    "clang_linux_amd64": download_clang_linux_amd64,
+    "ndk_linux_amd64": download_ndk_linux_amd64,
+}
+
+def download_toolchains_for_skcms(*args):
+    """
+    Point Bazel to the correct rules for downloading the different toolchains.
 
     Args:
-      name: Name of the external repository. This MUST equal "android_ndk".
+        *args: multiple toolchains, see top of file for
+               list of supported toolchains.
     """
 
-    # The toolchain assumes that the NDK is found at /external/android_ndk, so we must enforce this
-    # name.
-    if name != _download_android_ndk_rule_name:
-        fail("The name of this rule MUST be \"%s\"" % _download_android_ndk_rule_name)
-
-    # Archive taken from https://github.com/android/ndk/wiki/Unsupported-Downloads#r21e.
-    http_archive(
-        name = _download_android_ndk_rule_name,
-        urls = [
-            "https://dl.google.com/android/repository/android-ndk-r21e-linux-x86_64.zip",
-            "https://storage.googleapis.com/skia-world-readable/bazel/ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e.zip",
-        ],
-        sha256 = "ad7ce5467e18d40050dc51b8e7affc3e635c85bd8c59be62de32352328ed467e",
-        strip_prefix = "android-ndk-r21e",
-        build_file = Label("//toolchain:ndk.BUILD"),
-    )
-
-def download_toolchains(android_ndk_repository_name):
-    """Downloads the toolchains needed to build this repository.
-
-    Args:
-      android_ndk_repository_name: Name of the external repository with the android NDK. This MUST
-        equal "android_ndk".
-    """
-    _download_android_ndk(android_ndk_repository_name)
-
-# Path to the Android NDK from the point of view of the cc_toolchain rule.
-NDK_PATH = "external/%s" % _download_android_ndk_rule_name
+    for toolchain_name in args:
+        if toolchain_name not in name_toolchain:
+            fail("unrecognized toolchain name " + toolchain_name)
+        download_toolchain = name_toolchain[toolchain_name]
+        download_toolchain(name = toolchain_name)
diff --git a/toolchain/linux_amd64_trampolines/ar.sh b/toolchain/linux_amd64_trampolines/ar.sh
new file mode 100755
index 0000000..94c9da2
--- /dev/null
+++ b/toolchain/linux_amd64_trampolines/ar.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+external/clang_linux_amd64/bin/llvm-ar $@
diff --git a/toolchain/linux_amd64_trampolines/clang.sh b/toolchain/linux_amd64_trampolines/clang.sh
new file mode 100755
index 0000000..be6e59e
--- /dev/null
+++ b/toolchain/linux_amd64_trampolines/clang.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+# Copyright 2022 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+external/clang_linux_amd64/bin/clang $@
diff --git a/toolchain/ndk.BUILD b/toolchain/ndk.BUILD
index 347a53a..81c9580 100644
--- a/toolchain/ndk.BUILD
+++ b/toolchain/ndk.BUILD
@@ -3,13 +3,13 @@
 # this revision[2].
 #
 # The paths in this file point to locations inside the expanded Android NDK ZIP file (found at
-# external/android_ndk), and must be updated every time we upgrade to a new Android NDK version.
+# external/ndk_linux_amd64), and must be updated every time we upgrade to a new Android NDK version.
 #
 # [1] https://github.com/bazelbuild/bazel/blob/4710ef82ce34572878e07c52e83a0144d707f140/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java
 # [2] https://skia.googlesource.com/skcms/+/30c8e303800c256febb03a09fdcda7f75d119b1b/WORKSPACE#22
 
 filegroup(
-    name = "arm64-v8a_all_files",
+    name = "arm64_v8a_all_files",
     srcs = glob(["toolchains/llvm/**"]) + glob([
         "platforms/android-29/arch-arm64/**/*",
         "sources/cxx-stl/llvm-libc++/include/**/*",
@@ -18,26 +18,26 @@
         "sysroot/**/*",
         "toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/**/*",
     ]) + [
-      ":arm64-v8a_dynamic_runtime_libraries",
-      ":arm64-v8a_static_runtime_libraries",
+        ":arm64_v8a_dynamic_runtime_libraries",
+        ":arm64_v8a_static_runtime_libraries",
     ],
     visibility = ["//visibility:public"],
 )
 
 filegroup(
-    name = "arm64-v8a_dynamic_runtime_libraries",
+    name = "arm64_v8a_dynamic_runtime_libraries",
     srcs = glob(["sources/cxx-stl/llvm-libc++/libs/arm64-v8a/*.so"]),
     visibility = ["//visibility:public"],
 )
 
 filegroup(
-    name = "arm64-v8a_static_runtime_libraries",
+    name = "arm64_v8a_static_runtime_libraries",
     srcs = glob(["sources/cxx-stl/llvm-libc++/libs/arm64-v8a/*.a"]),
     visibility = ["//visibility:public"],
 )
 
 filegroup(
-    name = "armeabi-v7a_all_files",
+    name = "armeabi_v7a_all_files",
     srcs = glob(["toolchains/llvm/**"]) + glob([
         "platforms/android-29/arch-arm/**/*",
         "sources/cxx-stl/llvm-libc++/include/**/*",
@@ -46,20 +46,20 @@
         "sysroot/**/*",
         "toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/**/*",
     ]) + [
-      ":armeabi-v7a_dynamic_runtime_libraries",
-      ":armeabi-v7a_static_runtime_libraries",
+        ":armeabi_v7a_dynamic_runtime_libraries",
+        ":armeabi_v7a_static_runtime_libraries",
     ],
     visibility = ["//visibility:public"],
 )
 
 filegroup(
-    name = "armeabi-v7a_dynamic_runtime_libraries",
+    name = "armeabi_v7a_dynamic_runtime_libraries",
     srcs = glob(["sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/*.so"]),
     visibility = ["//visibility:public"],
 )
 
 filegroup(
-    name = "armeabi-v7a_static_runtime_libraries",
+    name = "armeabi_v7a_static_runtime_libraries",
     srcs = glob(["sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/*.a"]),
     visibility = ["//visibility:public"],
 )
diff --git a/toolchain/ndk_cc_toolchain_config.bzl b/toolchain/ndk_linux_arm64_toolchain_config.bzl
similarity index 89%
rename from toolchain/ndk_cc_toolchain_config.bzl
rename to toolchain/ndk_linux_arm64_toolchain_config.bzl
index ccf38ea..80d4a6c 100644
--- a/toolchain/ndk_cc_toolchain_config.bzl
+++ b/toolchain/ndk_linux_arm64_toolchain_config.bzl
@@ -5,7 +5,7 @@
 until this revision[2].
 
 The paths in this file point to locations inside the expanded Android NDK ZIP file (found at
-external/android_ndk), and must be updated every time we upgrade to a new Android NDK version.
+external/ndk_linux_amd64), and must be updated every time we upgrade to a new Android NDK version.
 
 [1] https://github.com/bazelbuild/bazel/blob/4710ef82ce34572878e07c52e83a0144d707f140/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidNdkRepositoryFunction.java#L422
 [2] https://skia.googlesource.com/skcms/+/30c8e303800c256febb03a09fdcda7f75d119b1b/WORKSPACE#22
@@ -20,7 +20,7 @@
     "tool_path",
     "with_feature_set",
 )
-load("download_toolchains.bzl", "NDK_PATH")
+load(":download_ndk_linux_amd64.bzl", "NDK_PATH")
 
 # Supported CPUs.
 _ARMEABI_V7A = "armeabi-v7a"
@@ -165,19 +165,19 @@
         return [
             tool_path(
                 name = "ar",
-                path = "trampolines/arm-linux-androideabi-ar.sh",
+                path = "android_trampolines/arm-linux-androideabi-ar.sh",
             ),
             tool_path(
                 name = "cpp",
-                path = "trampolines/clang.sh",
+                path = "android_trampolines/clang.sh",
             ),
             tool_path(
                 name = "dwp",
-                path = "trampolines/arm-linux-androideabi-dwp.sh",
+                path = "android_trampolines/arm-linux-androideabi-dwp.sh",
             ),
             tool_path(
                 name = "gcc",
-                path = "trampolines/clang.sh",
+                path = "android_trampolines/clang.sh",
             ),
             tool_path(
                 name = "gcov",
@@ -185,42 +185,42 @@
             ),
             tool_path(
                 name = "ld",
-                path = "trampolines/arm-linux-androideabi-ld.sh",
+                path = "android_trampolines/arm-linux-androideabi-ld.sh",
             ),
             tool_path(
                 name = "nm",
-                path = "trampolines/arm-linux-androideabi-nm.sh",
+                path = "android_trampolines/arm-linux-androideabi-nm.sh",
             ),
             tool_path(
                 name = "objcopy",
-                path = "trampolines/arm-linux-androideabi-objcopy.sh",
+                path = "android_trampolines/arm-linux-androideabi-objcopy.sh",
             ),
             tool_path(
                 name = "objdump",
-                path = "trampolines/arm-linux-androideabi-objdump.sh",
+                path = "android_trampolines/arm-linux-androideabi-objdump.sh",
             ),
             tool_path(
                 name = "strip",
-                path = "trampolines/arm-linux-androideabi-strip.sh",
+                path = "android_trampolines/arm-linux-androideabi-strip.sh",
             ),
         ]
     if cpu == _ARM64_V8A:
         return [
             tool_path(
                 name = "ar",
-                path = "trampolines/aarch64-linux-android-ar.sh",
+                path = "android_trampolines/aarch64-linux-android-ar.sh",
             ),
             tool_path(
                 name = "cpp",
-                path = "trampolines/clang.sh",
+                path = "android_trampolines/clang.sh",
             ),
             tool_path(
                 name = "dwp",
-                path = "trampolines/aarch64-linux-android-dwp.sh",
+                path = "android_trampolines/aarch64-linux-android-dwp.sh",
             ),
             tool_path(
                 name = "gcc",
-                path = "trampolines/clang.sh",
+                path = "android_trampolines/clang.sh",
             ),
             tool_path(
                 name = "gcov",
@@ -228,23 +228,23 @@
             ),
             tool_path(
                 name = "ld",
-                path = "trampolines/aarch64-linux-android-ld.sh",
+                path = "android_trampolines/aarch64-linux-android-ld.sh",
             ),
             tool_path(
                 name = "nm",
-                path = "trampolines/aarch64-linux-android-nm.sh",
+                path = "android_trampolines/aarch64-linux-android-nm.sh",
             ),
             tool_path(
                 name = "objcopy",
-                path = "trampolines/aarch64-linux-android-objcopy.sh",
+                path = "android_trampolines/aarch64-linux-android-objcopy.sh",
             ),
             tool_path(
                 name = "objdump",
-                path = "trampolines/aarch64-linux-android-objdump.sh",
+                path = "android_trampolines/aarch64-linux-android-objdump.sh",
             ),
             tool_path(
                 name = "strip",
-                path = "trampolines/aarch64-linux-android-strip.sh",
+                path = "android_trampolines/aarch64-linux-android-strip.sh",
             ),
         ]
     fail("Unknown CPU: " + cpu)
@@ -398,7 +398,7 @@
             mandatory = True,
             values = [_ARMEABI_V7A, _ARM64_V8A],
             doc = "Target CPU.",
-        )
+        ),
     },
     provides = [CcToolchainConfigInfo],
 )
diff --git a/toolchain/trampolines/aarch64-linux-android-ar.sh b/toolchain/trampolines/aarch64-linux-android-ar.sh
deleted file mode 100755
index 08b0744..0000000
--- a/toolchain/trampolines/aarch64-linux-android-ar.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar $@
diff --git a/toolchain/trampolines/aarch64-linux-android-dwp.sh b/toolchain/trampolines/aarch64-linux-android-dwp.sh
deleted file mode 100755
index de8fb1d..0000000
--- a/toolchain/trampolines/aarch64-linux-android-dwp.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-dwp $@
diff --git a/toolchain/trampolines/aarch64-linux-android-ld.sh b/toolchain/trampolines/aarch64-linux-android-ld.sh
deleted file mode 100755
index 6d0158a..0000000
--- a/toolchain/trampolines/aarch64-linux-android-ld.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld $@
diff --git a/toolchain/trampolines/aarch64-linux-android-nm.sh b/toolchain/trampolines/aarch64-linux-android-nm.sh
deleted file mode 100755
index 4bf5e74..0000000
--- a/toolchain/trampolines/aarch64-linux-android-nm.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-nm $@
diff --git a/toolchain/trampolines/aarch64-linux-android-objcopy.sh b/toolchain/trampolines/aarch64-linux-android-objcopy.sh
deleted file mode 100755
index 84d17a3..0000000
--- a/toolchain/trampolines/aarch64-linux-android-objcopy.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objcopy $@
diff --git a/toolchain/trampolines/aarch64-linux-android-objdump.sh b/toolchain/trampolines/aarch64-linux-android-objdump.sh
deleted file mode 100755
index c38e60d..0000000
--- a/toolchain/trampolines/aarch64-linux-android-objdump.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-objdump $@
diff --git a/toolchain/trampolines/aarch64-linux-android-strip.sh b/toolchain/trampolines/aarch64-linux-android-strip.sh
deleted file mode 100755
index e396c9d..0000000
--- a/toolchain/trampolines/aarch64-linux-android-strip.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-ar.sh b/toolchain/trampolines/arm-linux-androideabi-ar.sh
deleted file mode 100755
index ee99c68..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-ar.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-dwp.sh b/toolchain/trampolines/arm-linux-androideabi-dwp.sh
deleted file mode 100755
index b046d28..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-dwp.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-dwp $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-ld.sh b/toolchain/trampolines/arm-linux-androideabi-ld.sh
deleted file mode 100755
index c9c7a87..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-ld.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ld $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-nm.sh b/toolchain/trampolines/arm-linux-androideabi-nm.sh
deleted file mode 100755
index fe1fa2a..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-nm.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-nm $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-objcopy.sh b/toolchain/trampolines/arm-linux-androideabi-objcopy.sh
deleted file mode 100755
index 9fc92a3..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-objcopy.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objcopy $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-objdump.sh b/toolchain/trampolines/arm-linux-androideabi-objdump.sh
deleted file mode 100755
index b161b35..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-objdump.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-objdump $@
diff --git a/toolchain/trampolines/arm-linux-androideabi-strip.sh b/toolchain/trampolines/arm-linux-androideabi-strip.sh
deleted file mode 100755
index b8f7499..0000000
--- a/toolchain/trampolines/arm-linux-androideabi-strip.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip $@
diff --git a/toolchain/trampolines/clang.sh b/toolchain/trampolines/clang.sh
deleted file mode 100755
index 2d743f0..0000000
--- a/toolchain/trampolines/clang.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-external/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/clang $@
diff --git a/toolchain/utils.bzl b/toolchain/utils.bzl
new file mode 100644
index 0000000..fd7a4b8
--- /dev/null
+++ b/toolchain/utils.bzl
@@ -0,0 +1,40 @@
+"""This module provides the gcs_mirror_url macro."""
+
+# Set to True to force the macro to only return the mirror URL.
+_TEST_GCS_MIRROR = False
+
+# Must be kept in sync with the suffixes supported by gcs_mirror (e.g.
+# https://skia.googlesource.com/skia/+/8ad66c2340713234df6b249e793415233337a103/bazel/gcs_mirror/gcs_mirror.go#140).
+_SUPPORTED_SUFFIXES = [".tar.gz", ".tgz", ".tar.xz", ".deb", ".zip"]
+
+_GCS_MIRROR_PREFIX = "https://storage.googleapis.com/skia-world-readable/bazel"
+
+def gcs_mirror_url(url, sha256):
+    """Takes the URL of an external resource and computes its GCS mirror URL.
+
+    We store backup copies of external resources in the skia-world-readable GCS bucket. This macro
+    returns a list with two elements: the original URL, and the mirrored URL.
+
+    Files are expected to be in the mirror location named after their sha256 hash. The files should
+    still have their file extension, as some of the Starlark functions sniff the file extension
+    (e.g. download_and_extract). See //bazel/gcs_mirror for an automated way to update this mirror.
+
+    To mirror a new URL, please use the `gcs_mirror` utility found at
+    https://skia.googlesource.com/skia/+/8ad66c2340713234df6b249e793415233337a103/bazel/gcs_mirror/gcs_mirror.go.
+
+    Args:
+        url: URL of the mirrored resource.
+        sha256: SHA256 hash of the mirrored resource.
+    Returns:
+        A list of the form [original URL, mirror URL].
+    """
+    extension = ""
+    for suffix in _SUPPORTED_SUFFIXES:
+        if url.endswith(suffix):
+            extension = suffix
+            break
+    if extension == "":
+        fail("URL %s has an unsupported suffix." % url)
+
+    mirror_url = "%s/%s%s" % (_GCS_MIRROR_PREFIX, sha256, extension)
+    return [mirror_url] if _TEST_GCS_MIRROR else [url, mirror_url]