Merge branch 'main' into rules-based-toolchain-example
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 9fbb3ed..2b2dd22 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -49,3 +49,13 @@
       - "--ignore_dev_dependency"
     build_targets:
       - "//cc/..."
+  ubuntu_rule_based_toolchains:
+    name: Ubuntu rule-based toolchains
+    platform: ubuntu1804
+    working_directory: examples/rule_based_toolchain
+    build_flags:
+      - "--enable_bzlmod"
+    build_targets:
+      - "//..."
+    test_targets:
+      - "//..."
diff --git a/.bazelignore b/.bazelignore
new file mode 100644
index 0000000..ff5e9ce
--- /dev/null
+++ b/.bazelignore
@@ -0,0 +1 @@
+examples/rule_based_toolchain
diff --git a/.gitignore b/.gitignore
index 7f69f3a..0d4fed2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-/bazel-*
+bazel-*
 MODULE.bazel.lock
diff --git a/examples/rule_based_toolchain/.bazelrc b/examples/rule_based_toolchain/.bazelrc
new file mode 100644
index 0000000..ac2fe2f
--- /dev/null
+++ b/examples/rule_based_toolchain/.bazelrc
@@ -0,0 +1,2 @@
+# Do not use the default toolchain.
+build --repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=0
diff --git a/examples/rule_based_toolchain/.bazelversion b/examples/rule_based_toolchain/.bazelversion
new file mode 100644
index 0000000..b26a34e
--- /dev/null
+++ b/examples/rule_based_toolchain/.bazelversion
@@ -0,0 +1 @@
+7.2.1
diff --git a/examples/rule_based_toolchain/BUILD.bazel b/examples/rule_based_toolchain/BUILD.bazel
new file mode 100644
index 0000000..28103fd
--- /dev/null
+++ b/examples/rule_based_toolchain/BUILD.bazel
@@ -0,0 +1,28 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc:defs.bzl", "cc_test")
+
+licenses(["notice"])
+
+cc_test(
+    name = "quick_test",
+    srcs = ["quick_test.cc"],
+    deps = [
+        "//dynamic_answer",
+        "//static_answer",
+        "@googletest//:gtest",
+        "@googletest//:gtest_main",
+    ],
+)
diff --git a/examples/rule_based_toolchain/MODULE.bazel b/examples/rule_based_toolchain/MODULE.bazel
new file mode 100644
index 0000000..88b5227
--- /dev/null
+++ b/examples/rule_based_toolchain/MODULE.bazel
@@ -0,0 +1,44 @@
+module(
+    name = "rule_based_toolchain",
+    version = "0.0.1",
+)
+
+bazel_dep(name = "platforms", version = "0.0.10")
+bazel_dep(name = "googletest", version = "1.15.2")
+bazel_dep(name = "bazel_skylib", version = "1.7.1")
+bazel_dep(name = "rules_cc")
+local_path_override(
+    module_name = "rules_cc",
+    path = "../..",
+)
+
+http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "clang-linux-x86_64",
+    build_file = "//toolchain:clang.BUILD",
+    sha256 = "9042f89df9c13a2bf28e16ce34dfe22934b59b5d8390e94b030bb378bdb3c898",
+    type = "zip",
+    url = "https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-amd64/+/git_revision:0cfd03ac0d3f9713090a581bda07584754c73a49",
+)
+
+http_archive(
+    name = "clang-linux-aarch64",
+    build_file = "//toolchain:clang.BUILD",
+    sha256 = "61abb915821190baddafa973c69a9db9acda5a16ed3a89489ea2b3b030a2330b",
+    type = "zip",
+    url = "https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/clang/linux-arm64/+/git_revision:0cfd03ac0d3f9713090a581bda07584754c73a49",
+)
+
+http_archive(
+    name = "linux_sysroot",
+    build_file = "//toolchain:linux_sysroot.BUILD",
+    sha256 = "f45ca0d8b46810b94d2a7dbc65f9092337d6a9568b260b51173a5ab9314da25e",
+    type = "zip",
+    url = "https://chrome-infra-packages.appspot.com/dl/fuchsia/third_party/sysroot/bionic/+/git_revision:702eb9654703a7cec1cadf93a7e3aa269d053943",
+)
+
+register_toolchains(
+    "//toolchain:host_cc_toolchain",
+    dev_dependency = True,
+)
diff --git a/examples/rule_based_toolchain/README.md b/examples/rule_based_toolchain/README.md
new file mode 100644
index 0000000..9d370a5
--- /dev/null
+++ b/examples/rule_based_toolchain/README.md
@@ -0,0 +1,15 @@
+# Rule-based toolchains
+This example showcases a fully working rule-based toolchain for Linux. This also
+serves as an integration test to ensure rule-based toolchains continue to work
+as intended.
+
+The complete toolchain configuration lives [here](https://github.com/bazelbuild/rules_cc/tree/main/examples/rule_based_toolchain/toolchain).
+
+# Trying the example
+From this directory, you can run example tests that build using this toolchain
+with the following command:
+```
+$ bazel test //...
+```
+
+This example currently only supports Linux.
diff --git a/examples/rule_based_toolchain/constraint/BUILD.bazel b/examples/rule_based_toolchain/constraint/BUILD.bazel
new file mode 100644
index 0000000..062b033
--- /dev/null
+++ b/examples/rule_based_toolchain/constraint/BUILD.bazel
@@ -0,0 +1,65 @@
+# Copyright 2024 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.
+
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
+licenses(["notice"])
+
+selects.config_setting_group(
+    name = "linux_x86_64",
+    match_all = [
+        "@platforms//os:linux",
+        "@platforms//cpu:x86_64",
+    ],
+)
+
+selects.config_setting_group(
+    name = "linux_aarch64",
+    match_all = [
+        "@platforms//os:linux",
+        "@platforms//cpu:aarch64",
+    ],
+)
+
+selects.config_setting_group(
+    name = "macos_x86_64",
+    match_all = [
+        "@platforms//os:macos",
+        "@platforms//cpu:x86_64",
+    ],
+)
+
+selects.config_setting_group(
+    name = "macos_aarch64",
+    match_all = [
+        "@platforms//os:macos",
+        "@platforms//cpu:aarch64",
+    ],
+)
+
+selects.config_setting_group(
+    name = "windows_x86_64",
+    match_all = [
+        "@platforms//os:windows",
+        "@platforms//cpu:x86_64",
+    ],
+)
+
+selects.config_setting_group(
+    name = "windows_aarch64",
+    match_all = [
+        "@platforms//os:windows",
+        "@platforms//cpu:aarch64",
+    ],
+)
diff --git a/examples/rule_based_toolchain/dynamic_answer/BUILD.bazel b/examples/rule_based_toolchain/dynamic_answer/BUILD.bazel
new file mode 100644
index 0000000..7a8d640
--- /dev/null
+++ b/examples/rule_based_toolchain/dynamic_answer/BUILD.bazel
@@ -0,0 +1,45 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_shared_library")
+
+licenses(["notice"])
+
+cc_library(
+    name = "headers",
+    hdrs = ["public/dynamic_answer.h"],
+    includes = ["public"],
+    visibility = ["//visibility:private"],
+)
+
+cc_library(
+    name = "answer",
+    srcs = ["dynamic_answer.c"],
+    visibility = ["//visibility:private"],
+    deps = [":headers"],
+)
+
+cc_shared_library(
+    name = "shared_library",
+    visibility = ["//visibility:private"],
+    deps = [":answer"],
+)
+
+# Forces linkage as a shared library.
+cc_library(
+    name = "dynamic_answer",
+    srcs = [":shared_library"],
+    visibility = ["//visibility:public"],
+    deps = [":headers"],
+)
diff --git a/examples/rule_based_toolchain/dynamic_answer/dynamic_answer.c b/examples/rule_based_toolchain/dynamic_answer/dynamic_answer.c
new file mode 100644
index 0000000..247526b
--- /dev/null
+++ b/examples/rule_based_toolchain/dynamic_answer/dynamic_answer.c
@@ -0,0 +1,19 @@
+// Copyright 2024 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.
+
+#include "dynamic_answer.h"
+
+int dynamic_answer(void) {
+    return 24;
+}
diff --git a/examples/rule_based_toolchain/dynamic_answer/public/dynamic_answer.h b/examples/rule_based_toolchain/dynamic_answer/public/dynamic_answer.h
new file mode 100644
index 0000000..c354758
--- /dev/null
+++ b/examples/rule_based_toolchain/dynamic_answer/public/dynamic_answer.h
@@ -0,0 +1,28 @@
+// Copyright 2024 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.
+
+#ifndef DYNAMIC_ANSWER_PUBLIC_DYNAMIC_ANSWER_H_
+#define DYNAMIC_ANSWER_PUBLIC_DYNAMIC_ANSWER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+int dynamic_answer(void);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // DYNAMIC_ANSWER_PUBLIC_DYNAMIC_ANSWER_H_
diff --git a/examples/rule_based_toolchain/quick_test.cc b/examples/rule_based_toolchain/quick_test.cc
new file mode 100644
index 0000000..80737dd
--- /dev/null
+++ b/examples/rule_based_toolchain/quick_test.cc
@@ -0,0 +1,26 @@
+// Copyright 2024 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.
+
+#include <gtest/gtest.h>
+
+#include "dynamic_answer.h"
+#include "static_answer.h"
+
+TEST(Static, ProperlyLinked) {
+  EXPECT_EQ(static_answer(), 42);
+}
+
+TEST(Dynamic, ProperlyLinked) {
+  EXPECT_EQ(dynamic_answer(), 24);
+}
diff --git a/examples/rule_based_toolchain/static_answer/BUILD.bazel b/examples/rule_based_toolchain/static_answer/BUILD.bazel
new file mode 100644
index 0000000..f185f06
--- /dev/null
+++ b/examples/rule_based_toolchain/static_answer/BUILD.bazel
@@ -0,0 +1,33 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc:defs.bzl", "cc_library")
+
+licenses(["notice"])
+
+cc_library(
+    name = "answer",
+    srcs = ["static_answer.cc"],
+    hdrs = ["public/static_answer.h"],
+    includes = ["public"],
+    linkstatic = True,
+    visibility = ["//visibility:private"],
+)
+
+# TODO: This should be a cc_static_library when that's supported.
+alias(
+    name = "static_answer",
+    actual = ":answer",
+    visibility = ["//visibility:public"],
+)
diff --git a/examples/rule_based_toolchain/static_answer/public/static_answer.h b/examples/rule_based_toolchain/static_answer/public/static_answer.h
new file mode 100644
index 0000000..a77c8e5
--- /dev/null
+++ b/examples/rule_based_toolchain/static_answer/public/static_answer.h
@@ -0,0 +1,28 @@
+// Copyright 2024 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.
+
+#ifndef STATIC_ANSWER_PUBLIC_STATIC_ANSWER_H_
+#define STATIC_ANSWER_PUBLIC_STATIC_ANSWER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+int static_answer(void);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // STATIC_ANSWER_PUBLIC_STATIC_ANSWER_H_
diff --git a/examples/rule_based_toolchain/static_answer/static_answer.cc b/examples/rule_based_toolchain/static_answer/static_answer.cc
new file mode 100644
index 0000000..4f8a06f
--- /dev/null
+++ b/examples/rule_based_toolchain/static_answer/static_answer.cc
@@ -0,0 +1,19 @@
+// Copyright 2024 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.
+
+#include "static_answer.h"
+
+extern "C" int static_answer() {
+    return 42;
+}
diff --git a/examples/rule_based_toolchain/toolchain/BUILD.bazel b/examples/rule_based_toolchain/toolchain/BUILD.bazel
new file mode 100644
index 0000000..58e3540
--- /dev/null
+++ b/examples/rule_based_toolchain/toolchain/BUILD.bazel
@@ -0,0 +1,39 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc/toolchains:toolchain.bzl", "cc_toolchain")
+
+licenses(["notice"])
+
+cc_toolchain(
+    name = "host_clang",
+    args = select({
+        "@platforms//os:linux": [
+            "//toolchain/args:linux_sysroot",
+        ],
+        "//conditions:default": [],
+    }) + [
+        "//toolchain/args:no_canonical_prefixes",
+        "//toolchain/args:warnings",
+    ],
+    enabled_features = ["@rules_cc//cc/toolchains/args:experimental_replace_legacy_action_config_features"],
+    known_features = ["@rules_cc//cc/toolchains/args:experimental_replace_legacy_action_config_features"],
+    tool_map = "//toolchain/tools:all_tools",
+)
+
+toolchain(
+    name = "host_cc_toolchain",
+    toolchain = ":host_clang",
+    toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
diff --git a/examples/rule_based_toolchain/toolchain/args/BUILD.bazel b/examples/rule_based_toolchain/toolchain/args/BUILD.bazel
new file mode 100644
index 0000000..537c6b2
--- /dev/null
+++ b/examples/rule_based_toolchain/toolchain/args/BUILD.bazel
@@ -0,0 +1,52 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc/toolchains:args.bzl", "cc_args")
+load("@rules_cc//cc/toolchains/args:sysroot.bzl", "cc_sysroot")
+
+cc_sysroot(
+    name = "linux_sysroot",
+    data = [
+        "@linux_sysroot//:root",
+        "@linux_sysroot//:usr-include",
+        "@linux_sysroot//:usr-include-x86_64-linux-gnu",
+    ],
+    sysroot = "@linux_sysroot//:root",
+    visibility = ["//visibility:public"],
+)
+
+cc_args(
+    name = "warnings",
+    actions = [
+        "@rules_cc//cc/toolchains/actions:c_compile",
+        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
+    ],
+    args = [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+        "-Wpedantic",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+cc_args(
+    name = "no_canonical_prefixes",
+    actions = [
+        "@rules_cc//cc/toolchains/actions:c_compile",
+        "@rules_cc//cc/toolchains/actions:cpp_compile_actions",
+    ],
+    args = ["-no-canonical-prefixes"],
+    visibility = ["//visibility:public"],
+)
diff --git a/examples/rule_based_toolchain/toolchain/clang.BUILD b/examples/rule_based_toolchain/toolchain/clang.BUILD
new file mode 100644
index 0000000..24842da
--- /dev/null
+++ b/examples/rule_based_toolchain/toolchain/clang.BUILD
@@ -0,0 +1,85 @@
+# Copyright 2024 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.
+
+load("@bazel_skylib//rules/directory:directory.bzl", "directory")
+load("@bazel_skylib//rules/directory:subdirectory.bzl", "subdirectory")
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+exports_files(glob(["bin/**"]))
+
+# Directory-based rules in this toolchain only referece things in
+# lib/ or include/ subdirectories.
+directory(
+    name = "toolchain_root",
+    srcs = glob([
+        "lib/**",
+        "include/**",
+    ]),
+)
+
+subdirectory(
+    name = "include-c++-v1",
+    parent = ":toolchain_root",
+    path = "include/c++/v1",
+)
+
+subdirectory(
+    name = "lib-clang-include",
+    parent = ":toolchain_root",
+    path = "lib/clang/19/include",
+)
+
+subdirectory(
+    name = "include-x86_64-unknown-linux-gnu-c++-v1",
+    parent = ":toolchain_root",
+    path = "include/x86_64-unknown-linux-gnu/c++/v1",
+)
+
+filegroup(
+    name = "builtin_headers",
+    srcs = [
+        ":include-c++-v1",
+        ":include-x86_64-unknown-linux-gnu-c++-v1",
+        ":lib-clang-include",
+    ],
+)
+
+# Various supporting files needed to run the linker.
+filegroup(
+    name = "linker_builtins",
+    data = glob([
+        "bin/lld*",
+        "bin/ld*",
+        "lib/**/*.a",
+        "lib/**/*.so*",
+        "lib/**/*.o",
+    ]) + [
+        ":multicall_support_files",
+    ],
+)
+
+# Some toolchain distributions use busybox-style handling of tools, so things
+# like `clang++` just redirect to a `llvm` binary. This glob catches this
+# binary if it's included in the distribution, and is a no-op if the multicall
+# binary doesn't exist.
+filegroup(
+    name = "multicall_support_files",
+    srcs = glob(
+        ["bin/llvm"],
+        allow_empty = True,
+    ),
+)
diff --git a/examples/rule_based_toolchain/toolchain/linux_sysroot.BUILD b/examples/rule_based_toolchain/toolchain/linux_sysroot.BUILD
new file mode 100644
index 0000000..66ebca7
--- /dev/null
+++ b/examples/rule_based_toolchain/toolchain/linux_sysroot.BUILD
@@ -0,0 +1,37 @@
+# Copyright 2024 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.
+
+load("@bazel_skylib//rules/directory:directory.bzl", "directory")
+load("@bazel_skylib//rules/directory:subdirectory.bzl", "subdirectory")
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])
+
+directory(
+    name = "root",
+    srcs = glob(["**/*"]),
+)
+
+subdirectory(
+    name = "usr-include-x86_64-linux-gnu",
+    parent = ":root",
+    path = "usr/include/x86_64-linux-gnu",
+)
+
+subdirectory(
+    name = "usr-include",
+    parent = ":root",
+    path = "usr/include",
+)
diff --git a/examples/rule_based_toolchain/toolchain/tools/BUILD.bazel b/examples/rule_based_toolchain/toolchain/tools/BUILD.bazel
new file mode 100644
index 0000000..1ed55ef
--- /dev/null
+++ b/examples/rule_based_toolchain/toolchain/tools/BUILD.bazel
@@ -0,0 +1,200 @@
+# Copyright 2024 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.
+
+load("@rules_cc//cc/toolchains:tool.bzl", "cc_tool")
+load("@rules_cc//cc/toolchains:tool_map.bzl", "cc_tool_map")
+
+licenses(["notice"])
+
+# This `select` happens under the target configuration. For macOS,
+# llvm-libtool-darwin should be used when creating static libraries even if the
+# exec platform is linux.
+alias(
+    name = "all_tools",
+    actual = select({
+        "@platforms//os:macos": ":macos_tools",
+        "//conditions:default": ":default_tools",
+    }),
+    visibility = ["//visibility:public"],
+)
+
+COMMON_TOOLS = {
+    "@rules_cc//cc/toolchains/actions:assembly_actions": ":asm",
+    "@rules_cc//cc/toolchains/actions:c_compile": ":clang",
+    "@rules_cc//cc/toolchains/actions:cpp_compile_actions": ":clang++",
+    "@rules_cc//cc/toolchains/actions:link_actions": ":lld",
+    "@rules_cc//cc/toolchains/actions:objcopy_embed_data": ":llvm-objcopy",
+    "@rules_cc//cc/toolchains/actions:strip": ":llvm-strip",
+}
+
+cc_tool_map(
+    name = "default_tools",
+    tools = COMMON_TOOLS | {
+        "@rules_cc//cc/toolchains/actions:ar_actions": ":llvm-ar",
+    },
+    visibility = ["//visibility:private"],
+)
+
+cc_tool_map(
+    name = "macos_tools",
+    tools = COMMON_TOOLS | {
+        "@rules_cc//cc/toolchains/actions:ar_actions": ":llvm-libtool-darwin",
+    },
+    visibility = ["//visibility:private"],
+)
+
+# TODO: https://github.com/bazelbuild/rules_cc/issues/235 - Workaround until
+# Bazel has a more robust way to implement `cc_tool_map`.
+alias(
+    name = "asm",
+    actual = ":clang",
+)
+
+cc_tool(
+    name = "clang",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/clang",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/clang",
+    }),
+    data = [
+        ":exec_platform_builtin_headers",
+        ":exec_platform_multicall_support_files",
+    ],
+)
+
+cc_tool(
+    name = "clang++",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/clang++",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/clang++",
+    }),
+    data = [
+        ":exec_platform_builtin_headers",
+        ":exec_platform_multicall_support_files",
+    ],
+)
+
+cc_tool(
+    name = "lld",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/clang++",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/clang++",
+    }),
+    data = [
+        ":exec_platform_builtin_headers",
+        ":exec_platform_linker_builtins",
+        ":exec_platform_multicall_support_files",
+    ],
+)
+
+cc_tool(
+    name = "llvm-ar",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-ar",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-ar",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "llvm-libtool-darwin",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-libtool-darwin",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-libtool-darwin",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "llvm-objcopy",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-objcopy",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-objcopy",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "llvm-objdump",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-objdump",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-objdump",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "llvm-cov",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-cov",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-cov",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "llvm-strip",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/llvm-strip",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/llvm-strip",
+    }),
+    data = [":exec_platform_multicall_support_files"],
+)
+
+cc_tool(
+    name = "clang-tidy",
+    src = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:bin/clang-tidy",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:bin/clang-tidy",
+    }),
+    data = [
+        ":exec_platform_builtin_headers",
+        ":exec_platform_multicall_support_files",
+    ],
+)
+
+#################################
+#   Platform-specific aliases   #
+#################################
+
+# These aliases are used to reduce duplication of `select` statements throughout
+# this build file. The select statements in these aliases are evaluated under
+# the exec configuration.
+
+alias(
+    name = "exec_platform_builtin_headers",
+    actual = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:builtin_headers",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:builtin_headers",
+    }),
+    visibility = ["//visibility:private"],
+)
+
+alias(
+    name = "exec_platform_multicall_support_files",
+    actual = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:multicall_support_files",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:multicall_support_files",
+    }),
+    visibility = ["//visibility:private"],
+)
+
+alias(
+    name = "exec_platform_linker_builtins",
+    actual = select({
+        "//constraint:linux_aarch64": "@clang-linux-aarch64//:linker_builtins",
+        "//constraint:linux_x86_64": "@clang-linux-x86_64//:linker_builtins",
+    }),
+    visibility = ["//visibility:private"],
+)