Explorar o código

Merge LLVM and Bazel infra from the toolchain repository. (#207)

The Bazel bits are collected into a directory and given less confusing
names (I hope). Other than names, everything is a direct copy from the
toolchain repository without any edits.

A few files needed to be merged in:
- `.gitignore`
- `.pre-commit-config.yaml`

Subsequent commits will add relevant C++ infrastructure and then the
source code itself.
Chandler Carruth %!s(int64=5) %!d(string=hai) anos
pai
achega
6aae31b65b

+ 8 - 0
.bazelrc

@@ -0,0 +1,8 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+build --crosstool_top=@bazel_cc_toolchain
+build --host_crosstool_top=@bazel_cc_toolchain
+
+build:fuzzer --features=fuzzer

+ 1 - 0
.bazelversion

@@ -0,0 +1 @@
+last_green

+ 1 - 0
.gitignore

@@ -3,3 +3,4 @@
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 **/__pycache__/
+/bazel-*

+ 11 - 0
.gitmodules

@@ -0,0 +1,11 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+[submodule "third_party/llvm-bazel"]
+	path = third_party/llvm-bazel
+	url = https://github.com/google/llvm-bazel.git
+	fetchRecurseSubmodules = false
+[submodule "third_party/llvm-project"]
+	path = third_party/llvm-project
+	url = https://github.com/llvm/llvm-project.git

+ 4 - 1
.pre-commit-config.yaml

@@ -34,7 +34,7 @@ repos:
             Part of the Carbon Language project, under the Apache License v2.0 with LLVM
             Exceptions. See /LICENSE for license information.
             SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-        exclude: '^website/(firebase/.firebaserc|jekyll/(Gemfile.lock|theme/.*))$'
+        exclude: '^(website/(firebase/.firebaserc|jekyll/(Gemfile.lock|theme/.*))|.bazelversion|compile_flags.txt|.*\.def)$'
       - id: check-google-doc-style
       - id: markdown-toc
   - repo: https://github.com/codespell-project/codespell
@@ -67,3 +67,6 @@ repos:
     rev: 3.8.4
     hooks:
       - id: flake8
+
+# We fully exclude fuzzer corpus directories as they are generated binary data.
+exclude: '^(.*/fuzzer_corpus/.*)$'

+ 3 - 0
BUILD

@@ -0,0 +1,3 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

+ 48 - 0
WORKSPACE

@@ -0,0 +1,48 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+workspace(name = "carbon")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# We want to use LLVM via an external CMake build, so pull in the Bazel
+# infrastructure that provides direct CMake interfacing support.
+http_archive(
+    name = "rules_foreign_cc",
+    strip_prefix = "rules_foreign_cc-master",
+    url = "https://github.com/bazelbuild/rules_foreign_cc/archive/master.zip",
+)
+
+# Set up necessary dependencies for working with the foreign C++ rules.
+load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies")
+
+rules_foreign_cc_dependencies()
+
+# Detect and configure a Clang and LLVM based toolchain.
+load("//bazel/cc_toolchains:clang_detection.bzl", "detect_clang_toolchain")
+
+detect_clang_toolchain(name = "bazel_cc_toolchain")
+
+local_repository(
+    name = "llvm_bazel",
+    path = "third_party/llvm-bazel/llvm-bazel",
+)
+
+load("@llvm_bazel//:configure.bzl", "llvm_configure")
+
+llvm_configure(
+    name = "llvm-project",
+    src_path = "third_party/llvm-project",
+    src_workspace = "@carbon//:WORKSPACE",
+)
+
+load("@llvm_bazel//:terminfo.bzl", "llvm_terminfo_system")
+
+# We require successful detection and use of a system terminfo library.
+llvm_terminfo_system(name = "llvm_terminfo")
+
+load("@llvm_bazel//:zlib.bzl", "llvm_zlib_system")
+
+# We require successful detection and use of a system zlib library.
+llvm_zlib_system(name = "llvm_zlib")

+ 6 - 0
bazel/cc_toolchains/BUILD

@@ -0,0 +1,6 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Empty `BUILD` file as this package just provides repository rule inputs to
+# configure a Clang toolchain.

+ 725 - 0
bazel/cc_toolchains/clang_cc_toolchain_config.bzl

@@ -0,0 +1,725 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""A Starlark cc_toolchain configuration rule"""
+
+load(
+    "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+    "action_config",
+    "feature",
+    "feature_set",
+    "flag_group",
+    "flag_set",
+    "tool",
+    "tool_path",
+    "variable_with_value",
+    "with_feature_set",
+)
+load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+load(
+    ":clang_detected_variables.bzl",
+    "clang_include_dirs_list",
+    "clang_resource_dir",
+    "llvm_bindir",
+)
+
+all_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.assemble,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+    ACTION_NAMES.cpp_module_codegen,
+]
+
+all_cpp_compile_actions = [
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+    ACTION_NAMES.cpp_module_codegen,
+]
+
+preprocessor_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_header_parsing,
+    ACTION_NAMES.cpp_module_compile,
+]
+
+codegen_compile_actions = [
+    ACTION_NAMES.c_compile,
+    ACTION_NAMES.cpp_compile,
+    ACTION_NAMES.linkstamp_compile,
+    ACTION_NAMES.assemble,
+    ACTION_NAMES.preprocess_assemble,
+    ACTION_NAMES.cpp_module_codegen,
+]
+
+all_link_actions = [
+    ACTION_NAMES.cpp_link_executable,
+    ACTION_NAMES.cpp_link_dynamic_library,
+    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+]
+
+def _impl(ctx):
+    tool_paths = [
+        tool_path(name = "ar", path = llvm_bindir + "/llvm-ar"),
+        tool_path(name = "ld", path = llvm_bindir + "/ld.lld"),
+        tool_path(name = "cpp", path = llvm_bindir + "/clang-cpp"),
+        tool_path(name = "gcc", path = llvm_bindir + "/clang"),
+        tool_path(name = "dwp", path = llvm_bindir + "/llvm-dwp"),
+        tool_path(name = "gcov", path = llvm_bindir + "/llvm-cov"),
+        tool_path(name = "nm", path = llvm_bindir + "/llvm-nm"),
+        tool_path(name = "objcopy", path = llvm_bindir + "/llvm-objcopy"),
+        tool_path(name = "objdump", path = llvm_bindir + "/llvm-objdump"),
+        tool_path(name = "strip", path = llvm_bindir + "/llvm-strip"),
+    ]
+
+    action_configs = [
+        action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang")])
+        for name in [ACTION_NAMES.c_compile]
+    ] + [
+        action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang++")])
+        for name in all_cpp_compile_actions
+    ] + [
+        action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/clang++")])
+        for name in all_link_actions
+    ] + [
+        action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-ar")])
+        for name in [ACTION_NAMES.cpp_link_static_library]
+    ] + [
+        action_config(action_name = name, enabled = True, tools = [tool(path = llvm_bindir + "/llvm-strip")])
+        for name in [ACTION_NAMES.strip]
+    ]
+
+    default_compile_flags_feature = feature(
+        name = "default_compile_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-Wall",
+                            "-Wextra",
+                            "-Wthread-safety",
+                            "-Wself-assign",
+                            # Unfortunately, LLVM isn't clean for this warning.
+                            "-Wno-unused-parameter",
+                            # We use partial sets of designated initializers in
+                            # test code.
+                            "-Wno-missing-field-initializers",
+                            "-fcolor-diagnostics",
+                        ],
+                    ),
+                ]),
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-MD", "-MF", "%{dependency_file}"],
+                        expand_if_available = "dependency_file",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-frandom-seed=%{output_file}"],
+                        expand_if_available = "output_file",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(flags = ["-fPIC"], expand_if_available = "pic"),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_codegen,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-gsplit-dwarf"],
+                        expand_if_available = "per_object_debug_info_file",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-D%{preprocessor_defines}"],
+                        iterate_over = "preprocessor_defines",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-include", "%{includes}"],
+                        iterate_over = "includes",
+                        expand_if_available = "includes",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.linkstamp_compile,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-iquote", "%{quote_include_paths}"],
+                        iterate_over = "quote_include_paths",
+                    ),
+                    flag_group(
+                        flags = ["-I%{include_paths}"],
+                        iterate_over = "include_paths",
+                    ),
+                    flag_group(
+                        flags = ["-isystem", "%{system_include_paths}"],
+                        iterate_over = "system_include_paths",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = ["-g"],
+                    ),
+                ]),
+                with_features = [with_feature_set(features = ["dbg"])],
+            ),
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-g0",
+                            "-O3",
+                            "-DNDEBUG",
+                            "-ffunction-sections",
+                            "-fdata-sections",
+                            # Even when optimizing, preserve frame pointers for profiling.
+                            "-fno-omit-frame-pointer",
+                            "-mno-omit-leaf-frame-pointer",
+                        ],
+                    ),
+                ]),
+                with_features = [with_feature_set(features = ["opt"])],
+            ),
+            flag_set(
+                actions = all_cpp_compile_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-std=c++17",
+                            "-stdlib=libc++",
+                        ],
+                    ),
+                ]),
+            ),
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{user_compile_flags}"],
+                        iterate_over = "user_compile_flags",
+                        expand_if_available = "user_compile_flags",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-no-canonical-prefixes",
+                            "-Wno-builtin-macro-redefined",
+                            "-D__DATE__=\"redacted\"",
+                            "-D__TIMESTAMP__=\"redacted\"",
+                            "-D__TIME__=\"redacted\"",
+                        ],
+                    ),
+                ]),
+            ),
+            flag_set(
+                actions = all_compile_actions,
+                flag_groups = [
+                    flag_group(
+                        expand_if_available = "source_file",
+                        flags = ["-c", "%{source_file}"],
+                    ),
+                    flag_group(
+                        expand_if_available = "output_assembly_file",
+                        flags = ["-S"],
+                    ),
+                    flag_group(
+                        expand_if_available = "output_preprocess_file",
+                        flags = ["-E"],
+                    ),
+                    flag_group(
+                        expand_if_available = "output_file",
+                        flags = ["-o", "%{output_file}"],
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    default_link_flags_feature = feature(
+        name = "default_link_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.cpp_link_dynamic_library,
+                    ACTION_NAMES.cpp_link_nodeps_dynamic_library,
+                ],
+                flag_groups = [flag_group(flags = ["-shared"])],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{linkstamp_paths}"],
+                        iterate_over = "linkstamp_paths",
+                        expand_if_available = "linkstamp_paths",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["-o", "%{output_execpath}"],
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.cpp_link_executable,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["-pie"],
+                        expand_if_available = "force_pic",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-fuse-ld=lld",
+                            "-Wl,-no-as-needed",
+                            # Force the C++ standard library to be statically
+                            # linked. This works even with libc++ despite the
+                            # name, however we have to manually link the ABI
+                            # library and libunwind.
+                            "-static-libstdc++",
+                            # Link with libc++.
+                            "-stdlib=libc++",
+                            # Force static linking with libc++abi as well.
+                            "-l:libc++abi.a",
+                            # Link with Clang's runtime library. This is always
+                            # linked statically.
+                            #"-rtlib=compiler-rt",
+                        ],
+                    ),
+                ]),
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["-L%{library_search_directories}"],
+                        iterate_over = "library_search_directories",
+                        expand_if_available = "library_search_directories",
+                    ),
+                ],
+            ),
+            flag_set(
+                flag_groups = [
+                    flag_group(
+                        iterate_over = "runtime_library_search_directories",
+                        flags = [
+                            "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}",
+                        ],
+                        expand_if_available =
+                            "runtime_library_search_directories",
+                    ),
+                ],
+                actions = all_link_actions,
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["-Wl,--gdb-index"],
+                        expand_if_available = "is_using_fission",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["-Wl,-S"],
+                        expand_if_available = "strip_debug_symbols",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        iterate_over = "libraries_to_link",
+                        flag_groups = [
+                            flag_group(
+                                flags = ["-Wl,--start-lib"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file_group",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["-Wl,-whole-archive"],
+                                expand_if_true =
+                                    "libraries_to_link.is_whole_archive",
+                            ),
+                            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",
+                                ),
+                            ),
+                            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.name}"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "interface_library",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["%{libraries_to_link.name}"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "static_library",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["-l%{libraries_to_link.name}"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "dynamic_library",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["-l:%{libraries_to_link.name}"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "versioned_dynamic_library",
+                                ),
+                            ),
+                            flag_group(
+                                flags = ["-Wl,-no-whole-archive"],
+                                expand_if_true = "libraries_to_link.is_whole_archive",
+                            ),
+                            flag_group(
+                                flags = ["-Wl,--end-lib"],
+                                expand_if_equal = variable_with_value(
+                                    name = "libraries_to_link.type",
+                                    value = "object_file_group",
+                                ),
+                            ),
+                        ],
+                        expand_if_available = "libraries_to_link",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["%{user_link_flags}"],
+                        iterate_over = "user_link_flags",
+                        expand_if_available = "user_link_flags",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [
+                    ACTION_NAMES.cpp_link_static_library,
+                ] + all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        expand_if_available = "linker_param_file",
+                        flags = ["@%{linker_param_file}"],
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    sysroot_feature = feature(
+        name = "sysroot",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = all_compile_actions + all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["--sysroot=%{sysroot}"],
+                        expand_if_available = "sysroot",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    default_archiver_flags_feature = feature(
+        name = "default_archiver_flags",
+        enabled = True,
+        flag_sets = [
+            flag_set(
+                actions = [ACTION_NAMES.cpp_link_static_library],
+                flag_groups = [
+                    flag_group(flags = ["rcsD"]),
+                    flag_group(
+                        flags = ["%{output_execpath}"],
+                        expand_if_available = "output_execpath",
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = [ACTION_NAMES.cpp_link_static_library],
+                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",
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    use_module_maps = feature(
+        name = "use_module_maps",
+        requires = [feature_set(features = ["module_maps"])],
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    # These flag groups are separate so they do not expand to
+                    # the cross product of the variables.
+                    flag_group(flags = ["-fmodule-name=%{module_name}"]),
+                    flag_group(
+                        flags = ["-fmodule-map-file=%{module_map_file}"],
+                    ),
+                ],
+            ),
+        ],
+    )
+
+    # Tell bazel we support module maps in general, so they will be generated
+    # for all c/c++ rules.
+    # Note: not all C++ rules support module maps; thus, do not imply this
+    # feature from other features - instead, require it.
+    module_maps = feature(
+        name = "module_maps",
+        enabled = True,
+        implies = [
+            # "module_map_home_cwd",
+            # "module_map_without_extern_module",
+            # "generate_submodules",
+        ],
+    )
+
+    layering_check = feature(
+        name = "layering_check",
+        implies = ["use_module_maps"],
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.cpp_module_compile,
+                ],
+                flag_groups = [
+                    flag_group(flags = [
+                        "-fmodules-strict-decluse",
+                        "-Wprivate-header",
+                    ]),
+                    flag_group(
+                        iterate_over = "dependent_module_map_files",
+                        flags = [
+                            "-fmodule-map-file=%{dependent_module_map_files}",
+                        ],
+                    ),
+                ],
+            ),
+        ],
+    )
+    fuzzer = feature(
+        name = "fuzzer",
+        flag_sets = [
+            flag_set(
+                actions = all_compile_actions + all_link_actions,
+                flag_groups = [
+                    flag_group(
+                        flags = ["-fsanitize=fuzzer,address"],
+                    ),
+                ],
+            ),
+            flag_set(
+                actions = all_link_actions,
+                flag_groups = ([
+                    flag_group(
+                        flags = [
+                            "-static-libsan",
+                        ],
+                    ),
+                ]),
+            ),
+        ],
+    )
+
+    features = [
+        feature(
+            name = "no_legacy_features",
+        ),
+        feature(
+            name = "supports_pic",
+            enabled = True,
+        ),
+        feature(
+            name = "supports_start_end_lib",
+            enabled = True,
+        ),
+        default_compile_flags_feature,
+        default_archiver_flags_feature,
+        default_link_flags_feature,
+        feature(name = "supports_dynamic_linker", enabled = True),
+        feature(name = "dbg"),
+        feature(name = "opt"),
+        sysroot_feature,
+        fuzzer,
+        module_maps,
+        layering_check,
+        use_module_maps,
+    ]
+
+    return cc_common.create_cc_toolchain_config_info(
+        ctx = ctx,
+        features = features,
+        action_configs = action_configs,
+        cxx_builtin_include_directories = clang_include_dirs_list + [
+            # Append the share directory for sanitizer data files.
+            clang_resource_dir + "/share",
+        ],
+        toolchain_identifier = "local",
+        host_system_name = "local",
+        target_system_name = "local",
+        target_cpu = "k8",
+        target_libc = "local",
+        compiler = "local",
+        abi_version = "local",
+        abi_libc_version = "local",
+        tool_paths = tool_paths,
+    )
+
+cc_toolchain_config = rule(
+    implementation = _impl,
+    provides = [CcToolchainConfigInfo],
+)

+ 13 - 0
bazel/cc_toolchains/clang_detected_variables.tpl.bzl

@@ -0,0 +1,13 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""A Starlark file exporting detected Clang configuration variables.
+
+This file gets processed by a repository rule, substituting the
+`{VARIABLE}`s with values detected by invoking Clang.
+"""
+
+llvm_bindir = "{LLVM_BINDIR}"
+clang_resource_dir = "{CLANG_RESOURCE_DIR}"
+clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST}

