Project: /_project.yaml Book: /_book.yaml
{% include “_buttons.html” %}
Bazel has sophisticated support for modeling platforms and toolchains for multi-architecture and cross-compiled builds.
This page summarizes the state of this support.
Key Point: Bazel's platform and toolchain APIs are available today. Not all languages support them. Use these APIs with your project if you can. Bazel is migrating all major languages so eventually all builds will be platform-based.
See also:
C++ rules use platforms to select toolchains when --incompatible_enable_cc_toolchain_resolution
is set.
This means you can configure a C++ project with:
bazel build //:my_cpp_project --platforms=//:myplatform
instead of the legacy:
bazel build //:my_cpp_project` --cpu=... --crosstool_top=... --compiler=...
This will be enabled by default in Bazel 7.0 (#7260{: .external}).
To test your C++ project with platforms, see Migrating Your Project and Configuring C++ toolchains.
Java rules use platforms to select toolchains.
This replaces legacy flags --java_toolchain
, --host_java_toolchain
, --javabase
, and --host_javabase
.
See Java and Bazel for details.
Android rules use platforms to select toolchains when --incompatible_enable_android_toolchain_resolution
is set.
This means you can configure an Android project with:
bazel build //:my_android_project --android_platforms=//:my_android_platform
instead of with legacy flags like --android_crosstool_top
, --android_cpu
, and --fat_apk_cpu
.
This will be enabled by default in Bazel 7.0 (#16285{: .external}).
To test your Android project with platforms, see Migrating Your Project.
Apple rules{: .external} do not support platforms and are not yet scheduled for support.
You can still use platform APIs with Apple builds (for example, when building with a mixture of Apple rules and pure C++) with platform mappings.
If you own a language rule set, see Migrating your rule set for adding support.
Platforms and toolchains were introduced to standardize how software projects target different architectures and cross-compile.
This was inspired{: .external} by the observation that language maintainers were already doing this in ad hoc, incompatible ways. For example, C++ rules used --cpu
and --crosstool_top
to declare a target CPU and toolchain. Neither of these correctly models a “platform”. This produced awkward and incorrect builds.
Java, Android, and other languages evolved their own flags for similar purposes, none of which interoperated with each other. This made cross-language builds confusing and complicated.
Bazel is intended for large, multi-language, multi-platform projects. This demands more principled support for these concepts, including a clear standard API.
Upgrading to the new API requires two efforts: releasing the API and upgrading rule logic to use it.
The first is done but the second is ongoing. This consists of ensuring language-specific platforms and toolchains are defined, language logic reads toolchains through the new API instead of old flags like --crosstool_top
, and config_setting
s select on the new API instead of old flags.
This work is straightforward but requires a distinct effort for each language, plus fair warning for project owners to test against upcoming changes.
This is why this is an ongoing migration.
This migration is complete when all projects build with the form:
bazel build //:myproject --platforms=//:myplatform
This implies:
//:myplatform
.//:myplatform
.//:myplatform
references common declarations{: .external} of CPU
, OS
, and other generic, language-independent propertiesselect()
s properly match //:myplatform
.//:myplatform
is defined in a clear, accessible place: in your project's repo if the platform is unique to your project, or some common place all consuming projects can find itOld flags like --cpu
, --crosstool_top
, and --fat_apk_cpu
will be deprecated and removed as soon as it's safe to do so.
Ultimately, this will be the sole way to configure architectures.
If you build with languages that support platforms, your build should already work with an invocation like:
bazel build //:myproject --platforms=//:myplatform
See Status and your language's documentation for precise details.
If a language requires a flag to enable platform support, you also need to set that flag. See Status for details.
For your project to build, you need to check the following:
//:myplatform
must exist. It‘s generally the project owner’s responsibility to define platforms because different projects target different machines. See Default platforms.
The toolchains you want to use must exist. If using stock toolchains, the language owners should include instructions for how to register them. If writing your own custom toolchains, you need to register them in your MODULE.bazel
file or with --extra_toolchains
.
select()
s and configuration transitions must resolve properly. See select() and Transitions.
If your build mixes languages that do and don't support platforms, you may need platform mappings to help the legacy languages work with the new API. See Platform mappings for details.
If you still have problems, reach out for support.
Project owners should define explicit platforms to describe the architectures they want to build for. These are then triggered with --platforms
.
When --platforms
isn‘t set, Bazel defaults to a platform
representing the local build machine. This is auto-generated at @local_config_platform//:host
so there’s no need to explicitly define it. It maps the local machine's OS
and CPU
with constraint_value
s declared in @platforms
{: .external}.
select()
{:#select}Projects can select()
on constraint_value
targets but not complete platforms. This is intentional so select()
supports as wide a variety of machines as possible. A library with ARM
-specific sources should support all ARM
-powered machines unless there's reason to be more specific.
To select on one or more constraint_value
s, use:
config_setting( name = "is_arm", constraint_values = [ "@platforms//cpu:arm", ], )
This is equivalent to traditionally selecting on --cpu
:
config_setting( name = "is_arm", values = { "cpu": "arm", }, )
More details here.
select
s on --cpu
, --crosstool_top
, etc. don't understand --platforms
. When migrating your project to platforms, you must either convert them to constraint_values
or use platform mappings to support both styles during migration.
Starlark transitions change flags down parts of your build graph. If your project uses a transition that sets --cpu
, --crossstool_top
, or other legacy flags, rules that read --platforms
won't see these changes.
When migrating your project to platforms, you must either convert changes like return { "//command_line_option:cpu": "arm" }
to return { "//command_line_option:platforms": "//:my_arm_platform" }
or use platform mappings to support both styles during migration. window.
If you own a rule set and want to support platforms, you need to:
Have rule logic resolve toolchains with the toolchain API. See toolchain API (ctx.toolchains
).
Optional: define an --incompatible_enable_platforms_for_my_language
flag so rule logic alternately resolves toolchains through the new API or old flags like --crosstool_top
during migration testing.
Define the relevant properties that make up platform components. See Common platform properties
Define standard toolchains and make them accessible to users through your rule's registration instructions (details)
Ensure select()
s and configuration transitions support platforms. This is the biggest challenge. It‘s particularly challenging for multi-language projects (which may fail if all languages can’t read --platforms
).
If you need to mix with rules that don't support platforms, you may need platform mappings to bridge the gap.
Common, cross-language platform properties like OS
and CPU
should be declared in @platforms
{: .external}. This encourages sharing, standardization, and cross-language compatibility.
Properties unique to your rules should be declared in your rule's repo. This lets you maintain clear ownership over the specific concepts your rules are responsible for.
If your rules use custom-purpose OSes or CPUs, these should be declared in your rule's repo vs. @platforms
{: .external}.
Platform mappings is a temporary API that lets platform-aware logic mix with legacy logic in the same build. This is a blunt tool that's only intended to smooth incompatibilities with different migration timeframes.
Caution: Only use this if necessary, and expect to eventually eliminate it.
A platform mapping is a map of either a platform()
to a corresponding set of legacy flags or the reverse. For example:
platforms: # Maps "--platforms=//platforms:ios" to "--cpu=ios_x86_64 --apple_platform_type=ios". //platforms:ios --cpu=ios_x86_64 --apple_platform_type=ios flags: # Maps "--cpu=ios_x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios". --cpu=ios_x86_64 --apple_platform_type=ios //platforms:ios # Maps "--cpu=darwin_x86_64 --apple_platform_type=macos" to "//platform:macos". --cpu=darwin_x86_64 --apple_platform_type=macos //platforms:macos
Bazel uses this to guarantee all settings, both platform-based and legacy, are consistently applied throughout the build, including through transitions.
By default Bazel reads mappings from the platform_mappings
file in your workspace root. You can also set --platform_mappings=//:my_custom_mapping
.
See the platform mappings design{: .external} for details.
A platform
is a collection of constraint_value
targets:
platform( name = "myplatform", constraint_values = [ "@platforms//os:linux", "@platforms//cpu:arm", ], )
A constraint_value
is a machine property. Values of the same “kind” are grouped under a common constraint_setting
:
constraint_setting(name = "os") constraint_value( name = "linux", constraint_setting = ":os", ) constraint_value( name = "mac", constraint_setting = ":os", )
A toolchain
is a Starlark rule. Its attributes declare a language's tools (like compiler = "//mytoolchain:custom_gcc"
). Its providers pass this information to rules that need to build with these tools.
Toolchains declare the constraint_value
s of machines they can target (target_compatible_with = ["@platforms//os:linux"]
) and machines their tools can run on (exec_compatible_with = ["@platforms//os:mac"]
).
When building $ bazel build //:myproject --platforms=//:myplatform
, Bazel automatically selects a toolchain that can run on the build machine and build binaries for //:myplatform
. This is known as toolchain resolution.
The set of available toolchains can be registered in the MODULE.bazel
file with register_toolchains
or at the command line with --extra_toolchains
.
For more information see here.
For general support and questions about the migration timeline, contact bazel-discuss{: .external} or the owners of the appropriate rules.
For discussions on the design and evolution of the platform/toolchain APIs, contact bazel-dev{: .external}.