| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- # 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 cc_toolchain configuration rules for using the Carbon toolchain"""
- load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
- 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("//toolchain/runtimes:carbon_runtimes.bzl", "carbon_runtimes_build")
- load(
- "carbon_clang_variables.bzl",
- "clang_include_dirs",
- "clang_resource_dir",
- "clang_sysroot",
- )
- load(
- "cc_toolchain_actions.bzl",
- "all_c_compile_actions",
- "all_cpp_compile_actions",
- "all_link_actions",
- )
- load("cc_toolchain_carbon_project_features.bzl", "carbon_project_features")
- load("cc_toolchain_features.bzl", "clang_cc_toolchain_features")
- load(
- ":cc_toolchain_tools.bzl",
- "llvm_tool_paths",
- )
- def _make_action_configs(tools, 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 = [tools.clang],
- )
- for name in all_c_compile_actions
- ] + [
- action_config(
- action_name = name,
- enabled = True,
- tools = [tools.clangpp],
- )
- for name in all_cpp_compile_actions
- ] + [
- action_config(
- action_name = name,
- enabled = True,
- tools = [tools.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 = [tools.llvm_ar],
- )
- for name in [ACTION_NAMES.cpp_link_static_library]
- ] + [
- action_config(
- action_name = name,
- enabled = True,
- tools = [tools.llvm_strip],
- )
- for name in [ACTION_NAMES.strip]
- ]
- def _compute_clang_system_include_dirs():
- system_include_dirs_start_index = None
- for index, dir in enumerate(clang_include_dirs):
- # Skip over the include search directories until we find the resource
- # directory. The system include directories are everything after that.
- if dir.startswith(clang_resource_dir):
- system_include_dirs_start_index = index + 1
- break
- if not system_include_dirs_start_index:
- fail("Could not find the resource directory in the clang include " +
- "directories: {}".format(clang_include_dirs))
- return clang_include_dirs[system_include_dirs_start_index:]
- def _carbon_cc_toolchain_config_impl(ctx):
- llvm_bindir = "llvm/bin"
- clang_bindir = llvm_bindir
- tools = struct(
- carbon_busybox = tool(path = "carbon-busybox"),
- clang = tool(path = clang_bindir + "/clang"),
- clangpp = tool(path = clang_bindir + "/clang++"),
- llvm_ar = tool(path = llvm_bindir + "/llvm-ar"),
- llvm_strip = tool(path = llvm_bindir + "/llvm-strip"),
- )
- if ctx.attr.bins:
- carbon_busybox = None
- clang = None
- clangpp = None
- llvm_ar = None
- llvm_strip = None
- for f in ctx.files.bins:
- if f.basename == "carbon-busybox":
- carbon_busybox = f
- elif f.basename == "clang":
- clang = f
- elif f.basename == "clang++":
- clangpp = f
- elif f.basename == "llvm-ar":
- llvm_ar = f
- elif f.basename == "llvm-strip":
- llvm_strip = f
- if not all([carbon_busybox, clang, clangpp, llvm_ar, llvm_strip]):
- fail("Missing required tool in bins: {0}".format(ctx.attr.bins))
- llvm_bindir = llvm_ar.dirname
- clang_bindir = clang.dirname
- tools = struct(
- carbon_busybox = tool(tool = carbon_busybox),
- clang = tool(tool = clang),
- clangpp = tool(tool = clangpp),
- llvm_ar = tool(tool = llvm_ar),
- llvm_strip = tool(tool = llvm_strip),
- )
- # Only use a sysroot if a non-trivial one is set in Carbon's config.
- builtin_sysroot = None
- sysroot_include_search = []
- if clang_sysroot != "None" and clang_sysroot != "/":
- builtin_sysroot = clang_sysroot
- sysroot_include_search = ["%sysroot%/usr/include"]
- 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 = "{0}_toolchain_{1}_{2}".format(
- ctx.attr.identifier_prefix,
- 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,
- # TODO: This should be configured externally rather than here so
- # that the install Carbon toolchain doesn't automatically include
- # Carbon-project-specific flags. However, that is especially awkward
- # to do until we fully migrate to a rules-based toolchain, and the
- # project-specific flags are largely harmless at the moment. We also
- # omit a meaningful cache key as when using the Carbon toolchain we
- # don't need it as it is a hermetic part of Bazel.
- project_features = carbon_project_features(cache_key = ""),
- ),
- action_configs = _make_action_configs(tools, runtimes_path),
- cxx_builtin_include_directories = [
- "runtimes/libunwind/include",
- "runtimes/libcxx/include",
- "runtimes/libcxxabi/include",
- "{}/include".format(clang_resource_dir),
- "runtimes/clang_resource_dir/include",
- ] + _compute_clang_system_include_dirs() + sysroot_include_search,
- builtin_sysroot = builtin_sysroot,
- # This configuration only supports local non-cross builds so derive
- # everything from the target CPU selected.
- toolchain_identifier = identifier,
- # This is used to expose a "flag" that `config_setting` rules can use to
- # determine if the compiler is Clang.
- compiler = "clang",
- # Pass in our tool paths to expose Make variables like $(NM) and
- # $(OBJCOPY).
- tool_paths = llvm_tool_paths(llvm_bindir, clang_bindir),
- )
- carbon_cc_toolchain_config = rule(
- implementation = _carbon_cc_toolchain_config_impl,
- attrs = {
- "bins": attr.label(mandatory = False),
- "identifier_prefix": attr.string(mandatory = True),
- "runtimes": attr.label(mandatory = False),
- "target_cpu": attr.string(mandatory = True),
- "target_os": attr.string(mandatory = True),
- },
- provides = [CcToolchainConfigInfo],
- )
- def _transition_with_stage_impl(_, attr):
- return {
- "//:bootstrap_stage": attr.stage,
- "//:runtimes_build": attr.enable_runtimes_build,
- }
- _transition_with_stage = transition(
- inputs = [],
- outputs = [
- "//:bootstrap_stage",
- "//:runtimes_build",
- ],
- implementation = _transition_with_stage_impl,
- )
- def _filegroup_with_stage_impl(ctx):
- return [DefaultInfo(files = depset(ctx.files.srcs))]
- filegroup_with_stage = rule(
- implementation = _filegroup_with_stage_impl,
- attrs = {
- # Whether to enable runtimes building for the sources of this filegroup.
- "enable_runtimes_build": attr.bool(default = False),
- # Mark that our dependencies are built through a transition.
- "srcs": attr.label_list(mandatory = True, cfg = _transition_with_stage),
- # The bootstrap stage that the sources of this filegroup should be built
- # with.
- "stage": attr.int(mandatory = True),
- # Enable transitions in this rule.
- "_allowlist_function_transition": attr.label(
- default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
- ),
- },
- doc = """
- A filegroup whose sources are built using a specific toolchain stage, and
- which provides an interface to build those sources with or without enabling
- runtimes building.
- """,
- )
- def _exec_filegroup_impl(ctx):
- return [DefaultInfo(files = depset(ctx.files.srcs))]
- _exec_filegroup = rule(
- implementation = _exec_filegroup_impl,
- attrs = {
- "srcs": attr.label_list(cfg = "exec"),
- },
- )
- def filegroup_with_stage_and_exec(name, srcs, stage, tags = []):
- """Wraps `filegroup_with_stage` with a conditional `exec` config transition.
- When `//:bootstrap_exec_config` is disabled, this works exactly like
- `filegroup_with_stage`. But when it is _enabled_, it also adds an `exec`
- config transition. This allows bootstrapping for a target that is not exec
- compatible with the host, and in general makes bootstrapping more robust at
- the expense of a likely duplicate build of the entire toolchain.
- """
- filegroup_with_stage(
- name = name + "_stage_only",
- srcs = srcs,
- stage = stage,
- tags = tags,
- )
- _exec_filegroup(
- name = name + "_with_exec",
- srcs = [":" + name + "_stage_only"],
- tags = tags,
- )
- native.alias(
- name = name,
- actual = select({
- "//:bootstrap_with_exec_config": ":" + name + "_with_exec",
- "//conditions:default": ":" + name + "_stage_only",
- }),
- tags = tags,
- )
- def _gen_cc_toolchain_paths_impl(ctx):
- cc_toolchain = find_cpp_toolchain(ctx)
- expanded_vars = [
- ctx.expand_make_variables("vars", v, {})
- for v in ctx.attr.vars
- ]
- out = ctx.actions.declare_file(ctx.attr.name + ".txt")
- ctx.actions.write(out, "\n".join(expanded_vars) + "\n")
- # Include all toolchain files in runfiles.
- runfiles = ctx.runfiles(files = [out]).merge(
- ctx.runfiles(transitive_files = cc_toolchain.all_files),
- )
- return [DefaultInfo(files = depset([out]), runfiles = runfiles)]
- gen_cc_toolchain_paths_with_stage = rule(
- implementation = _gen_cc_toolchain_paths_impl,
- attrs = {
- "enable_runtimes_build": attr.bool(default = False),
- "stage": attr.int(mandatory = True),
- "vars": attr.string_list(
- default = ["$(CC)", "$(AR)", "$(NM)", "$(OBJCOPY)", "$(STRIP)"],
- ),
- "_allowlist_function_transition": attr.label(
- default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
- ),
- "_cc_toolchain": attr.label(
- default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
- ),
- },
- toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
- cfg = _transition_with_stage,
- )
- def carbon_cc_toolchain_suite(
- name,
- all_hdrs,
- base_files,
- clang_hdrs,
- platforms,
- runtimes_cfg,
- build_stage = 1,
- base_stage = 0,
- tags = []):
- """Create a Carbon `cc_toolchain` for the current target platform.
- This provides the final toolchain for Carbon, but also all of the
- infrastructure for supporting on-demand built runtimes in this toolchain.
- There is also support for bootstrapping, where one `build_stage` toolchain
- builds on top of another `base_stage`.
- Args:
- name:
- The name of the toolchain suite to produce, used as the base of the
- names of each component of the toolchain suite.
- all_hdrs: A list of header files to include in the toolchain.
- base_files: A list of files to include in the toolchain.
- build_stage: The stage to use for the build files.
- base_stage: The stage to use for the base files.
- clang_hdrs: A list of header files to include in the toolchain.
- platforms: An array of (os, cpu) pairs to support in the toolchain.
- runtimes_cfg: The runtimes configuration to use in the toolchain.
- tags: Tags to apply to the toolchain.
- """
- # First, declare file groups that are explicitly built using the base stage,
- # and not in the runtimes build. These allow us to form the inputs to both
- # the runtimes toolchain and the main toolchain of this stage that are built
- # entirely by the base stage toolchain.
- filegroup_with_stage_and_exec(
- name = "{}_clang_hdrs".format(name),
- srcs = clang_hdrs,
- stage = base_stage,
- tags = tags,
- )
- filegroup_with_stage_and_exec(
- name = "{}_base_files".format(name),
- srcs = base_files,
- stage = base_stage,
- tags = tags,
- )
- filegroup_with_stage_and_exec(
- name = "{}_runtimes_compile_files".format(name),
- srcs = [
- ":{}_base_files".format(name),
- ":{}_clang_hdrs".format(name),
- ],
- stage = base_stage,
- tags = tags,
- )
- filegroup_with_stage_and_exec(
- name = "{}_compile_files".format(name),
- srcs = [":{}_base_files".format(name)] + all_hdrs,
- stage = base_stage,
- tags = tags,
- )
- # Now build a configuration and toolchain that is configured to work
- # _without_ runtimes, and be used to _build_ the runtimes on-demand.
- carbon_cc_toolchain_config(
- name = "{}_runtimes_toolchain_config".format(name),
- identifier_prefix = "{}_runtimes".format(name),
- target_cpu = select({
- # Note that we need to select on both OS and CPU so that we end up
- # spelling the CPU in the correct OS-specific ways.
- ":is_{}_{}".format(os, cpu): cpu
- for os, cpus in platforms.items()
- for cpu in cpus
- }),
- target_os = select({
- "@platforms//os:{}".format(os): os
- for os in platforms.keys()
- }),
- bins = ":{}_base_files".format(name),
- tags = tags,
- )
- cc_toolchain(
- name = "{}_runtimes_cc_toolchain".format(name),
- all_files = ":{}_runtimes_compile_files".format(name),
- ar_files = ":{}_base_files".format(name),
- as_files = ":{}_runtimes_compile_files".format(name),
- compiler_files = ":{}_runtimes_compile_files".format(name),
- dwp_files = ":{}_base_files".format(name),
- linker_files = ":{}_base_files".format(name),
- objcopy_files = ":{}_base_files".format(name),
- strip_files = ":{}_base_files".format(name),
- toolchain_config = ":{}_runtimes_toolchain_config".format(name),
- toolchain_identifier = select({
- ":is_{}_{}".format(os, cpu): "{}_{}_{}_runtimes_toolchain".format(name, os, cpu)
- for os, cpus in platforms.items()
- for cpu in cpus
- }),
- tags = tags,
- )
- native.toolchain(
- name = "{}_runtimes_toolchain".format(name),
- target_settings = [
- ":is_bootstrap_stage_{}".format(build_stage),
- ":is_runtimes_build",
- ],
- use_target_platform_constraints = True,
- toolchain = ":{}_runtimes_cc_toolchain".format(name),
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
- tags = tags,
- )
- # Now that we have a toolchain for building runtimes, actually do the build
- # here using the runtimes config provided to us. This is important to do
- # here because we need each runtimes build for a particular bootstrapping
- # stage of the toolchain to be distinct.
- carbon_runtimes_build(
- name = "{}_runtimes_build".format(name),
- config = runtimes_cfg,
- clang_hdrs = [":{}_clang_hdrs".format(name)],
- tags = tags,
- )
- # Wrap the built runtimes for this stage in a filegroup that ensures they
- # are built at this stage, but with the runtimes build enabled. This will
- # select the runtimes build toolchain above that doesn't yet provide any
- # runtimes, avoiding a cycle when the main toolchain below depends on these
- # runtimes.
- filegroup_with_stage(
- name = "{}_runtimes".format(name),
- enable_runtimes_build = True,
- srcs = ["{}_runtimes_build".format(name)],
- stage = build_stage,
- tags = tags,
- )
- # Now we can build the main toolchain configuration, filegroups including
- # the on-demand built runtimes, and the final tolochain itself.
- carbon_cc_toolchain_config(
- name = "{}_toolchain_config".format(name),
- identifier_prefix = name,
- target_cpu = select({
- # Note that we need to select on both OS and CPU so that we end up
- # spelling the CPU in the correct OS-specific ways.
- ":is_{}_{}".format(os, cpu): cpu
- for os, cpus in platforms.items()
- for cpu in cpus
- }),
- target_os = select({
- "@platforms//os:{}".format(os): os
- for os in platforms.keys()
- }),
- runtimes = ":{}_runtimes".format(name),
- bins = ":{}_base_files".format(name),
- tags = tags,
- )
- native.filegroup(
- name = "{}_linker_files".format(name),
- srcs = [
- ":{}_base_files".format(name),
- ":{}_runtimes".format(name),
- ],
- tags = tags,
- )
- native.filegroup(
- name = "{}_all_files".format(name),
- srcs = [
- ":{}_compile_files".format(name),
- ":{}_linker_files".format(name),
- ],
- tags = tags,
- )
- cc_toolchain(
- name = "{}_cc_toolchain".format(name),
- all_files = ":{}_all_files".format(name),
- ar_files = ":" + name + "_base_files",
- as_files = ":" + name + "_compile_files",
- compiler_files = ":" + name + "_compile_files",
- dwp_files = ":" + name + "_linker_files",
- linker_files = ":" + name + "_linker_files",
- objcopy_files = ":" + name + "_base_files",
- strip_files = ":" + name + "_base_files",
- toolchain_config = ":" + name + "_toolchain_config",
- toolchain_identifier = select({
- ":is_{}_{}".format(os, cpu): "{}_{}_{}_toolchain".format(name, os, cpu)
- for os, cpus in platforms.items()
- for cpu in cpus
- }),
- tags = tags,
- )
- native.toolchain(
- name = name + "_toolchain",
- target_settings = [":is_bootstrap_stage_{}".format(build_stage), ":not_runtimes_build"],
- use_target_platform_constraints = True,
- toolchain = ":" + name + "_cc_toolchain",
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
- tags = tags,
- )
|