---
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){: .external}).

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/7.5.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){: .external}).

To test your Android project with platforms, see
[Migrating Your Project](#migrating-your-project).

### Apple {:#apple}

[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](#platform-mappings).

### Other languages {:#other-languages}

* [Go rules]{: .external} fully support platforms
* [Rust rules]{: .external} 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]{: .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.

### 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]{: .external}
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/7.5.0/extending/toolchains#registering-building-toolchains) them in your
   `MODULE.bazel` file or with [`--extra_toolchains`](https://bazel.build/versions/7.5.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){: .external}.

### `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/7.5.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){: .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`](https://github.com/bazelbuild/platforms){: .external}.

## 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 "--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](#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.

## 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]{: .external} or the owners of the appropriate rules.

For discussions on the design and evolution of the platform/toolchain APIs,
contact [bazel-dev]{: .external}.

## See also {:#see-also}

* [Configurable Builds - Part 1]{: .external}
* [Platforms]
* [Toolchains]
* [Bazel Platforms Cookbook]{: .external}
* [Platforms examples]{: .external}
* [Example C++ toolchain]{: .external}

[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