+ 169 - 0
bazel/cc_toolchains/clang_detection.bzl

@@ -0,0 +1,169 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""Starlark repository rules to detect and configure Clang (and LLVM) toolchain.
+
+These rules should be run from the `WORKSPACE` file to substitute appropriate
+configured values into a `clang_detected_variables.bzl` file that can be used
+by the actual toolchain configuration.
+"""
+
+# Tools that we verify are present as part of the detected Clang & LLVM toolchain.
+_CLANG_LLVM_TOOLS = [
+    "llvm-ar",
+    "ld.lld",
+    "clang-cpp",
+    "clang",
+    "clang++",
+    "llvm-dwp",
+    "llvm-cov",
+    "llvm-nm",
+    "llvm-objcopy",
+    "llvm-strip",
+]
+
+def _run(repository_ctx, cmd):
+    """Runs the provided `cmd`, checks for failure, and returns the result."""
+    exec_result = repository_ctx.execute(cmd, timeout = 10)
+    if exec_result.return_code != 0:
+        fail("Unable to run command successfully: %s" % str(cmd))
+
+    return exec_result
+
+def _find_clang(repository_ctx):
+    """Returns the path to a Clang executable if it can find one.
+
+    This assumes the `CC` environment variable points to a Clang binary or
+    looks for one on the path.
+    """
+    cc_env = repository_ctx.os.environ.get("CC")
+    if not cc_env:
+        # Without a specified `CC` name, simply look for `clang`.
+        clang = repository_ctx.which("clang")
+    elif "/" not in cc_env:
+        # Lookup relative `CC` names according to the system `PATH`.
+        clang = repository_ctx.which(cc_env)
+    else:
+        # An absolute `CC` path is simply be used directly.
+        clang = repository_ctx.path(cc_env)
+        if not clang.exists:
+            fail(("The `CC` environment variable is set to a path (`%s`) " +
+                  "that doesn't exist.") % cc_env)
+
+    # Check if either of the `which` invocations fail.
+    if not clang:
+        missing = "`clang`"
+        if cc_env:
+            missing = "`%s` (from the `CC` environment variable)" % cc_env
+        fail("Unable to find the %s executable on the PATH." % missing)
+
+    version_output = _run(repository_ctx, [clang, "--version"]).stdout
+    if "clang" not in version_output:
+        fail(("Selected Clang executable (`%s`) does not appear to actually " +
+              "be Clang.") % clang)
+
+    # Make sure this is part of a complete Clang and LLVM toolchain.
+    for tool in _CLANG_LLVM_TOOLS:
+        if not clang.dirname.get_child(tool).exists:
+            fail(("Couldn't find executable `%s` that is expected to be part " +
+                  "of the Clang and LLVM toolchain detected with `%s`.") %
+                 (tool, clang))
+
+    return clang
+
+def _compute_clang_resource_dir(repository_ctx, clang):
+    """Runs the `clang` binary to get its resource dir."""
+    output = _run(
+        repository_ctx,
+        [clang, "-no-canonical-prefixes", "--print-resource-dir"],
+    ).stdout
+
+    # The only line printed is this path.
+    return output.splitlines()[0]
+
+def _compute_clang_cpp_include_search_paths(repository_ctx, clang):
+    """Runs the `clang` binary and extracts the include search paths.
+
+    Returns the resulting paths as a list of strings.
+    """
+
+    # The only way to get this out of Clang currently is to parse the verbose
+    # output of the compiler when it is compiling C++ code.
+    cmd = [
+        clang,
+        # Avoid canonicalizing away symlinks.
+        "-no-canonical-prefixes",
+        # Extract verbose output.
+        "-v",
+        # Just parse the input, don't generate outputs.
+        "-fsyntax-only",
+        # Use libc++ rather than any other standard library.
+        "-stdlib=libc++",
+        # Force the language to be C++.
+        "-x",
+        "c++",
+        # Read in an empty input file.
+        "/dev/null",
+    ]
+
+    # Note that verbose output is on stderr, not stdout!
+    output = _run(repository_ctx, cmd).stderr.splitlines()
+
+    # Return the list of directories printed for system headers. These are the
+    # only ones that Bazel needs us to manually provide. We find these by
+    # searching for a begin and end marker. We also have to strip off a leading
+    # space from each path.
+    include_begin = output.index("#include <...> search starts here:") + 1
+    include_end = output.index("End of search list.", include_begin)
+    return [
+        repository_ctx.path(s.lstrip(" "))
+        for s in output[include_begin:include_end]
+    ]
+
+def _detect_clang_toolchain_impl(repository_ctx):
+    # First just symlink in the untemplated parts of the toolchain repo.
+    repository_ctx.symlink(repository_ctx.attr._clang_toolchain_build, "BUILD")
+    repository_ctx.symlink(
+        repository_ctx.attr._clang_cc_toolchain_config,
+        "cc_toolchain_config.bzl",
+    )
+
+    clang = _find_clang(repository_ctx)
+    resource_dir = _compute_clang_resource_dir(repository_ctx, clang)
+    include_dirs = _compute_clang_cpp_include_search_paths(
+        repository_ctx,
+        clang,
+    )
+
+    repository_ctx.template(
+        "clang_detected_variables.bzl",
+        repository_ctx.attr._clang_detected_variables_template,
+        substitutions = {
+            "{LLVM_BINDIR}": str(clang.dirname),
+            "{CLANG_RESOURCE_DIR}": resource_dir,
+            "{CLANG_INCLUDE_DIRS_LIST}": str([str(path) for path in include_dirs]),
+        },
+        executable = False,
+    )
+
+detect_clang_toolchain = repository_rule(
+    implementation = _detect_clang_toolchain_impl,
+    configure = True,
+    local = True,
+    attrs = {
+        "_clang_toolchain_build": attr.label(
+            default = Label("//bazel/cc_toolchains:clang_toolchain.BUILD"),
+            allow_single_file = True,
+        ),
+        "_clang_cc_toolchain_config": attr.label(
+            default = Label("//bazel/cc_toolchains:clang_cc_toolchain_config.bzl"),
+            allow_single_file = True,
+        ),
+        "_clang_detected_variables_template": attr.label(
+            default = Label("//bazel/cc_toolchains:clang_detected_variables.tpl.bzl"),
+            allow_single_file = True,
+        ),
+    },
+    environ = ["CC"],
+)

+ 44 - 0
bazel/cc_toolchains/clang_toolchain.BUILD

@@ -0,0 +1,44 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# This file is symlinked into a configured Clang toolchain repository as the
+# root `BUILD` file for that repository.
+
+load("@rules_cc//cc:defs.bzl", "cc_library", "cc_toolchain", "cc_toolchain_suite")
+load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+
+cc_library(
+    name = "malloc",
+)
+
+filegroup(
+    name = "empty",
+    srcs = [],
+)
+
+cc_toolchain_suite(
+    name = "bazel_cc_toolchain",
+    toolchains = {
+        "k8": ":cc-compiler-k8",
+    },
+)
+
+cc_toolchain(
+    name = "cc-compiler-k8",
+    all_files = ":empty",
+    ar_files = ":empty",
+    as_files = ":empty",
+    compiler_files = ":empty",
+    dwp_files = ":empty",
+    linker_files = ":empty",
+    objcopy_files = ":empty",
+    strip_files = ":empty",
+    supports_param_files = 1,
+    toolchain_config = ":local",
+    toolchain_identifier = "local",
+)
+
+cc_toolchain_config(
+    name = "local",
+)

+ 5 - 0
bazel/fuzzing/BUILD

@@ -0,0 +1,5 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Empty BUILD file to allow access to Bazel extensions.

+ 51 - 0
bazel/fuzzing/rules.bzl

@@ -0,0 +1,51 @@
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""Rules for building fuzz tests."""
+
+load("@rules_cc//cc:defs.bzl", "cc_test")
+
+def cc_fuzz_test(
+        name,
+        corpus = None,
+        args = [],
+        data = [],
+        features = [],
+        tags = [],
+        **kwargs):
+    """Macro for C++ fuzzing test.
+
+    Args:
+        name: The main fuzz test rule name.
+        corpus: List of files to use as a fuzzing corpus.
+        args: Will have the locations of the corpus files added and passed down
+            to the fuzz test.
+        data: Will have the corpus added and passed down to the fuzz test.
+        features: Will have the "fuzzer" feature added and passed down to the
+            fuzz test.
+        tags: Will have "fuzz_test" added and passed down to the fuzz test.
+        **kwargs: Remaining arguments passed down to the fuzz test.
+    """
+
+    # Add relevant tag and feature if necessary.
+    if "fuzz_test" not in tags:
+        tags = tags + ["fuzz_test"]
+    if "fuzzer" not in features:
+        features = features + ["fuzzer"]
+
+    # Append the corpus files to the test arguments. When run on a list of
+    # files rather than a directory, libFuzzer-based fuzzers will perform a
+    # regression test against the corpus.
+    if corpus:
+        data = data + corpus
+        args = args + ["$(location %s)" % file for file in corpus]
+
+    cc_test(
+        name = name,
+        args = args,
+        data = data,
+        features = features,
+        tags = tags,
+        **kwargs
+    )

+ 1 - 0
third_party/llvm-bazel

@@ -0,0 +1 @@
+Subproject commit 1536b141d636e95f2cc3c02c5891b23e322fb0c8

+ 19 - 0
third_party/update_llvm.sh

@@ -0,0 +1,19 @@
+#!/bin/bash -eux
+# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+# Exceptions. See /LICENSE for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Ensure the working directory is the script's directory.
+cd "$(dirname "$0")"
+
+# Update the `llvm-bazel` project first by just pulling from its HEAD.
+git submodule update --remote llvm-bazel
+
+# Find the current LLVM commit in the `llvm-bazel` project.
+llvm_commit=$(cd llvm-bazel; git submodule status third_party/llvm-project | awk '/-[0-9a-f]+ / { print substr($1, 2) }')
+
+# Fetch and checkout this commit of LLVM
+cd llvm-project
+git fetch
+git checkout $llvm_commit
+cd ..