| --- | |
| title: 'Migrating to Platforms' | |
| --- | |
| Bazel has sophisticated [support](#background) for modeling | |
| [platforms][Platforms] and [toolchains][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: | |
| * [Platforms][Platforms] | |
| * [Toolchains][Toolchains] | |
| * [Background][Background] | |
| ## Status {#status} | |
| ### C++ {#cxx} | |
| C++ rules use platforms to select toolchains when | |
| `--incompatible_enable_cc_toolchain_resolution` is set. | |
| This means you can configure a C++ project with: | |
| ```posix-terminal | |
| bazel build //:my_cpp_project --platforms=//:myplatform | |
| ``` | |
| instead of the legacy: | |
| ```posix-terminal | |
| bazel build //:my_cpp_project` --cpu=... --crosstool_top=... --compiler=... | |
| ``` | |
| This will be enabled by default in Bazel 7.0 ([#7260](https://github.com/bazelbuild/bazel/issues/7260)). | |
| To test your C++ project with platforms, see | |
| [Migrating Your Project](#migrating-your-project) and | |
| [Configuring C++ toolchains]. | |
| ### Java {#java} | |
| Java rules use platforms to select toolchains. | |
| This replaces legacy flags `--java_toolchain`, `--host_java_toolchain`, | |
| `--javabase`, and `--host_javabase`. | |
| See [Java and Bazel](/versions/9.2.0/docs/bazel-and-java) for details. | |
| ### Android {#android} | |
| Android rules use platforms to select toolchains when | |
| `--incompatible_enable_android_toolchain_resolution` is set. | |
| This means you can configure an Android project with: | |
| ```posix-terminal | |
| 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](https://github.com/bazelbuild/bazel/issues/16285)). | |
| To test your Android project with platforms, see | |
| [Migrating Your Project](#migrating-your-project). | |
| ### Apple {#apple} | |
| [Apple rules] 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](#platform-mappings). | |
| ### Other languages {#other-languages} | |
| * [Go rules] fully support platforms | |
| * [Rust rules] fully support platforms. | |
| If you own a language rule set, see [Migrating your rule set] for adding | |
| support. | |
| ## Background {#background} | |
| *Platforms* and *toolchains* were introduced to standardize how software | |
| projects target different architectures and cross-compile. | |
| This was | |
| [inspired][Inspiration] | |
| 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. | |
| ### Need for migration {#migration} | |
| 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. | |
| ### Goal {#goal} | |
| This migration is complete when all projects build with the form: | |
| ```posix-terminal | |
| bazel build //:myproject --platforms=//:myplatform | |
| ``` | |
| This implies: | |
| 1. Your project's rules choose the right toolchains for `//:myplatform`. | |
| 1. Your project's dependencies choose the right toolchains for `//:myplatform`. | |
| 1. `//:myplatform` references | |
| [common declarations][Common Platform Declarations] | |
| of `CPU`, `OS`, and other generic, language-independent properties | |
| 1. All relevant [`select()`s][select()] properly match `//:myplatform`. | |
| 1. `//: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 it | |
| Old 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. | |
| ## Migrating your project {#migrating-your-project} | |
| If you build with languages that support platforms, your build should already | |
| work with an invocation like: | |
| ```posix-terminal | |
| bazel build //:myproject --platforms=//:myplatform | |
| ``` | |
| See [Status](#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](#status) for details. | |
| For your project to build, you need to check the following: | |
| 1. `//:myplatform` must exist. It's generally the project owner's responsibility | |
| to define platforms because different projects target different machines. | |
| See [Default platforms](#default-platforms). | |
| 1. 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](https://bazel.build/versions/9.2.0/extending/toolchains#registering-building-toolchains) them in your | |
| `MODULE.bazel` file or with [`--extra_toolchains`](https://bazel.build/versions/9.2.0/reference/command-line-reference#flag--extra_toolchains). | |
| 1. `select()`s and [configuration transitions][Starlark transitions] must | |
| resolve properly. See [select()](#select) and [Transitions](#transitions). | |
| 1. 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](#platform-mappings) for details. | |
| If you still have problems, [reach out](#questions) for support. | |
| ### Default platforms {#default-platforms} | |
| Project owners should define explicit | |
| [platforms][Defining Constraints and 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 `@platforms//host` (aliased as | |
| `@bazel_tools//tools:host_platform`) | |
| 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`](https://github.com/bazelbuild/platforms). | |
| ### `select()` {#select} | |
| Projects can [`select()`][select()] on | |
| [`constraint_value` targets][constraint_value Rule] 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: | |
| ```python | |
| config_setting( | |
| name = "is_arm", | |
| constraint_values = [ | |
| "@platforms//cpu:arm", | |
| ], | |
| ) | |
| ``` | |
| This is equivalent to traditionally selecting on `--cpu`: | |
| ```python | |
| config_setting( | |
| name = "is_arm", | |
| values = { | |
| "cpu": "arm", | |
| }, | |
| ) | |
| ``` | |
| More details [here][select() Platforms]. | |
| `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](#platform-mappings) to support | |
| both styles during migration. | |
| ### Transitions {#transitions} | |
| [Starlark transitions][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](#platform-mappings) to support both styles during migration. | |
| window. | |
| ## Migrating your rule set {#migrating-your-rule-set} | |
| If you own a rule set and want to support platforms, you need to: | |
| 1. Have rule logic resolve toolchains with the toolchain API. See | |
| [toolchain API][Toolchains] (`ctx.toolchains`). | |
| 1. 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. | |
| 1. Define the relevant properties that make up platform components. See | |
| [Common platform properties](#common-platform-properties) | |
| 1. Define standard toolchains and make them accessible to users through your | |
| rule's registration instructions ([details](https://bazel.build/versions/9.2.0/extending/toolchains#registering-building-toolchains)) | |
| 1. Ensure [`select()`s](#select) and | |
| [configuration transitions](#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](#platform-mappings) to bridge the gap. | |
| ### Common platform properties {#common-platform-properties} | |
| Common, cross-language platform properties like `OS` and `CPU` should be | |
| declared in [`@platforms`](https://github.com/bazelbuild/platforms). | |
| 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`](https://github.com/bazelbuild/platforms). | |
| ## Platform mappings {#platform-mappings} | |
| *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: | |
| ```python | |
| platforms: | |
| # Maps "--platforms=//platforms:ios" to "--ios_multi_cpus=x86_64 --apple_platform_type=ios". | |
| //platforms:ios | |
| --ios_multi_cpus=x86_64 | |
| --apple_platform_type=ios | |
| flags: | |
| # Maps "--ios_multi_cpus=x86_64 --apple_platform_type=ios" to "--platforms=//platforms:ios". | |
| --ios_multi_cpus=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](#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] for details. | |
| ## API review {#api-review} | |
| A [`platform`][platform Rule] is a collection of | |
| [`constraint_value` targets][constraint_value Rule]: | |
| ```python | |
| platform( | |
| name = "myplatform", | |
| constraint_values = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:arm", | |
| ], | |
| ) | |
| ``` | |
| A [`constraint_value`][constraint_value Rule] is a machine | |
| property. Values of the same "kind" are grouped under a common | |
| [`constraint_setting`][constraint_setting Rule]: | |
| ```python | |
| constraint_setting(name = "os") | |
| constraint_value( | |
| name = "linux", | |
| constraint_setting = ":os", | |
| ) | |
| constraint_value( | |
| name = "mac", | |
| constraint_setting = ":os", | |
| ) | |
| ``` | |
| A [`toolchain`][Toolchains] is a [Starlark rule][Starlark rule]. Its | |
| attributes declare a language's tools (like `compiler = | |
| "//mytoolchain:custom_gcc"`). Its [providers][Starlark Provider] 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 Attribute] | |
| (`target_compatible_with = ["@platforms//os:linux"]`) and machines their tools can | |
| [run on][exec_compatible_with Attribute] | |
| (`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`][register_toolchains Function] or at the | |
| command line with [`--extra_toolchains`][extra_toolchains Flag]. | |
| For more information see [here][Toolchains]. | |
| ## Questions {#questions} | |
| For general support and questions about the migration timeline, contact | |
| [bazel-discuss] or the owners of the appropriate rules. | |
| For discussions on the design and evolution of the platform/toolchain APIs, | |
| contact [bazel-dev]. | |
| ## See also {#see-also} | |
| * [Configurable Builds - Part 1] | |
| * [Platforms] | |
| * [Toolchains] | |
| * [Bazel Platforms Cookbook] | |
| * [Platforms examples] | |
| * [Example C++ toolchain] | |
| [Android Rules]: /docs/bazel-and-android | |
| [Apple Rules]: https://github.com/bazelbuild/rules_apple | |
| [Background]: #background | |
| [Bazel platforms Cookbook]: https://docs.google.com/document/d/1UZaVcL08wePB41ATZHcxQV4Pu1YfA1RvvWm8FbZHuW8/ | |
| [bazel-dev]: https://groups.google.com/forum/#!forum/bazel-dev | |
| [bazel-discuss]: https://groups.google.com/forum/#!forum/bazel-discuss | |
| [Common Platform Declarations]: https://github.com/bazelbuild/platforms | |
| [constraint_setting Rule]: /reference/be/platforms-and-toolchains#constraint_setting | |
| [constraint_value Rule]: /reference/be/platforms-and-toolchains#constraint_value | |
| [Configurable Builds - Part 1]: https://blog.bazel.build/2019/02/11/configurable-builds-part-1.html | |
| [Configuring C++ toolchains]: /tutorials/ccp-toolchain-config | |
| [Defining Constraints and Platforms]: /extending/platforms#constraints-platforms | |
| [Example C++ toolchain]: https://github.com/gregestren/snippets/tree/master/custom_cc_toolchain_with_platforms | |
| [exec_compatible_with Attribute]: /reference/be/platforms-and-toolchains#toolchain.exec_compatible_with | |
| [extra_toolchains Flag]: /reference/command-line-reference#flag--extra_toolchains | |
| [Go Rules]: https://github.com/bazelbuild/rules_go | |
| [Inspiration]: https://blog.bazel.build/2019/02/11/configurable-builds-part-1.html | |
| [Migrating your rule set]: #migrating-your-rule-set | |
| [Platforms]: /extending/platforms | |
| [Platforms examples]: https://github.com/hlopko/bazel_platforms_examples | |
| [platform mappings design]: https://docs.google.com/document/d/1Vg_tPgiZbSrvXcJ403vZVAGlsWhH9BUDrAxMOYnO0Ls/edit | |
| [platform Rule]: /reference/be/platforms-and-toolchains#platform | |
| [register_toolchains Function]: /rules/lib/globals/module#register_toolchains | |
| [Rust rules]: https://github.com/bazelbuild/rules_rust | |
| [select()]: /docs/configurable-attributes | |
| [select() Platforms]: /docs/configurable-attributes#platforms | |
| [Starlark provider]: /extending/rules#providers | |
| [Starlark rule]: /extending/rules | |
| [Starlark transitions]: /extending/config#user-defined-transitions | |
| [target_compatible_with Attribute]: /reference/be/platforms-and-toolchains#toolchain.target_compatible_with | |
| [Toolchains]: /extending/toolchains |