3.3.2 Platform Vocabulary

Every build involves related but distinct machine choices. When code runs on the developer's host and actions execute there too, those choices are easy to overlook. A phone may be the target platform, while a CI worker is an execution platform: the target choice affects libraries and select() branches, while the execution choice says where compilers and other build tools run.

Bazel makes this explicit through three platform roles and a clean separation between where you build and what you build for1.

Three platform roles
Every build has a host, an execution platform, and a target platform
HOST
Where Bazel runs
The Bazel server process
@platforms//host
EXECUTION
Where actions run
Compiling, linking, codegen
local machine or RBE worker
TARGET
What you build for
Where the binary will run
‑‑platforms=//platforms:android_arm64
SINGLE-PLATFORM
All three are the same
Build on laptop, run on laptop
host = exec = target
CROSS-COMPILATION
Different target
Build iOS app on a Mac
host = exec ≠ target
MULTI-PLATFORM
All three differ
Remote workers + different target
host ≠ exec ≠ target
select() matches the target platform — what the code is built for, not where it compiles.

Three Platform Roles

Bazel recognizes three roles a machine can play in a build1:

Host platform — the machine where Bazel itself runs. There is exactly one host platform per build invocation. Bazel auto-detects it at startup and exposes it as @platforms//host1.

Execution platform — the machine that runs build actions (compiling, linking, code generation). For local builds, this is the same as the host. With remote build execution, it can be a different machine entirely — a Linux CI worker, a cluster node, or a macOS machine with Xcode1.

Target platform — the machine the built software is intended to run on. This is what --platforms controls. When you build an Android app on your laptop, the target platform is Android ARM64 even though the execution platform is your laptop's OS and CPU1.

A single build can have one host platform, multiple execution platforms, and multiple target platforms1. A mobile app might compile C++ natively on a remote Linux worker (execution) while targeting both Android ARM64 and iOS ARM64 (two target platforms).

Three Build Scenarios

The relationship between these roles defines the type of build1:

Single-platform build — host, execution, and target are all the same. This is the default: you build on your laptop, for your laptop, and run the result on your laptop. Most developers start here and never need to think about platforms explicitly1.

Cross-compilation build — host and execution are the same, but the target is different. Building an iOS app on a Mac without remote execution is cross-compilation: the Mac is both host and execution platform, but the target is an iPhone1.

Multi-platform build — all three roles are different. Building an iOS app on a Mac while offloading C++ compilation to remote Linux machines means the host is the Mac, some execution platforms are remote Linux, and the target is iOS1.

Why the Distinction Matters for select()

The select() mechanism introduced in 3.3.1 Configurable Attributes (select()) matches against the target platform configuration, not the execution platform2. If you cross-compile for Linux on a Mac, a select() key like @platforms//os:linux fires — even though the compiler is running on macOS2.

This matches the intuitive meaning: platform-specific source code should match the platform where the binary will run, not where it was compiled. But it surprises developers who assume select() reflects their current machine.

Tools that need to run during the build — compilers, code generators, test harnesses — are a different story. They need to match the execution platform. Bazel handles this through cfg = "exec" on rule attributes, covered in 4.6.4 Execution Configuration for Tools (cfg = "exec").

The Default: @platforms//host

When --platforms is not set, Bazel defaults to @platforms//host1. This special platform auto-detects the local machine's OS and CPU using constraint values from the @platforms repository3. There is no need to define it — it exists automatically.

This means single-platform builds require no platform configuration at all. The host detection maps the local machine to its matching @platforms//os:* and @platforms//cpu:* constraint values (for example, @platforms//os:linux and @platforms//cpu:x86_64 on an x86 Linux workstation), so select() and config_setting work without explicit --platforms flags3.

Beyond Two Roles: the Full Picture

The two-role mental model — "execution is where you build, target is what you build for" — covers most maintainer scenarios. But the three-role model matters in practice when:

  • Remote build execution separates host from execution (the Bazel server runs locally, but actions run remotely)
  • A build produces both host tools and target binaries in the same invocation
  • Toolchain resolution needs to find a compiler that runs on the execution platform and produces code for the target platform4

The rule-author view of the same model starts in 4.6.1 Platform Model for Rule Authors. The full toolchain resolution mechanism, execution groups, and custom platform definitions are covered across 4.6 Toolchains & Platform Resolution. The constraint values that compose platforms are detailed in 3.3.3 Constraint Values, and the --platforms flag for overriding the target platform is in 3.3.5 --platforms Flag. For now, the key vocabulary is:

  • Target platform = what you're building for — what select() sees, what --platforms sets
  • Execution platform = what you're building on — where actions run
  • Host platform = where the Bazel server process runs — usually the same as execution for local builds

Apple builds make the dimensions concrete. The apple_support map points to upstream platforms/ and constraints/, where OS, CPU, and device-versus-simulator are modeled separately. Xcode selection is a separate configuration mechanism exercised by xcode_config_test.bzl through local/remote Xcode-version resolution. That is Apple-specific evidence for the vocabulary, not a new generic platform axis.

key takeaway

"Building on Linux for Android" means execution platform = Linux, target platform = Android. select() matches the target platform — the machine your code will run on, not the machine compiling it.

Check your understanding · 3 questions

1.Match each platform role to its definition:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
Host platform
Execution platform
Target platform

2.You build an iOS app on a Mac without remote execution. You use select({'@platforms//os:ios': [':ios_impl']}). Which platform does that condition match against?

Select one answer

3.True or false about platform defaults:

Choose True or False for each sentence

When --platforms is not set, Bazel defaults to @platforms//host, which auto-detects the local machine's OS and CPU.
A single build can target only one platform at a time.
0 of 3 answered

Footnotes

  1. Platforms — platform types, three build scenarios, and @platforms//host default 1 2 3 4 5 6 7 8 9 10 11

  2. Configurable Build Attributes — select() and platform constraint_values matching 1 2

  3. Migrating to Platforms — default platform detection and @platforms standard constraint values 1 2

  4. Writing Bazel rules: platforms and toolchains — host/execution/target platform concepts, toolchain resolution as dependency injection