To get hands-on with CROSSTOOL
, see the Configuring CROSSTOOL
tutorial.
CROSSTOOL
is a text file containing a protocol buffer that provides the necessary level of granularity for configuring the behavior of Bazel's C++ rules. By default, Bazel automatically configures CROSSTOOL
for your build, but you have the option to configure it manually. You reference the CROSSTOOL
file in your BUILD
file(s) using a cc_toolchain
target and check it into source control alongside your project. You can share a single CROSSTOOL
file across multiple projects or create separate per-project files.
When a C++ target enters the analysis phase, Bazel selects the appropriate cc_toolchain
target based on the BUILD file, then reads the corresponding toolchain definition from the CROSSTOOL
file. The cc_toolchain
target passes information from the CROSSTOOL
proto to the C++ target through a CcToolchainProvider
.
For example, a compile or link action, instantiated by a rule such as cc_binary
or cc_library
, needs the following information:
--copt/--linkopt
optionsAll of the above information except the artifacts required in the sandbox is specified in the CROSSTOOL
proto.
The artifacts to be shipped to the sandbox are declared in the cc_toolchain
target. For example, with the cc_toolchain.linker_files
attribute you can specify the linker binary and toolchain libraries to ship into the sandbox.
CROSSTOOL
proto structureThe CROSSTOOL
proto has the following structure:
Map from --cpu
to toolchain (to be used when --compiler
is not specified or when cc_toolchain_suite.toolchains
omits the cpu
entry)
Toolchain for a particular --cpu
and --compiler
combination (1)
compiler_flags
linker_flags
compilation_mode_flags
linking_mode_flags
features
Toolchain for a particular --cpu
and --compiler
combination (2)
Toolchain for a particular --cpu
and --compiler
combination (3)
...
The toolchain selection logic operates as follows:
User specifies a cc_toolchain_suite
target in the BUILD
file and points Bazel to the target using the --crosstool_top
option. The CROSSTOOL
file must reside in the same directory as the BUILD
file containing the cc_toolchain_suite
target.
The cc_toolchain_suite
target and the CROSSTOOL
file reference multiple toolchains. The values of the --cpu
and --compiler
flags determine which of those toolchains is selected, either based only on the --cpu
flag value, or based on a joint --cpu | --compiler
value. The selection process is as follows:
If the --compiler
option is specified, Bazel selects the corresponding entry from the cc_toolchain_suite.toolchains
attribute with --cpu | --compiler
. If Bazel does not find a corresponding entry, it throws an error.
If the --compiler
option is not specified, Bazel selects the corresponding entry from the cc_toolchain_suite.toolchains
attribute with just --cpu
.
However, if Bazel does not find a corresponding entry and the --incompatible_disable_cc_toolchain_label_from_crosstool_proto
option is disabled, Bazel iterates through default_toolchains
in the CROSSTOOL
file until it finds an entry where the default_toolchain.cpu
value matches the specified --cpu
option value. Bazel then reads the toolchain_identifier
value to identify the corresponding toolchain, and selects the appropriate entry in the cc_toolchain_suite.toolchains
attribute using toolchain.target_cpu | toolchain.compiler
.
If no flags are specified, Bazel inspects the host system and selects a --cpu
value based on its findings. See the inspection mechanism code.
Once a toolchain has been selected, corresponding feature
and action_config
messages in the CROSSTOOL
file govern the configuration of the build (that is, items described earlier in this document). These messages allow the implementation of fully fledged C++ features in Bazel without modifying the Bazel binary. C++ rules support multiple unique actions documented in detail in the Bazel source code.
CROSSTOOL
featuresA feature is an entity that requires command-line flags, actions, constraints on the execution environment, or dependency alterations. A feature can be something as simple as allowing BUILD files to select configurations of flags, such as treat_warnings_as_errors
, or interact with the C++ rules and include new compile actions and inputs to the compilation, such as header_modules
or thin_lto
.
Ideally, a toolchain definition consists of a set of features, where each feature consists of one or more flag groups, each defining a list of flags that apply to specific Bazel actions.
A feature is specified by name, which allows full decoupling of the CROSSTOOL
configuration from Bazel releases. In other words, a Bazel release does not affect the behavior of CROSSTOOL
configurations as long as those configurations do not require the use of new features.
A feature is enabled in one of the following ways:
enabled
field in the CROSSTOOL
file is set to true
.--feature
Bazel option or features
rule attribute.Features can have interdependencies, depend on command line flags, BUILD
file settings, and other variables.
Dependencies are typically managed directly with Bazel, which simply enforces the requirements and manages conflicts intrinsic to the nature of the features defined in the build. The toolchain specification allows for more granular constraints for use directly within the CROSSTOOL
file that govern feature support and expansion. These are:
CROSSTOOL
actionsCROSSTOOL
actions provide the flexibility to modify the circumstances under which an action executes without assuming how the action will be run. An action_config
specifies the tool binary that an action invokes, while a feature
specifies the configuration (flags) that determine how that tool behaves when the action is invoked.
Features reference CROSSTOOL
actions to signal which Bazel actions they affect since CROSSTOOL
actions can modify the Bazel action graph. The CROSSTOOL
file includes actions that have flags and tools associated with them, such as c++-compile
. Flags are assigned to each action by associating them with a feature.
Each CROSSTOOL
action name represents a single type of action performed by Bazel, such as compiling or linking. There is, however, a many-to-one relationship between CROSSTOOL
actions and Bazel action types, where a Bazel action type refers to a Java class that implements an action (such as CppCompileAction
). In particular, the “assembler actions” and “compiler actions” in the table below are CppCompileAction
, while the link actions are CppLinkAction
.
AR actions assemble object files into archive libraries (.a
files) via ar
and encode some semantics into the name.
CROSSTOOL
action_config
A CROSSTOOL
action_config
is a proto message that describes a Bazel action by specifying the tool (binary) to invoke during the action and sets of flags, defined by features, that apply constraints to the action's execution. A CROSSTOOL
action takes the following attributes:
A CROSSTOOL
action_config
can require and imply other features and action_configs as dictated by the feature relationships described earlier. This behavior is similar to that of a feature.
The last two attributes are redundant against the corresponding attributes on features and are included because some Bazel actions require certain flags or environment variables and we want to avoid unnecessary action_config
+feature
pairs. Typically, sharing a single feature across multiple action_config
s is preferred.
You can not define more than one CROSSTOOL
action_config
with the same action_name
within the same toolchain. This prevents ambiguity in tool paths and enforces the intention behind action_config
- that an action's properties are clearly described in a single place in the toolchain.
tool
messagesA CROSSTOOL
action_config
can specify a set of tools via tool
messages. A tool
message consists of the following fields:
For a given CROSSTOOL
action_config
, only a single tool
message applies its tool path and execution requirements to the Bazel action. A tool is selected by sequentially parsing tool
messages on an action_config
until a tool with a with_feature
set matching the feature configuration is found (see Feature relationships earlier in this document for more information). We recommend that you end your tool lists with a default tool that corresponds to an empty feature configuration.
Features and CROSSTOOL
actions can be used together to implement Bazel actions with diverse cross-platform semantics. For example, debug symbol generation on macOS requires generating symbols in the compile action, then invoking a specialized tool during the link action to create compressed dsym archive, and then decompressing that archive to produce the application bundle and .plist
files consumable by Xcode.
With Bazel, this process can instead be implemented as follows, with unbundle-debuginfo
being a Bazel action:
toolchain { action_config { config_name: "c++-link-executable" action_name: "c++-link-executable" tool { with_feature { feature: "generate-debug-symbols" } tool_path: "toolchain/mac/ld-with-dsym-packaging" } tool { tool_path: "toolchain/mac/ld" } } feature { name: "generate-debug-symbols" flag_set { action: "c-compile" action: "c++-compile" flag_group { flag: "-g" } } implies: "unbundle-debuginfo" } }
This same feature can be implemented entirely differently for Linux, which uses fission
, or for Windows, which produces .pdb
files. For example, the implementation for fission
-based debug symbol generation might look as follows:
toolchain { action_config { name: "c++-compile" tool { tool_path: "toolchain/bin/gcc" } } feature { name: "generate-debug-symbols" requires { feature: "dbg" } flag_set { action: "c++-compile" flag_group { flag: "-gsplit-dwarf" } } flag_set { action: "c++-link-executable" flag_group { flag: "-Wl" flag: "--gdb-index" } } } } }
CROSSTOOL
allows you to bundle flags into groups that serve a specific purpose. You can specify a flag within the CROSSTOOL
file using pre-defined variables within the flag value, which the compiler expands when adding the flag to the build command. For example:
flag_group { flag: "%{output_file_path}" }
In this case, the contents of the flag will be replaced by the output file path of the action.
Flag groups are expanded to the build command in the order in which they appear in the CROSSTOOL
file, top-to-bottom, left-to-right.
For flags that need to repeat with different values when added to the build command, the flag group can iterate variables of type list
. For example, the variable include_path
of type list
:
flag_group { iterate_over: "include_paths" flag: "-I%{include_paths}" }
expands to -I<path>
for each path element in the include_paths
list. All flags (or flag_group
s) in the body of a flag group declaration are expanded as a unit. For example:
flag_group { iterate_over: "include_paths" flag: "-I" flag: "%{include_paths}" }
expands to -I <path>
for each path element in the include_paths
list.
A variable can repeat multiple times. For example:
flag_group { iterate_over: "include_paths" flag: "-iprefix=%{include_paths}" flag: "-isystem=%{include_paths}" }
expands to:
-iprefix=<inc0> -isystem=<inc0> -iprefix=<inc1> -isystem=<inc1>
Variables can correspond to structures accessible using dot-notation. For example:
flag_group { flag: "-l%{libraries_to_link.name}" }
Structures can be nested and may also contain sequences. To prevent name clashes and to be explicit, you must specify the full path through the fields. For example:
flag_group { iterate_over: "libraries_to_link" flag_group { iterate_over: "libraries_to_link.shared_libraries" flag: "-l%{libraries_to_link.shared_libraries.name}" } }
Flag groups support conditional expansion based on the presence of a particular variable or its field using the expand_if_all_available
, expand_if_none_available
, expand_if_true
, expand_if_false
, or expand_if_equal
messages. For example:
flag_group { iterate_over: "libraries_to_link" flag_group { iterate_over: "libraries_to_link.shared_libraries" flag_group { expand_if_all_available: "libraries_to_link.shared_libraries.is_whole_archive" flag: "--whole_archive" } flag_group { flag: "-l%{libraries_to_link.shared_libraries.name}" } flag_group { expand_if_all_available: "libraries_to_link.shared_libraries.is_whole_archive" flag: "--no_whole_archive" } } }
Note: The --whole_archive
and --no_whole_archive
options are added to the build command only when a currently iterated library has an is_whole_archive
field.
CROSSTOOL
referenceThis section provides a reference of build variables, features, and other information required to successfully configure CROSSTOOL
.
CROSSTOOL
build variablesThe following is a reference of CROSSTOOL
build variables.
Note: The Action column indicates the relevant action type, if applicable.
The following is a reference of CROSSTOOL
features and their activation conditions.
This is a long list of features. The plan is to get rid of them once Crosstool in Starlark is done. For the curious reader see the implementation in CppActionConfigs, and for production toolchains consider adding no_legacy_features
to make the toolchain more standalone.