| --- | |
| title: 'Platforms' | |
| --- | |
| ## Intro | |
| Bazel can build and test code on a variety of hardware, operating systems, and | |
| system configurations. This can involve different versions of build tools like | |
| linkers and compilers. To help manage this complexity, Bazel has the concepts | |
| of *constraints* and *platforms*. | |
| A *constraint* is a distinguishing property of a build or production machine. | |
| Common constraints are CPU architecture, presence or absence of a GPU, or | |
| version of a locally installed compiler. But constraints can be *anything* that | |
| meaningfully distinguishes machines when orchestrating build work. | |
| A *platform* is a collection of constraints that specifies a complete machine. | |
| Bazel uses this concept to let developers choose which machines they want to | |
| build for, which machines should run compile and test actions, and which | |
| [toolchains](/extending/toolchains) build actions should compile with. | |
| Developers can also use constraints to [select](/docs/configurable-attributes) | |
| custom properties or dependencies on their build rules. For example: "*use | |
| `src_arm.cc` when the build targets an Arm machine*". | |
| ## Platform types | |
| Bazel recognizes three roles a platform may play: | |
| * **Host** - The platform on which Bazel itself runs. | |
| * **Execution** - A platform which runs compile actions to produce build outputs. | |
| * **Target** - A platform the code being built should run on. | |
| Note: Builds only have one host platform, but often have multiple execution and | |
| target platforms. For example, both remote Linux CI machines and developer Macs | |
| may run build actions. And mobile apps have code for multiple phone types and | |
| hardware extensions. | |
| Builds generally have three kinds of relationships to platforms: | |
| * **Single-platform builds** - Host, execution, and target platforms are the | |
| same. For example, building on a developer machine without remote execution, | |
| then running the built binary on the same machine. | |
| * **Cross-compilation builds** - Host and execution platforms are the same, | |
| but the target platform is different. For example, building an iOS app on a | |
| Macbook Pro without remote execution. | |
| * **Multi-platform builds** - Host, execution, and target platforms are all | |
| different. For example, building an iOS app on a Macbook Pro and using remote | |
| Linux machines to compile C++ actions that don't need Xcode. | |
| ## Specifying platforms | |
| The most common way for developers to use platforms is to specify desired | |
| target machines with the `--platforms` flag: | |
| ```shell | |
| $ bazel build //:my_linux_app --platforms=//myplatforms:linux_x86 | |
| ``` | |
| Organizations generally maintain their own platform definitions because build | |
| machine setups vary between organizations. | |
| When `--platforms` isn't set, it defaults to `@platforms//host`. This is | |
| specially defined to auto-detect the host machine's OS and CPU properties so | |
| builds target the same machine Bazel runs on. Build rules can | |
| [select](/docs/configurable-attributes) on these properties with the | |
| [@platforms/os](https://github.com/bazelbuild/platforms/blob/main/os/BUILD) and | |
| [@platforms/cpu](https://github.com/bazelbuild/platforms/blob/main/cpu/BUILD) | |
| constraints. | |
| ## Generally useful constraints and platforms {#useful-constraints-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 defined 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 | |
| auto-detects the OS and CPU properties of the machine Bazel runs on. | |
| ## Defining constraints {#constraints} | |
| Constraints are modeled with the [`constraint_setting`][constraint_setting] and | |
| [`constraint_value`][constraint_value] build rules. | |
| [constraint_setting]: /reference/be/platforms-and-toolchains#constraint_setting | |
| [constraint_value]: /reference/be/platforms-and-toolchains#constraint_value | |
| `constraint_setting` declares a type of property. For example: | |
| ```python | |
| constraint_setting(name = "cpu") | |
| ``` | |
| `constraint_value` declares a possible value for that property: | |
| ```python | |
| constraint_value( | |
| name = "x86", | |
| constraint_setting = ":cpu" | |
| ) | |
| ``` | |
| These can be referenced as labels when defining platforms or customizing build rules | |
| on them. If the above examples are defined in `cpus/BUILD`, you can reference | |
| the `x86` constraint as `//cpus:x86`. | |
| If visibility allows, you can extend an existing `constraint_setting` by | |
| defining your own value for it. | |
| ## Defining platforms {#platforms} | |
| The [`platform`](/reference/be/platforms-and-toolchains#platform) build rule | |
| defines a platform as a collection of `constraint_value`s: | |
| ```python | |
| platform( | |
| name = "linux_x86", | |
| constraint_values = [ | |
| "@platforms//os::linux", | |
| "@platforms//cpu:x86", | |
| ], | |
| ) | |
| ``` | |
| This models a machine that must have both the `//@platforms//os:linux` and `//@platforms//cpu:x86` | |
| constraints. | |
| Platforms may only have one `constraint_value` for a given `constraint_setting`. | |
| This means, for example, a platform can't have two CPUs unless you create another | |
| `constraint_setting` type to model the second value. | |
| ## Skipping incompatible targets {#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`](/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 won't 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? {#when-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`](/reference/be/general#test_suite) are | |
| similarly skipped if the `test_suite` is specified on the command line with | |
| [`--expand_test_suites`](/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 {#expressive-constraints} | |
| For more flexibility in expressing constraints, use the | |
| `@platforms//:incompatible` | |
| [`constraint_value`](/reference/be/platforms-and-toolchains#constraint_value) | |
| that no platform satisfies. | |
| Use [`select()`](/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` {#cquery-incompatible-target-detection} | |
| You can use the | |
| [`IncompatiblePlatformProvider`](/rules/lib/providers/IncompatiblePlatformProvider) | |
| in `bazel cquery`'s [Starlark output format](/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). |