From 5d415295903f433ec430ea009916dea1fe85db4d Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Wed, 11 Mar 2026 18:24:56 -0700 Subject: [PATCH] Introduce a Bazel-integrated build for the installed runtimes (#6872) This shifts the Bazel toolchain configuration of our installation to build all of the Clang runtimes Carbon uses on-demand natively in Bazel. We export the information about how to build into a generated Starlark file, and emit BUILD files and Starlark logic into the installation to orchestrate the build. This requires some complex management of Bazel toolchains -- we need to first set-up a "runtimes toolchain" that doesn't have runtimes of its own, but can be used to _build_ runtimes. Then we build the runtimes using that toolchain, and assemble them into the standard layout for a Carbon runtimes tree. Finally we configure the _actual_ toolchain with this built tree. Currently, this is only setup for the installed toolchain, but I plan to factor this runtimes build into one that can be used directly as well to break up the monolithic runtimes build step into Bazel-integrated build of the runtimes. This will also serve as the foundation for adding bootstrapping support directly to our Bazel build. --------- Co-authored-by: Dana Jansens --- ...orting_compiler-rt_build_information.patch | 22 +- toolchain/base/BUILD | 4 + toolchain/base/runtimes_build_info.bzl | 49 +++- toolchain/base/runtimes_build_vars.tpl.bzl | 45 +++ toolchain/driver/clang_runtimes.cpp | 5 - toolchain/install/BUILD | 11 + .../bazel/carbon_cc_toolchain_config.bzl | 270 ++++++++++++++++-- toolchain/install/bazel/carbon_runtimes.bzl | 151 ++++++++++ .../install/bazel/clang_resource_dir.BUILD | 10 + toolchain/install/bazel/install.BUILD | 11 +- .../install/bazel/make_include_copts.bzl | 25 ++ toolchain/install/bazel/runtimes.BUILD | 196 +++++++++++++ 12 files changed, 738 insertions(+), 61 deletions(-) create mode 100644 toolchain/base/runtimes_build_vars.tpl.bzl create mode 100644 toolchain/install/bazel/carbon_runtimes.bzl create mode 100644 toolchain/install/bazel/clang_resource_dir.BUILD create mode 100644 toolchain/install/bazel/make_include_copts.bzl create mode 100644 toolchain/install/bazel/runtimes.BUILD diff --git a/bazel/llvm_project/0009_Introduce_starlark_exporting_compiler-rt_build_information.patch b/bazel/llvm_project/0009_Introduce_starlark_exporting_compiler-rt_build_information.patch index 55b7c4e33953..5e41deae4ab2 100644 --- a/bazel/llvm_project/0009_Introduce_starlark_exporting_compiler-rt_build_information.patch +++ b/bazel/llvm_project/0009_Introduce_starlark_exporting_compiler-rt_build_information.patch @@ -1,7 +1,7 @@ -Commit ID: 959284a466ca392a5ccd501cf1c1622d1f423c06 +Commit ID: d3b82534c2546a892a27856672ed95a7db97dba3 Change ID: zyxuvzwmzsnorloyuupuurxkppkoplnw Author : Chandler Carruth (2026-02-16 23:17:06) -Committer: Chandler Carruth (2026-03-08 07:43:17) +Committer: Chandler Carruth (2026-03-11 07:54:02) Improve compiler-rt build structure and export compilation info @@ -12,7 +12,7 @@ Committer: Chandler Carruth (2026-03-08 07:43:17) compiling these source files. diff --git a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel -index 4ded226174..0c3a7daaee 100644 +index 4ded226174..3b5b8fc787 100644 --- a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel @@ -3,6 +3,7 @@ @@ -98,12 +98,14 @@ index 4ded226174..0c3a7daaee 100644 ] + [ AARCH64_OUTLINE_ATOMICS_FMT.format(pat, size, model) for (pat, size, model) in AARCH64_OUTLINE_ATOMICS -@@ -328,10 +360,18 @@ +@@ -328,10 +360,20 @@ "lib/builtins/aarch64/lse.S", # These files are provided by SME-specific file groups above. "lib/builtins/aarch64/*sme*", + # This is only used with MinGW. + "lib/builtins/aarch64/chkstk.S", ++ # TODO: Remove this once we have a way of accessing `SipHash.h`. ++ "lib/builtins/aarch64/emupac.cpp", ], ), ) @@ -117,7 +119,7 @@ index 4ded226174..0c3a7daaee 100644 BUILTINS_ARM_VFP_SRCS_PATTERNS = [ "lib/builtins/arm/*vfp*.S", "lib/builtins/arm/*vfp*.c", -@@ -348,9 +388,19 @@ +@@ -348,9 +390,19 @@ ), ) @@ -138,7 +140,7 @@ index 4ded226174..0c3a7daaee 100644 srcs = glob( [ "lib/builtins/arm/*.S", -@@ -359,14 +409,52 @@ +@@ -359,14 +411,52 @@ "lib/builtins/arm/*.h", ], allow_empty = True, @@ -196,7 +198,7 @@ index 4ded226174..0c3a7daaee 100644 [ "lib/builtins/ppc/*.S", "lib/builtins/ppc/*.c", -@@ -377,17 +465,64 @@ +@@ -377,17 +467,64 @@ ), ) @@ -272,7 +274,7 @@ index 4ded226174..0c3a7daaee 100644 ) # Source files for the x86 architecture specific builtins (both 32-bit and -@@ -402,8 +537,14 @@ +@@ -402,8 +539,14 @@ # Source files for the x86-64 architecture specific builtins. filegroup( @@ -289,7 +291,7 @@ index 4ded226174..0c3a7daaee 100644 [ "lib/builtins/x86_64/*.S", "lib/builtins/x86_64/*.c", -@@ -411,13 +552,29 @@ +@@ -411,13 +554,29 @@ "lib/builtins/x86_64/*.h", ], allow_empty = True, @@ -321,7 +323,7 @@ index 4ded226174..0c3a7daaee 100644 [ "lib/builtins/i386/*.S", "lib/builtins/i386/*.c", -@@ -429,28 +586,16 @@ +@@ -429,28 +588,16 @@ # This file is used for both i386 and x86_64 and so included in the # broader x86 sources. "lib/builtins/i386/fp_mode.c", diff --git a/toolchain/base/BUILD b/toolchain/base/BUILD index c3a5a6880ddc..aed3bda08bb3 100644 --- a/toolchain/base/BUILD +++ b/toolchain/base/BUILD @@ -8,6 +8,10 @@ load("runtimes_build_info.bzl", "generate_runtimes_build_info_cc_library") package(default_visibility = ["//visibility:public"]) +exports_files([ + "runtimes_build_vars.tpl.bzl", +]) + cc_library( name = "block_value_store", hdrs = ["block_value_store.h"], diff --git a/toolchain/base/runtimes_build_info.bzl b/toolchain/base/runtimes_build_info.bzl index 7fb283b5fc32..2fefefb5942f 100644 --- a/toolchain/base/runtimes_build_info.bzl +++ b/toolchain/base/runtimes_build_info.bzl @@ -19,6 +19,7 @@ Future runtimes we plan to add support for but not yet included: - Profiling runtimes """ +load("@llvm-project//:vars.bzl", "LLVM_VERSION_MAJOR") load("@llvm-project//compiler-rt:compiler-rt.bzl", "builtins_copts", "crt_copts") load("@llvm-project//libcxx:libcxx_library.bzl", "libcxx_and_abi_copts") load("@llvm-project//libunwind:libunwind_library.bzl", "libunwind_copts") @@ -124,6 +125,7 @@ def _get_substitutions(ctx): "CRT_COPTS": _format_one_per_line(crt_copts), "LIBCXX_AND_ABI_COPTS": _format_one_per_line(libcxx_and_abi_copts), "LIBUNWIND_COPTS": _format_one_per_line(libunwind_copts), + "LLVM_VERSION_MAJOR": LLVM_VERSION_MAJOR, } | { k.upper(): _get_path(key_attr(k), _builtins_path) for k in CRT_FILES.keys() @@ -146,6 +148,20 @@ def _get_substitutions(ctx): )] } +_common_runtimes_rule_attrs = { + "_" + k: attr.label(default = v, allow_single_file = True) + for k, v in CRT_FILES.items() +} | { + "_" + _get_name(g): attr.label_list(default = [g], allow_files = True) + for g in ( + BUILTINS_SRCS_FILEGROUPS + + BUILTINS_TEXTUAL_SRCS_FILEGROUPS + + RUNTIMES_HDRS_FILEGROUPS + + RUNTIMES_SRCS_FILEGROUPS + + RUNTIMES_TEXTUAL_SRCS_FILEGROUPS + ) +} + def _generate_runtimes_build_info_h_rule(ctx): h_file = ctx.actions.declare_file(ctx.label.name) ctx.actions.expand_template( @@ -157,19 +173,7 @@ def _generate_runtimes_build_info_h_rule(ctx): generate_runtimes_build_info_h = rule( implementation = _generate_runtimes_build_info_h_rule, - attrs = { - "_" + k: attr.label(default = v, allow_single_file = True) - for k, v in CRT_FILES.items() - } | { - "_" + _get_name(g): attr.label_list(default = [g], allow_files = True) - for g in ( - BUILTINS_SRCS_FILEGROUPS + - BUILTINS_TEXTUAL_SRCS_FILEGROUPS + - RUNTIMES_HDRS_FILEGROUPS + - RUNTIMES_SRCS_FILEGROUPS + - RUNTIMES_TEXTUAL_SRCS_FILEGROUPS - ) - } | { + attrs = _common_runtimes_rule_attrs | { "_template_file": attr.label( default = "runtimes_build_info.tpl.h", allow_single_file = True, @@ -197,3 +201,22 @@ def generate_runtimes_build_info_cc_library(name, deps = [], **kwargs): ] + deps, **kwargs ) + +def _generate_runtimes_build_vars_rule(ctx): + file = ctx.actions.declare_file(ctx.label.name) + ctx.actions.expand_template( + template = ctx.file._template_file, + output = file, + substitutions = _get_substitutions(ctx), + ) + return [DefaultInfo(files = depset([file]))] + +generate_runtimes_build_vars = rule( + implementation = _generate_runtimes_build_vars_rule, + attrs = _common_runtimes_rule_attrs | { + "_template_file": attr.label( + default = "runtimes_build_vars.tpl.bzl", + allow_single_file = True, + ), + }, +) diff --git a/toolchain/base/runtimes_build_vars.tpl.bzl b/toolchain/base/runtimes_build_vars.tpl.bzl new file mode 100644 index 000000000000..f94e0d34d406 --- /dev/null +++ b/toolchain/base/runtimes_build_vars.tpl.bzl @@ -0,0 +1,45 @@ +# 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 Carbon toolchain runtimes build info variables. + +This file is a template that is expanded into a starlark file for the installed +Carbon toolchain that provides a trivial textual definition of the relevant +build info in variables. +""" + +llvm_version_major = LLVM_VERSION_MAJOR + +crtbegin_src = CRTBEGIN_SRC +crtend_src = CRTEND_SRC + +crt_copts = [CRT_COPTS] + +builtins_aarch64_srcs = [BUILTINS_AARCH64_SRCS] +builtins_x86_64_srcs = [BUILTINS_X86_64_SRCS] +builtins_i386_srcs = [BUILTINS_I386_SRCS] + +builtins_aarch64_textual_srcs = [BUILTINS_AARCH64_TEXTUAL_SRCS] +builtins_x86_64_textual_srcs = [BUILTINS_X86_64_TEXTUAL_SRCS] +builtins_i386_textual_srcs = [BUILTINS_I386_TEXTUAL_SRCS] + +builtins_copts = [BUILTINS_COPTS] + +libcxx_hdrs = [LIBCXX_HDRS] +libcxx_linux_srcs = [LIBCXX_LINUX_SRCS] +libcxx_macos_srcs = [LIBCXX_MACOS_SRCS] +libcxx_win32_srcs = [LIBCXX_WIN32_SRCS] + +libc_internal_libcxx_hdrs = [LIBCXX_SHARED_HEADERS_HDRS] + +libcxxabi_hdrs = [LIBCXXABI_HDRS] +libcxxabi_srcs = [LIBCXXABI_SRCS] +libcxxabi_textual_srcs = [LIBCXXABI_TEXTUAL_SRCS] + +libcxx_copts = [LIBCXX_AND_ABI_COPTS] + +libunwind_hdrs = [LIBUNWIND_HDRS] +libunwind_srcs = [LIBUNWIND_SRCS] + +libunwind_copts = [LIBUNWIND_COPTS] diff --git a/toolchain/driver/clang_runtimes.cpp b/toolchain/driver/clang_runtimes.cpp index 3b4559e66d61..83e674dd6617 100644 --- a/toolchain/driver/clang_runtimes.cpp +++ b/toolchain/driver/clang_runtimes.cpp @@ -447,11 +447,6 @@ auto ClangResourceDirBuilder::CollectBuiltinsSrcFiles() // Only compile source files, not headers. llvm::erase_if(src_files, [](llvm::StringRef file) { return file.ends_with(".h"); }); - - // TODO: Remove this once we have a way of accessing `SipHash.h`. - llvm::erase_if(src_files, [](llvm::StringRef file) { - return file.ends_with("emupac.cpp"); - }); return src_files; } diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index 39c2461e0ef7..c4cbc7044045 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -15,6 +15,7 @@ load( "BUILTINS_SRCS_FILEGROUPS", "BUILTINS_TEXTUAL_SRCS_FILEGROUPS", "CRT_FILES", + "generate_runtimes_build_vars", ) load("configure_cmake_file.bzl", "configure_cmake_file") load("install_filegroups.bzl", "install_filegroup", "install_symlink", "install_target", "make_install_filegroups") @@ -305,6 +306,11 @@ filegroup( ], ) +# Generate a Starlark file with all the build variables needed for our runtimes. +generate_runtimes_build_vars( + name = "staging_bazel/runtimes_build_vars.bzl", +) + # Given a CMake-style install prefix[1], the hierarchy looks like: # # - prefix/bin: Binaries intended for direct use. @@ -350,7 +356,10 @@ install_dirs = { "lib/carbon/bazel": [ install_target("carbon_cc_toolchain_config.bzl", "bazel/carbon_cc_toolchain_config.bzl"), install_target("carbon_detected_variables.tpl.bzl", "bazel/carbon_detected_variables.tpl.bzl"), + install_target("carbon_runtimes.bzl", "bazel/carbon_runtimes.bzl"), install_target("carbon_toolchain.bzl", "bazel/carbon_toolchain.bzl"), + install_target("make_include_copts.bzl", "bazel/make_include_copts.bzl"), + install_target("runtimes_build_vars.bzl", "staging_bazel/runtimes_build_vars.bzl"), install_target("BUILD", "bazel/empty.BUILD"), ], "lib/carbon/llvm/bin": [install_symlink( @@ -367,6 +376,7 @@ install_dirs = { install_filegroup("libcxx", ":libcxx"), install_filegroup("libcxxabi", ":libcxxabi"), install_filegroup("libunwind", ":libunwind"), + install_target("BUILD", "bazel/runtimes.BUILD"), ], "lib/carbon/runtimes/libc": [ install_filegroup("internal", ":libc_internal"), @@ -379,6 +389,7 @@ install_dirs = { ), ], "lib/carbon/llvm/lib/clang/" + LLVM_VERSION_MAJOR: [ + install_target("BUILD", "bazel/clang_resource_dir.BUILD"), install_filegroup( "include", ":clang_headers", diff --git a/toolchain/install/bazel/carbon_cc_toolchain_config.bzl b/toolchain/install/bazel/carbon_cc_toolchain_config.bzl index e7d8ceeb00ec..51e1f0e5e8e6 100644 --- a/toolchain/install/bazel/carbon_cc_toolchain_config.bzl +++ b/toolchain/install/bazel/carbon_cc_toolchain_config.bzl @@ -9,20 +9,86 @@ load( "clang_include_dirs", "clang_sysroot", ) +load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") +load( + "@rules_cc//cc:cc_toolchain_config_lib.bzl", + "action_config", + "flag_group", + "flag_set", + "tool", +) load( "@rules_cc//cc:defs.bzl", "CcToolchainConfigInfo", "cc_toolchain", ) load("@rules_cc//cc/common:cc_common.bzl", "cc_common") +load("//bazel:runtimes_build_vars.bzl", "llvm_version_major") +load( + ":cc_toolchain_actions.bzl", + "all_c_compile_actions", + "all_cpp_compile_actions", + "all_link_actions", +) load(":cc_toolchain_features.bzl", "clang_cc_toolchain_features") load( ":cc_toolchain_tools.bzl", - "llvm_action_configs", "llvm_tool_paths", ) -def _impl(ctx): +def _make_action_configs(runtimes_path = None): + runtimes_flag = "--no-build-runtimes" + if runtimes_path: + runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path) + + return [ + action_config( + action_name = name, + enabled = True, + tools = [tool(path = "llvm/bin/clang")], + ) + for name in all_c_compile_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tool(path = "llvm/bin/clang++")], + ) + for name in all_cpp_compile_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tool(path = "carbon-busybox")], + flag_sets = [flag_set(flag_groups = [flag_group(flags = [ + runtimes_flag, + "link", + # We want to allow Bazel to intermingle linked object files and + # Clang-spelled link flags. The first `--` starts the list of + # initial object files by ending flags to the `link` subcommand, + # and the second `--` switches to Clang-spelled flags. + "--", + "--", + ])])], + ) + for name in all_link_actions + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tool(path = "llvm/bin/llvm-ar")], + ) + for name in [ACTION_NAMES.cpp_link_static_library] + ] + [ + action_config( + action_name = name, + enabled = True, + tools = [tool(path = "llvm/bin/llvm-strip")], + ) + for name in [ACTION_NAMES.strip] + ] + +def _carbon_cc_toolchain_config_impl(ctx): # Hard code the the repository-relative path of the LLVM (and Clang) # binaries as it is a fixed aspect of the install structure. llvm_bindir = "llvm/bin" @@ -32,14 +98,28 @@ def _impl(ctx): if clang_sysroot != "None" and clang_sysroot != "/": builtin_sysroot = clang_sysroot - identifier = "carbon-toolchain-{0}-{1}".format(ctx.attr.target_cpu, ctx.attr.target_os) + runtimes_path = None + if ctx.attr.runtimes: + for f in ctx.files.runtimes: + if f.basename == "runtimes_root": + runtimes_path = f.dirname + break + if not runtimes_path: + fail("Unable to compute the runtimes path for: {0}".format( + ctx.attr.runtimes, + )) + + identifier = "carbon-toolchain-{0}-{1}".format( + ctx.attr.target_cpu, + ctx.attr.target_os, + ) return cc_common.create_cc_toolchain_config_info( ctx = ctx, features = clang_cc_toolchain_features( target_os = ctx.attr.target_os, target_cpu = ctx.attr.target_cpu, ), - action_configs = llvm_action_configs(llvm_bindir), + action_configs = _make_action_configs(runtimes_path), cxx_builtin_include_directories = clang_include_dirs, builtin_sysroot = builtin_sysroot, @@ -56,56 +136,184 @@ def _impl(ctx): ) carbon_cc_toolchain_config = rule( - implementation = _impl, + implementation = _carbon_cc_toolchain_config_impl, attrs = { + "runtimes": attr.label(mandatory = False), "target_cpu": attr.string(mandatory = True), "target_os": attr.string(mandatory = True), }, provides = [CcToolchainConfigInfo], ) -def carbon_cc_toolchain_suite(name, configs): +def _runtimes_transition_impl(_, attr): + # Adjust the platform to the runtimes platform across the transition. + return {"//command_line_option:platforms": [str(attr.runtimes_platform)]} + +runtimes_transition = transition( + inputs = [], + outputs = ["//command_line_option:platforms"], + implementation = _runtimes_transition_impl, +) + +def _runtimes_filegroup_impl(ctx): + return [DefaultInfo(files = depset(ctx.files.srcs))] + +runtimes_filegroup = rule( + implementation = _runtimes_filegroup_impl, + attrs = { + # The platform to use when building the runtimes. + "runtimes_platform": attr.label(mandatory = True), + + # Mark that our dependencies are built through a transition. + "srcs": attr.label_list(mandatory = True, cfg = runtimes_transition), + + # Enable transitions in this rule. + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) + +def carbon_cc_toolchain_suite(name, platforms): """Create a toolchain suite that uses the local Clang/LLVM install. Args: name: The name of the toolchain suite to produce. - configs: An array of (os, cpu) pairs to support in the toolchain. + platforms: An array of (os, cpu) pairs to support in the toolchain. """ + # Our base filegroup for the toolchain just includes the busybox and the + # LLVM symlinks. Importantly, this _doesn't_ include the install digest that + # would cause cache misses as-if every file in the toolchain were an input + # to every action. native.filegroup( - name = name + "_files", - srcs = native.glob([ - "**", + name = name + "_base_files", + srcs = ["carbon-busybox", "carbon_install.txt"] + native.glob([ + "llvm/bin/*", ]), ) - # Create the individual local toolchains for each CPU. - for (os, cpu) in configs: - config_name = "{0}_{1}_{2}".format(name, os, cpu) + # We also need a compile-specific filegroup for use when _building_ + # runtimes. This needs to include the dedicated Clang headers, but we + # require the runtimes themselves to provide the relevant headers and + # dependencies. + native.filegroup( + name = name + "_runtimes_compile_files", + srcs = [ + ":" + name + "_base_files", + "//llvm/lib/clang/{0}:clang_hdrs".format(llvm_version_major), + ], + ) + + # We can build a single common compile filegroup regardless of CPU and OS. + # This needs to include all the headers of the runtimes, but those are + # architecture independent. + native.filegroup( + name = name + "_compile_files", + srcs = [ + ":" + name + "_runtimes_compile_files", + "//runtimes:libunwind_hdrs", + "//runtimes:libcxx_hdrs", + ], + ) + + # Create the actual toolchains for each OS and CPU. + for (os, cpu) in platforms: + platform_name = "{0}_{1}_{2}".format(name, os, cpu) + platform_constraints = [ + "@platforms//os:" + os, + "@platforms//cpu:" + cpu, + ] + + # First, configure a platform and toolchain for building runtimes. This + # toolchain will only have the Clang headers and no runtime libraries + # included. + native.platform( + name = platform_name + "_runtimes_platform", + constraint_values = [":is_runtimes_build"] + platform_constraints, + ) carbon_cc_toolchain_config( - name = config_name + "_config", + name = platform_name + "_runtimes_toolchain_config", target_cpu = cpu, target_os = os, ) cc_toolchain( - name = config_name + "_tools", - all_files = ":" + name + "_files", - ar_files = ":" + name + "_files", - as_files = ":" + name + "_files", - compiler_files = ":" + name + "_files", - dwp_files = ":" + name + "_files", - linker_files = ":" + name + "_files", - objcopy_files = ":" + name + "_files", - strip_files = ":" + name + "_files", - supports_param_files = 1, - toolchain_config = ":" + config_name + "_config", - toolchain_identifier = config_name, + name = platform_name + "_runtimes_cc_toolchain", + all_files = ":" + name + "_runtimes_compile_files", + ar_files = ":" + name + "_base_files", + as_files = ":" + name + "_runtimes_compile_files", + compiler_files = ":" + name + "_runtimes_compile_files", + dwp_files = ":" + name + "_base_files", + linker_files = ":" + name + "_base_files", + objcopy_files = ":" + name + "_base_files", + strip_files = ":" + name + "_base_files", + toolchain_config = ":" + platform_name + "_runtimes_toolchain_config", + toolchain_identifier = platform_name + "_runtimes", ) - compatible_with = ["@platforms//cpu:" + cpu, "@platforms//os:" + os] native.toolchain( - name = config_name, - exec_compatible_with = compatible_with, - target_compatible_with = compatible_with, - toolchain = config_name + "_tools", + name = platform_name + "_runtimes_toolchain", + exec_compatible_with = platform_constraints, + target_compatible_with = [":is_runtimes_build"] + platform_constraints, + toolchain = platform_name + "_runtimes_cc_toolchain", + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", + ) + + # Now we can use the runtimes platform to build the runtimes on-demand. + runtimes_filegroup( + name = platform_name + "_runtimes", + srcs = ["//runtimes:carbon_runtimes"], + runtimes_platform = ":" + platform_name + "_runtimes_platform", + ) + + # Finally, we can build the main platform and toolchain. + native.platform( + name = platform_name + "_platform", + constraint_values = platform_constraints, + ) + + # Build the main config with runtimes passed in. This allows the + # configuration to use these built runtimes where needed. + carbon_cc_toolchain_config( + name = platform_name + "_toolchain_config", + target_cpu = cpu, + target_os = os, + runtimes = ":" + platform_name + "_runtimes", + ) + + # We also include the runtimes in the linker files. We have to do this + # in addition to the config parameter as the config can't carry the + # actual dependency. + native.filegroup( + name = platform_name + "_linker_files", + srcs = [ + ":" + name + "_base_files", + ":" + platform_name + "_runtimes", + ], + ) + native.filegroup( + name = platform_name + "_all_files", + srcs = [ + ":" + name + "_compile_files", + ":" + platform_name + "_linker_files", + ], + ) + cc_toolchain( + name = platform_name + "_cc_toolchain", + all_files = ":" + platform_name + "_all_files", + ar_files = ":" + name + "_base_files", + as_files = ":" + name + "_compile_files", + compiler_files = ":" + name + "_compile_files", + dwp_files = ":" + platform_name + "_linker_files", + linker_files = ":" + platform_name + "_linker_files", + objcopy_files = ":" + name + "_base_files", + strip_files = ":" + name + "_base_files", + toolchain_config = ":" + platform_name + "_toolchain_config", + toolchain_identifier = platform_name, + ) + native.toolchain( + name = platform_name + "_toolchain", + exec_compatible_with = platform_constraints, + target_compatible_with = platform_constraints, + toolchain = platform_name + "_cc_toolchain", toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", ) diff --git a/toolchain/install/bazel/carbon_runtimes.bzl b/toolchain/install/bazel/carbon_runtimes.bzl new file mode 100644 index 000000000000..699dd966b3cd --- /dev/null +++ b/toolchain/install/bazel/carbon_runtimes.bzl @@ -0,0 +1,151 @@ +# 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 rules for building a Carbon runtimes tree. + +TODO: Currently, this produces a complete, static Carbon runtimes tree that +mirrors the exact style of runtimes tree the Carbon toolchain would build on its +own. However, it would be preferable to preserve the builtins, libc++, and +libunwind `cc_library` rules as "normal" library rules (if behind a transition) +and automatically depend on them. This would allow things like LTO and such to +include these. However, this requires support in `@rules_cc` for this kind of +dependency to be added. +""" + +load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") +load("@rules_cc//cc/common:cc_common.bzl", "cc_common") + +def _build_crt_file(ctx, cc_toolchain, feature_configuration, crt_file): + _, compilation_outputs = cc_common.compile( + name = ctx.label.name + ".compile_" + crt_file.basename, + actions = ctx.actions, + feature_configuration = feature_configuration, + cc_toolchain = cc_toolchain, + srcs = [crt_file], + user_compile_flags = ctx.attr.crt_copts, + ) + + # Extract the PIC object file and make sure we built one. + obj = compilation_outputs.pic_objects[0] + if not obj: + fail("The toolchain failed to produce a PIC object file. Ensure your " + + "toolchain supports PIC.") + + return obj + +def _removeprefix_or_fail(s, prefix): + new_s = s.removeprefix(prefix) + if new_s == s: + fail("Unable to remove prefix '{0}' from '{1}'".format(prefix, s)) + return new_s + +def _carbon_runtimes_impl(ctx): + outputs = [] + prefix = ctx.attr.name + + # Create a marker file in the runtimes root first. We'll use this to locate + # the runtimes for the toolchain. + root_out = ctx.actions.declare_file("{0}/runtimes_root".format(prefix)) + ctx.actions.write(output = root_out, content = "") + outputs.append(root_out) + + # Setup the C++ toolchain and configuration. We also force the `pic` feature + # to be enabled for these actions as we always want PIC generated code -- + # this avoids the need to build two versions of the runtimes and doesn't + # create problems with modern code generation when linking statically. This + # also simplifies extracting the outputs as we only need to look at + # `pic_objects`. + cc_toolchain = find_cpp_toolchain(ctx) + feature_configuration = cc_common.configure_features( + ctx = ctx, + cc_toolchain = cc_toolchain, + requested_features = ctx.features + ["pic"], + unsupported_features = ctx.disabled_features, + ) + + if ctx.attr.target_triple != "": + builtins_lib_path = "clang_resource_dir/lib/{0}".format(ctx.attr.target_triple) + else: + builtins_lib_path = "clang_resource_dir/lib" + + for filename, src in [ + ("crtbegin", ctx.files.crtbegin_src), + ("crtend", ctx.files.crtend_src), + ]: + if not src: + continue + src = src[0] + crt_obj = _build_crt_file(ctx, cc_toolchain, feature_configuration, src) + crt_out = ctx.actions.declare_file("{0}/{1}/clang_rt.{2}.o".format( + prefix, + builtins_lib_path, + filename, + )) + ctx.actions.symlink(output = crt_out, target_file = crt_obj) + outputs.append(crt_out) + + for runtime_dir, archive_name, archive in [ + (builtins_lib_path, "libclang_rt.builtins.a", ctx.files.builtins_archive[0]), + ("libcxx/lib", "libc++.a", ctx.files.libcxx_archive[0]), + ("libunwind/lib", "libunwind.a", ctx.files.libunwind_archive[0]), + ]: + runtime_out = ctx.actions.declare_file("{0}/{1}/{2}".format( + prefix, + runtime_dir, + archive_name, + )) + ctx.actions.symlink(output = runtime_out, target_file = archive) + outputs.append(runtime_out) + + for hdr in ctx.files.clang_hdrs: + # Incrementally remove prefixes of the paths to find the `include` + # directory we want to symlink into the output tree. + rel_path = hdr.path + if hdr.root.path != "": + rel_path = _removeprefix_or_fail(rel_path, hdr.root.path + "/") + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.workspace_root + "/") + rel_path = _removeprefix_or_fail(rel_path, hdr.owner.package + "/") + rel_path = _removeprefix_or_fail(rel_path, "include/") + + out_hdr = ctx.actions.declare_file( + "{0}/clang_resource_dir/include/{1}".format(prefix, rel_path), + ) + ctx.actions.symlink(output = out_hdr, target_file = hdr) + outputs.append(out_hdr) + + return [DefaultInfo(files = depset(outputs))] + +carbon_runtimes = rule( + implementation = _carbon_runtimes_impl, + attrs = { + "builtins_archive": attr.label(mandatory = True, allow_files = [".a"]), + "clang_hdrs": attr.label_list(mandatory = True, allow_files = True), + "crt_copts": attr.string_list(default = []), + "crtbegin_src": attr.label(allow_files = [".c"]), + "crtend_src": attr.label(allow_files = [".c"]), + "libcxx_archive": attr.label(mandatory = True, allow_files = [".a"]), + "libunwind_archive": attr.label(mandatory = True, allow_files = [".a"]), + "target_triple": attr.string(mandatory = True), + "_cc_toolchain": attr.label( + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + ), + }, + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + fragments = ["cpp"], + doc = """Builds a Carbon runtimes tree under the `name` directory. + + This rule works to replicate the behavior of the Carbon toolchain building a + runtimes tree directly in Bazel to allow it to fully benefit from Bazel's + orchestration, caching, and even remote execution. + + It produces a runtimes tree customized for the specific target platform, + similar to what the Carbon toolchain does on its own when invoked outside of + Bazel. + + The runtimes tree includes a complete Clang resource-dir, including CRT + begin/end objects, and the builtins library. It also includes a built libc++ + and libunwind library. These are arranged in the standard Carbon runtimes + layout so that they are correctly found by the toolchain. + """, +) diff --git a/toolchain/install/bazel/clang_resource_dir.BUILD b/toolchain/install/bazel/clang_resource_dir.BUILD new file mode 100644 index 000000000000..0808d06c75cd --- /dev/null +++ b/toolchain/install/bazel/clang_resource_dir.BUILD @@ -0,0 +1,10 @@ +# 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 + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "clang_hdrs", + srcs = glob(["include/**"]), +) diff --git a/toolchain/install/bazel/install.BUILD b/toolchain/install/bazel/install.BUILD index 1ec8c5cb549e..83eb108b88a4 100644 --- a/toolchain/install/bazel/install.BUILD +++ b/toolchain/install/bazel/install.BUILD @@ -6,9 +6,16 @@ load("//bazel:carbon_cc_toolchain_config.bzl", "carbon_cc_toolchain_suite") package(default_visibility = ["//visibility:public"]) +constraint_setting(name = "runtimes_build") + +constraint_value( + name = "is_runtimes_build", + constraint_setting = ":runtimes_build", +) + carbon_cc_toolchain_suite( - name = "carbon_cc_toolchain", - configs = [ + name = "carbon", + platforms = [ ("linux", "aarch64"), ("linux", "x86_64"), ("freebsd", "x86_64"), diff --git a/toolchain/install/bazel/make_include_copts.bzl b/toolchain/install/bazel/make_include_copts.bzl new file mode 100644 index 000000000000..30bc4721d385 --- /dev/null +++ b/toolchain/install/bazel/make_include_copts.bzl @@ -0,0 +1,25 @@ +# 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 + +"""Workaround for lack of easy way to add private includes. + +This is a workaround for the following Bazel issue: +https://github.com/bazelbuild/bazel/issues/2670 +""" + +def make_include_copts(include_dirs): + """Create `copts` to search the provided directories for includes.""" + copts = [] + prefix = "" + + repo_name = native.repository_name() + if repo_name != "@": + prefix = "external/{}/".format(repo_name[1:]) + + package_name = native.package_name() + for include_dir in include_dirs: + copts.append("-I{}{}/{}".format(prefix, package_name, include_dir)) + copts.append("-I$(GENDIR)/{}{}/{}".format(prefix, package_name, include_dir)) + + return copts diff --git a/toolchain/install/bazel/runtimes.BUILD b/toolchain/install/bazel/runtimes.BUILD new file mode 100644 index 000000000000..b71f2c5792d0 --- /dev/null +++ b/toolchain/install/bazel/runtimes.BUILD @@ -0,0 +1,196 @@ +# 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 + +load("@rules_cc//cc:defs.bzl", "cc_library") +load("//bazel:carbon_runtimes.bzl", "carbon_runtimes") +load("//bazel:make_include_copts.bzl", "make_include_copts") +load( + "//bazel:runtimes_build_vars.bzl", + "builtins_aarch64_srcs", + "builtins_aarch64_textual_srcs", + "builtins_copts", + "builtins_i386_srcs", + "builtins_i386_textual_srcs", + "builtins_x86_64_srcs", + "builtins_x86_64_textual_srcs", + "crt_copts", + "crtbegin_src", + "crtend_src", + "libc_internal_libcxx_hdrs", + "libcxx_copts", + "libcxx_hdrs", + "libcxx_linux_srcs", + "libcxx_macos_srcs", + "libcxx_win32_srcs", + "libcxxabi_hdrs", + "libcxxabi_srcs", + "libcxxabi_textual_srcs", + "libunwind_copts", + "libunwind_hdrs", + "libunwind_srcs", + "llvm_version_major", +) + +package(default_visibility = ["//visibility:public"]) + +# Clang looks in a target-triple subdirectory of its resource directory for the +# builtins and CRT files. Triples capture a large range of settings and aren't +# even consistent structurally between platforms, so we expect to have roughly +# one setting per supported target triple. By convention, we name them after the +# target triple. +# +# TODO: Add constraints and other settings so we can select the correct target +# triple for different `libc`s. +# +# TODO: Add other OS and CPU triples. +config_setting( + name = "x86_64-unknown-linux-gnu", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], +) + +config_setting( + name = "aarch64-unknown-linux-gnu", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:aarch64", + ], +) + +cc_library( + name = "builtins_internal", + hdrs_check = "strict", + textual_hdrs = select({ + "@platforms//cpu:aarch64": builtins_aarch64_textual_srcs, + "@platforms//cpu:i386": builtins_i386_textual_srcs, + "@platforms//cpu:x86_64": builtins_x86_64_textual_srcs, + "//conditions:default": [], + }), +) + +cc_library( + name = "builtins", + srcs = select({ + "@platforms//cpu:aarch64": builtins_aarch64_srcs, + "@platforms//cpu:i386": builtins_i386_srcs, + "@platforms//cpu:x86_64": builtins_x86_64_srcs, + "//conditions:default": [], + }), + copts = builtins_copts + make_include_copts([ + "builtins", + ]), + hdrs_check = "strict", + deps = [":builtins_internal"], +) + +filegroup( + name = "builtins_archive", + srcs = [":builtins"], + output_group = "archive", +) + +filegroup( + name = "libunwind_hdrs", + srcs = libunwind_hdrs, +) + +cc_library( + name = "libunwind", + srcs = libunwind_srcs, + hdrs = [":libunwind_hdrs"], + copts = libunwind_copts + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + ], + hdrs_check = "strict", + includes = ["libunwind/include"], + linkstatic = 1, +) + +filegroup( + name = "libunwind_archive", + srcs = [":libunwind"], + output_group = "archive", +) + +cc_library( + name = "libcxxabi_internal", + hdrs_check = "strict", + textual_hdrs = libcxxabi_textual_srcs, +) + +cc_library( + name = "libc_internal_libcxx", + hdrs = libc_internal_libcxx_hdrs, + hdrs_check = "strict", +) + +filegroup( + name = "libcxx_hdrs", + srcs = [ + "libcxx/include/__assertion_handler", + "libcxx/include/__config_site", + ] + libcxx_hdrs + libcxxabi_hdrs, +) + +cc_library( + name = "libcxx", + srcs = select({ + "@platforms//os:macos": libcxx_macos_srcs, + "@platforms//os:windows": libcxx_win32_srcs, + "//conditions:default": libcxx_linux_srcs, + }) + libcxxabi_srcs, + hdrs = [":libcxx_hdrs"], + copts = libcxx_copts + make_include_copts([ + "libcxx/src", + "libc/internal", + ]) + [ + # We disable all warnings as upstream isn't clean with the common + # warning flags Carbon uses by default. + "-w", + ], + hdrs_check = "strict", + includes = [ + "libcxx/include", + "libcxxabi/include", + ], + linkstatic = 1, + deps = [ + ":libc_internal_libcxx", + ":libcxxabi_internal", + ], +) + +filegroup( + name = "libcxx_archive", + srcs = [":libcxx"], + output_group = "archive", +) + +carbon_runtimes( + name = "carbon_runtimes", + builtins_archive = ":builtins_archive", + clang_hdrs = [ + "//llvm/lib/clang/{0}:clang_hdrs".format(llvm_version_major), + ], + crt_copts = crt_copts, + crtbegin_src = select({ + "@platforms//os:linux": crtbegin_src, + "//conditions:default": None, + }), + crtend_src = select({ + "@platforms//os:linux": crtend_src, + "//conditions:default": None, + }), + libcxx_archive = ":libcxx_archive", + libunwind_archive = ":libunwind_archive", + target_triple = select({ + ":aarch64-unknown-linux-gnu": "aarch64-unknown-linux-gnu", + ":x86_64-unknown-linux-gnu": "x86_64-unknown-linux-gnu", + "//conditions:default": "", + }), +)