mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-30 22:02:41 +01:00
Now that LLVM 12 has been released we no longer have any need to bootstrap LLVM to get the desired featureset. LLVM 12 is available widely, including in Homebrew across multiple platforms and in the GitHub action runners. Sadly, the Linux distribution builds of LLVM-12 are largely broken and not as useful for us. The Homebrew Linux install was also broken originally, but I've worked extensively with the Homebrew folks to get the Linux install into a really good shape. It should now work reliably. There are two primary bugs in Linux LLVM packages that need to be fixed before we can just use them: - https://bugs.llvm.org/show_bug.cgi?id=43604 - https://bugs.llvm.org/show_bug.cgi?id=46321 Once those are addressed and point releases with the fixes widely available we can further simplify things. Even with the need to use Homebrew installs, using the released LLVM has the extra advantage of making it easy to properly support Darwin ARM and I've added that configuration so that I can test things there. Last but not least, this will significantly shrink our build outputs which should allow building much more in continuous integration on GitHub actions without exceeding the action cache size limits. I've even added several tweaks and adjustments to the compile and build flags to improve the build performance and reduce the build output size. Once this is landed and stable, we can consider adding the refactoring tooling back to our CI. One of the biggest downsides of this path is that our CI has to download and install the LLVM toolchain from Homebrew on each run. This is pretty slow (takes a couple of minutes). But it is a fixed overhead -- it won't get worse over time. Eventually, we can either look at a much fancier action configuration to avoid this or hopefully the Debian packages will get updated and we can move back to those. The bootstrapping has served us long enough at this point. We can resurrect it if we ever find a compelling reason for breaking off of the latest LLVM release as our host toolchain. Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
# 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 configure Clang (and LLVM) toolchain for Bazel.
|
|
|
|
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.
|
|
"""
|
|
|
|
def _run(repository_ctx, cmd):
|
|
"""Runs the provided `cmd`, checks for failure, and returns the result."""
|
|
exec_result = repository_ctx.execute(cmd)
|
|
if exec_result.return_code != 0:
|
|
fail("Unable to run command successfully: %s" % str(cmd))
|
|
|
|
return exec_result
|
|
|
|
def _detect_system_clang(repository_ctx):
|
|
"""Detects whether the system-provided clang can be used.
|
|
|
|
Returns a tuple of (is_clang, environment).
|
|
"""
|
|
|
|
# If the user provides an explicit `CC` environment variable, use that as
|
|
# the compiler. This should point at the `clang` executable to use.
|
|
cc = repository_ctx.os.environ.get("CC")
|
|
if cc:
|
|
version_output = _run(repository_ctx, [cc, "--version"]).stdout
|
|
if not "clang" in version_output:
|
|
fail("The `CC` environment variable is not a Clang compiler.")
|
|
return repository_ctx.path(cc)
|
|
|
|
# Try looking on the path. We check for the specific versioned name and then the normal name.
|
|
system_clang = repository_ctx.which("clang-12")
|
|
if not system_clang:
|
|
system_clang = repository_ctx.which("clang")
|
|
if not system_clang:
|
|
fail("Unable to find a `clang` executable on the system path.")
|
|
|
|
return system_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_mac_os_sysroot(repository_ctx):
|
|
"""Runs `xcrun` to extract the correct sysroot."""
|
|
xcrun = repository_ctx.which("xcrun")
|
|
if not xcrun:
|
|
fail("`xcrun` not found: is Xcode installed?")
|
|
output = _run(repository_ctx, [xcrun, "--show-sdk-path"]).stdout
|
|
return output.splitlines()[0]
|
|
|
|
def _compute_clang_cpp_include_search_paths(repository_ctx, clang, sysroot):
|
|
"""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",
|
|
# Force the language to be C++.
|
|
"-x",
|
|
"c++",
|
|
# Read in an empty input file.
|
|
"/dev/null",
|
|
# Always use libc++.
|
|
"-stdlib=libc++",
|
|
]
|
|
|
|
# We need to use a sysroot to correctly represent a run on macOS.
|
|
if repository_ctx.os.name.lower().startswith("mac os"):
|
|
if not sysroot:
|
|
fail("Must provide a sysroot on macOS!")
|
|
cmd.append("--sysroot=" + sysroot)
|
|
|
|
# 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 _configure_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",
|
|
)
|
|
|
|
# Find a Clang C++ compiler, and where it lives. We need to walk symlinks
|
|
# here as the other LLVM tools may not be symlinked into the PATH even if
|
|
# `clang` is. We also insist on finding the basename of `clang++` as that is
|
|
# important for C vs. C++ compiles.
|
|
clang = _detect_system_clang(repository_ctx)
|
|
clang = clang.realpath.dirname.get_child("clang++")
|
|
|
|
# Compute the various directories used by Clang.
|
|
resource_dir = _compute_clang_resource_dir(repository_ctx, clang)
|
|
sysroot_dir = None
|
|
if repository_ctx.os.name.lower().startswith("mac os"):
|
|
sysroot_dir = _compute_mac_os_sysroot(repository_ctx)
|
|
include_dirs = _compute_clang_cpp_include_search_paths(
|
|
repository_ctx,
|
|
clang,
|
|
sysroot_dir,
|
|
)
|
|
|
|
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],
|
|
),
|
|
"{SYSROOT}": str(sysroot_dir),
|
|
},
|
|
executable = False,
|
|
)
|
|
|
|
configure_clang_toolchain = repository_rule(
|
|
implementation = _configure_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"],
|
|
)
|