| --- | |
| title: 'Platforms' | |
| --- | |
| Bazel can build and test code on a variety of hardware, operating systems, and | |
| system configurations, using many different versions of build tools such as | |
| linkers and compilers. To help manage this complexity, Bazel has a concept of | |
| *constraints* and *platforms*. A constraint is a dimension in which build or | |
| production environments may differ, such as CPU architecture, the presence or | |
| absence of a GPU, or the version of a system-installed compiler. A platform is a | |
| named collection of choices for these constraints, representing the particular | |
| resources that are available in some environment. | |
| Modeling the environment as a platform helps Bazel to automatically select the | |
| appropriate | |
| [toolchains](/versions/8.8.1/extending/toolchains) | |
| for build actions. Platforms can also be used in combination with the | |
| [config_setting](/versions/8.8.1/reference/be/general#config_setting) | |
| rule to write [configurable attributes](/versions/8.8.1/docs/configurable-attributes). | |
| Bazel recognizes three roles that a platform may serve: | |
| * **Host** - the platform on which Bazel itself runs. | |
| * **Execution** - a platform on which build tools execute build actions to | |
| produce intermediate and final outputs. | |
| * **Target** - a platform on which a final output resides and executes. | |
| Bazel supports the following build scenarios regarding platforms: | |
| * **Single-platform builds** (default) - host, execution, and target platforms | |
| are the same. For example, building a Linux executable on Ubuntu running on | |
| an Intel x64 CPU. | |
| * **Cross-compilation builds** - host and execution platforms are the same, but | |
| the target platform is different. For example, building an iOS app on macOS | |
| running on a MacBook Pro. | |
| * **Multi-platform builds** - host, execution, and target platforms are all | |
| different. | |
| Tip: for detailed instructions on migrating your project to platforms, see | |
| [Migrating to Platforms](/versions/8.8.1/concepts/platforms). | |
| ## Defining constraints and platforms | |
| The space of possible choices for platforms is defined by using the | |
| [`constraint_setting`][constraint_setting] and | |
| [`constraint_value`][constraint_value] rules within `BUILD` files. | |
| `constraint_setting` creates a new dimension, while | |
| `constraint_value` creates a new value for a given dimension; together they | |
| effectively define an enum and its possible values. For example, the following | |
| snippet of a `BUILD` file introduces a constraint for the system's glibc version | |
| with two possible values. | |
| [constraint_setting]: /reference/be/platforms-and-toolchains#constraint_setting | |
| [constraint_value]: /reference/be/platforms-and-toolchains#constraint_value | |
| ```python | |
| constraint_setting(name = "glibc_version") | |
| constraint_value( | |
| name = "glibc_2_25", | |
| constraint_setting = ":glibc_version", | |
| ) | |
| constraint_value( | |
| name = "glibc_2_26", | |
| constraint_setting = ":glibc_version", | |
| ) | |
| ``` | |
| Constraints and their values may be defined across different packages in the | |
| workspace. They are referenced by label and subject to the usual visibility | |
| controls. If visibility allows, you can extend an existing constraint setting by | |
| defining your own value for it. | |
| The [`platform`](/versions/8.8.1/reference/be/platforms-and-toolchains#platform) rule introduces a new platform with | |
| certain choices of constraint values. The | |
| following creates a platform named `linux_x86`, and says that it describes any | |
| environment that runs a Linux operating system on an x86_64 architecture with a | |
| glibc version of 2.25. (See below for more on Bazel's built-in constraints.) | |
| ```python | |
| platform( | |
| name = "linux_x86", | |
| constraint_values = [ | |
| "@platforms//os:linux", | |
| "@platforms//cpu:x86_64", | |
| ":glibc_2_25", | |
| ], | |
| ) | |
| ``` | |
| Note: It is an error for a platform to specify more than one value of the | |
| same constraint setting, such as `@platforms//cpu:x86_64` and | |
| `@platforms//cpu:arm` for `@platforms//cpu:cpu`. | |
| ## Generally useful constraints and platforms | |
| To keep the ecosystem consistent, Bazel team maintains a repository with | |
| constraint definitions for the most popular CPU architectures and operating | |
| systems. These are all located in | |
| [https://github.com/bazelbuild/platforms](https://github.com/bazelbuild/platforms). | |
| Bazel ships with the following special platform definition: | |
| `@platforms//host` (aliased as `@bazel_tools//tools:host_platform`). This is the | |
| autodetected host platform value - | |
| represents autodetected platform for the system Bazel is running on. | |
| ## Specifying a platform for a build | |
| You can specify the host and target platforms for a build using the following | |
| command-line flags: | |
| * `--host_platform` - defaults to `@bazel_tools//tools:host_platform` | |
| * This target is aliased to `@platforms//host`, which is backed by a repo | |
| rule that detects the host OS and CPU and writes the platform target. | |
| * There's also `@platforms//host:constraints.bzl`, which exposes | |
| an array called `HOST_CONSTRAINTS`, which can be used in other BUILD and | |
| Starlark files. | |
| * `--platforms` - defaults to the host platform | |
| * This means that when no other flags are set, | |
| `@platforms//host` is the target platform. | |
| * If `--host_platform` is set and not `--platforms`, the value of | |
| `--host_platform` is both the host and target platform. | |
| ## Skipping incompatible targets | |
| When building for a specific target platform it is often desirable to skip | |
| targets that will never work on that platform. For example, your Windows device | |
| driver is likely going to generate lots of compiler errors when building on a | |
| Linux machine with `//...`. Use the | |
| [`target_compatible_with`](/versions/8.8.1/reference/be/common-definitions#common.target_compatible_with) | |
| attribute to tell Bazel what target platform constraints your code has. | |
| The simplest use of this attribute restricts a target to a single platform. | |
| The target will not be built for any platform that doesn't satisfy all of the | |
| constraints. The following example restricts `win_driver_lib.cc` to 64-bit | |
| Windows. | |
| ```python | |
| cc_library( | |
| name = "win_driver_lib", | |
| srcs = ["win_driver_lib.cc"], | |
| target_compatible_with = [ | |
| "@platforms//cpu:x86_64", | |
| "@platforms//os:windows", | |
| ], | |
| ) | |
| ``` | |
| `:win_driver_lib` is *only* compatible for building with 64-bit Windows and | |
| incompatible with all else. Incompatibility is transitive. Any targets | |
| that transitively depend on an incompatible target are themselves considered | |
| incompatible. | |
| ### When are targets skipped? | |
| Targets are skipped when they are considered incompatible and included in the | |
| build as part of a target pattern expansion. For example, the following two | |
| invocations skip any incompatible targets found in a target pattern expansion. | |
| ```console | |
| $ bazel build --platforms=//:myplatform //... | |
| ``` | |
| ```console | |
| $ bazel build --platforms=//:myplatform //:all | |
| ``` | |
| Incompatible tests in a [`test_suite`](/versions/8.8.1/reference/be/general#test_suite) are | |
| similarly skipped if the `test_suite` is specified on the command line with | |
| [`--expand_test_suites`](/versions/8.8.1/reference/command-line-reference#flag--expand_test_suites). | |
| In other words, `test_suite` targets on the command line behave like `:all` and | |
| `...`. Using `--noexpand_test_suites` prevents expansion and causes | |
| `test_suite` targets with incompatible tests to also be incompatible. | |
| Explicitly specifying an incompatible target on the command line results in an | |
| error message and a failed build. | |
| ```console | |
| $ bazel build --platforms=//:myplatform //:target_incompatible_with_myplatform | |
| ... | |
| ERROR: Target //:target_incompatible_with_myplatform is incompatible and cannot be built, but was explicitly requested. | |
| ... | |
| FAILED: Build did NOT complete successfully | |
| ``` | |
| Incompatible explicit targets are silently skipped if | |
| `--skip_incompatible_explicit_targets` is enabled. | |
| ### More expressive constraints | |
| For more flexibility in expressing constraints, use the | |
| `@platforms//:incompatible` | |
| [`constraint_value`](/versions/8.8.1/reference/be/platforms-and-toolchains#constraint_value) | |
| that no platform satisfies. | |
| Use [`select()`](/versions/8.8.1/reference/be/functions#select) in combination with | |
| `@platforms//:incompatible` to express more complicated restrictions. For | |
| example, use it to implement basic OR logic. The following marks a library | |
| compatible with macOS and Linux, but no other platforms. | |
| Note: An empty constraints list is equivalent to "compatible with everything". | |
| ```python | |
| cc_library( | |
| name = "unixish_lib", | |
| srcs = ["unixish_lib.cc"], | |
| target_compatible_with = select({ | |
| "@platforms//os:osx": [], | |
| "@platforms//os:linux": [], | |
| "//conditions:default": ["@platforms//:incompatible"], | |
| }), | |
| ) | |
| ``` | |
| The above can be interpreted as follows: | |
| 1. When targeting macOS, the target has no constraints. | |
| 2. When targeting Linux, the target has no constraints. | |
| 3. Otherwise, the target has the `@platforms//:incompatible` constraint. Because | |
| `@platforms//:incompatible` is not part of any platform, the target is | |
| deemed incompatible. | |
| To make your constraints more readable, use | |
| [skylib](https://github.com/bazelbuild/bazel-skylib)'s | |
| [`selects.with_or()`](https://github.com/bazelbuild/bazel-skylib/blob/main/docs/selects_doc.md#selectswith_or). | |
| You can express inverse compatibility in a similar way. The following example | |
| describes a library that is compatible with everything _except_ for ARM. | |
| ```python | |
| cc_library( | |
| name = "non_arm_lib", | |
| srcs = ["non_arm_lib.cc"], | |
| target_compatible_with = select({ | |
| "@platforms//cpu:arm": ["@platforms//:incompatible"], | |
| "//conditions:default": [], | |
| }), | |
| ) | |
| ``` | |
| ### Detecting incompatible targets using `bazel cquery` | |
| You can use the | |
| [`IncompatiblePlatformProvider`](/versions/8.8.1/rules/lib/providers/IncompatiblePlatformProvider) | |
| in `bazel cquery`'s [Starlark output | |
| format](/versions/8.8.1/query/cquery#output-format-definition) to distinguish | |
| incompatible targets from compatible ones. | |
| This can be used to filter out incompatible targets. The example below will | |
| only print the labels for targets that are compatible. Incompatible targets are | |
| not printed. | |
| ```console | |
| $ cat example.cquery | |
| def format(target): | |
| if "IncompatiblePlatformProvider" not in providers(target): | |
| return target.label | |
| return "" | |
| $ bazel cquery //... --output=starlark --starlark:file=example.cquery | |
| ``` | |
| ### Known Issues | |
| Incompatible targets [ignore visibility | |
| restrictions](https://github.com/bazelbuild/bazel/issues/16044). |