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
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--platformssets - 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.
"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
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
Footnotes
-
Platforms — platform types, three build scenarios, and
@platforms//hostdefault ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
Configurable Build Attributes — select() and platform constraint_values matching ↩1 ↩2
-
Migrating to Platforms — default platform detection and
@platformsstandard constraint values ↩1 ↩2 -
Writing Bazel rules: platforms and toolchains — host/execution/target platform concepts, toolchain resolution as dependency injection ↩