From aba77b12b9a196b8a9a2faa603dbe8cbac5cc550 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Thu, 15 Sep 2022 13:23:59 -0700 Subject: [PATCH] Modify clang configuration to make llvm-15 work better. (#2186) The debug flag change is discussed at https://github.com/llvm/llvm-project/issues/57637 This modifies the devcontainer Dockerfile to switch to an ubuntu and apt-based llvm-15. That was used in testing of these changes. The move away from brew is partly necessary if we want llvm-15, but also installs much faster (roughly 90s setup). This was based in part on #1618 --- .devcontainer/Dockerfile | 42 +++++++++++++------ .../clang_cc_toolchain_config.bzl | 13 ++++-- bazel/cc_toolchains/clang_configuration.bzl | 32 +++++++++++--- .../clang_detected_variables.tpl.bzl | 1 + 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 16e8c70ec7fb..8380186918bd 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,19 +2,37 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -ARG TAG=3.5.6 -FROM docker.io/homebrew/brew:${TAG} +FROM ubuntu:22.04 -# Install libxcrypt using Homebrew. -RUN brew install libxcrypt --overwrite +# Install apt tools: +# git: Used by VS Code. +# golang: Used for Bazelisk and Buildifier. +# python3: For Carbon tools. +# gnupg, software-properties-common, wget: For llvm.sh. +RUN apt-get update +RUN apt-get install -y \ + git \ + gnupg \ + golang \ + python3-pip \ + python3.9 \ + software-properties-common \ + wget -# Install all other dependencies using Homebrew. -RUN brew install bazelisk node python@3.9 +ENV PATH="/root/go/bin:${PATH}" +# Bazelisk is used for Carbon builds. +RUN go install github.com/bazelbuild/bazelisk@v1.14.0 +RUN ln -s /root/go/bin/bazelisk /root/go/bin/bazel +# Buildifier is used by the Bazel VS Code extension. +RUN go install github.com/bazelbuild/buildtools/buildifier@5.1.0 -# Install all python dependencies modules using Pip. +# Install LLVM from apt.llvm.org. +RUN wget https://apt.llvm.org/llvm.sh +RUN chmod +x llvm.sh +RUN ./llvm.sh 15 all +RUN rm llvm.sh +ENV CC="/usr/bin/clang-15" + +# Update pip and install black and pre-commit. RUN pip3 install -U pip -RUN pip3 install pre-commit black codespell - -# Install Clang/LLVM using Homebrew. -# Many Clang/LLVM releases aren't built with options we rely on. -RUN brew install llvm +RUN pip3 install black pre-commit diff --git a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl index d328ee04771c..8db25228df84 100644 --- a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl +++ b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl @@ -20,6 +20,7 @@ load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") load( ":clang_detected_variables.bzl", "clang_bindir", + "clang_version", "clang_include_dirs_list", "clang_resource_dir", "llvm_bindir", @@ -496,6 +497,13 @@ def _impl(ctx): implies = ["fuzzer"], ) + # With clang 14 and lower, we expect it to be built with libc++ debug + # support. In later LLVM versions, we expect the assertions define to work. + if clang_version and clang_version <= 14: + libcpp_debug_flags = ["-D_LIBCPP_DEBUG=1"] + else: + libcpp_debug_flags = ["-D_LIBCPP_ENABLE_ASSERTIONS=1"] + linux_flags_feature = feature( name = "linux_flags", enabled = True, @@ -537,10 +545,7 @@ def _impl(ctx): ), flag_set( actions = all_compile_actions, - flag_groups = [flag_group(flags = [ - # Enable libc++'s debug features. - "-D_LIBCPP_DEBUG=1", - ])], + flag_groups = [flag_group(flags = libcpp_debug_flags)], with_features = [ with_feature_set(not_features = ["opt"]), ], diff --git a/bazel/cc_toolchains/clang_configuration.bzl b/bazel/cc_toolchains/clang_configuration.bzl index 00e10615c3db..f20f07cc5120 100644 --- a/bazel/cc_toolchains/clang_configuration.bzl +++ b/bazel/cc_toolchains/clang_configuration.bzl @@ -17,6 +17,27 @@ def _run(repository_ctx, cmd): return exec_result +def _clang_version(version_output): + """Returns clang's major version number, or None if not found.""" + version_prefix = "clang version " + version_start = version_output.find(version_prefix) + if version_start == -1: + # No version + return None + version_start += len(version_prefix) + + # Find a dot to indicate something like 'clang version 14.0.6'. + version_dot = version_output.find(".", version_start) + if version_dot == -1: + return None + + # Make sure the dot was on the same line as the version. + if version_output.find("\n", version_start) < version_dot: + return None + + # Return the version as int. + return int(version_output[version_start:version_dot]) + def _detect_system_clang(repository_ctx): """Detects whether the system-provided clang can be used. @@ -38,7 +59,7 @@ def _detect_system_clang(repository_ctx): version_output = _run(repository_ctx, [cc_path, "--version"]).stdout if "clang" not in version_output: fail("Searching for clang or CC (%s), and found (%s), which is not a Clang compiler" % (cc, cc_path)) - return cc_path + return (cc_path, _clang_version(version_output)) def _compute_clang_resource_dir(repository_ctx, clang): """Runs the `clang` binary to get its resource dir.""" @@ -66,14 +87,14 @@ def _compute_clang_cpp_include_search_paths(repository_ctx, clang, sysroot): # Create an empty temp file for Clang to use if repository_ctx.os.name.lower().startswith("windows"): - repository_ctx.file('_temp', '') + repository_ctx.file("_temp", "") # Read in an empty input file. If we are building from # Windows, then we create an empty temp file. Clang # on Windows does not like it when you pass a non-existent file. if repository_ctx.os.name.lower().startswith("windows"): - repository_ctx.file('_temp', '') - input_file = repository_ctx.path('_temp') + repository_ctx.file("_temp", "") + input_file = repository_ctx.path("_temp") else: input_file = "/dev/null" @@ -131,7 +152,7 @@ def _configure_clang_toolchain_impl(repository_ctx): # 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_version) = _detect_system_clang(repository_ctx) clang = clang.realpath.dirname.get_child("clang++") # Compute the various directories used by Clang. @@ -168,6 +189,7 @@ def _configure_clang_toolchain_impl(repository_ctx): substitutions = { "{LLVM_BINDIR}": str(arpath.dirname), "{CLANG_BINDIR}": str(clang.dirname), + "{CLANG_VERSION}": str(clang_version), "{CLANG_RESOURCE_DIR}": resource_dir, "{CLANG_INCLUDE_DIRS_LIST}": str( [str(path) for path in include_dirs], diff --git a/bazel/cc_toolchains/clang_detected_variables.tpl.bzl b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl index f0308c7e4026..955456a5e99f 100644 --- a/bazel/cc_toolchains/clang_detected_variables.tpl.bzl +++ b/bazel/cc_toolchains/clang_detected_variables.tpl.bzl @@ -10,6 +10,7 @@ This file gets processed by a repository rule, substituting the llvm_bindir = "{LLVM_BINDIR}" clang_bindir = "{CLANG_BINDIR}" +clang_version = {CLANG_VERSION} clang_resource_dir = "{CLANG_RESOURCE_DIR}" clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST} sysroot_dir = "{SYSROOT}"