3.3.5 --platforms Flag
Everything in section 3.3 so far has been about describing platforms and reacting to them: 3.3.1 Configurable Attributes (select()) branches on constraint values, 3.3.2 Platform Vocabulary distinguishes target from execution, 3.3.4 Target Compatibility marks what can build where. The --platforms flag is where you choose the target platform for a build.
What --platforms Does
--platforms sets the top-level target platform for a build. It takes a label pointing to a platform() target:
bazel build //app:server --platforms=//platforms:linux_x86
That single flag tells Bazel three things1:
- Toolchain resolution selects toolchains compatible with the platform's constraint values (considering both target and execution constraints).
select()branches in BUILD files evaluate against the platform's constraints — aconfig_settingwithconstraint_values = ["@platforms//os:linux"]matches when the target platform includes that constraint.target_compatible_withchecks determine which targets are compatible and which get skipped in wildcard patterns like//....
The flag accepts any label that resolves to a platform() rule. The platform can live in your project, in a shared repository, or even in an external module.
The Default: @platforms//host
When --platforms is not set, Bazel defaults to @platforms//host2. This is a special platform that auto-detects the host machine's OS and CPU at build time. On a macOS ARM machine, for example, it resolves to:
platform(
name = "host",
constraint_values = ["@platforms//cpu:aarch64", "@platforms//os:osx"],
)
This means single-platform projects — where you build on the same type of machine where the code runs — rarely need --platforms at all. The default host platform just works.
Defining Your Own Platforms
When you need to cross-compile or target a specific environment, define a platform() in your project:
# platforms/BUILD.bazel
platform(
name = "linux_x86",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)
platform(
name = "android_arm64",
constraint_values = [
"@platforms//os:android",
"@platforms//cpu:aarch64",
],
)
The constraint values come from the standard @platforms repository, which provides OS and CPU constraints sufficient for most projects. Custom constraint settings (GPU vendor, libc variant) are only needed when the standard dimensions don't cover your build matrix — as covered in 3.3.3 Constraint Values.
The target-compatibility snippet keeps this platform layer tiny with two demo platform labels used by its --platforms commands.
.bazelrc Integration
Typing --platforms=//platforms:android_arm64 on every invocation is tedious. Named configs in .bazelrc make cross-compilation ergonomic3:
# .bazelrc
build:android --platforms=//platforms:android_arm64
build:linux --platforms=//platforms:linux_x86
Then build with:
bazel build --config=android //app:mobile
This pairs naturally with the .bazelrc layering from 3.2.1 .bazelrc Hierarchy — team-wide platform configs go in the workspace .bazelrc, while per-developer overrides live in user.bazelrc.
The maintainer workspace .bazelrc shows that named-config shape in context, with Linux and macOS configs pointing at the project's platform targets.
Legacy Flags and the Migration Path
Before the platforms API, each language had its own ad-hoc flags for targeting different architectures4. C++ used --cpu and --crosstool_top. Java used flags like --javabase, --java_toolchain, and their --host_* counterparts. Android used --android_cpu and --fat_apk_cpu. None of these interoperated — a multi-language project needed a different combination of flags per language.
--platforms is designed to replace these per-language flags with a single cross-language mechanism. The migration goal is that all projects eventually build with5:
bazel build //:myproject --platforms=//:myplatform
Most modern rulesets (Go, Rust, Java) already support --platforms for toolchain resolution. C++ rules enabled platform-based toolchain resolution by default starting in Bazel 75. Apple rules are a notable exception — they do not yet support --platforms and require platform mappings for mixed-language builds. The legacy flags are being deprecated and removed progressively. The next level revisits this same flag from the rule-author side in 4.6.1 Platform Model for Rule Authors, where the question becomes how rules ask Bazel for the right tools under a chosen target platform.
Rule authors can also attach Starlark build-setting values directly to platform targets through flags = [...]. The platform-based-flags snippet is the runnable preview of that Level 4 topic.
Platform Mappings
During migration, you may encounter projects mixing platform-aware rules with rules that still read legacy flags. Platform mappings bridge the gap by translating between --platforms and legacy flag combinations5:
# platform_mappings
platforms:
//platforms:linux_x86
--cpu=k8
--crosstool_top=//toolchains:linux_gcc
flags:
--cpu=k8
--crosstool_top=//toolchains:linux_gcc
//platforms:linux_x86
Bazel reads platform_mappings from the workspace root by default (overridable with --platform_mappings). This ensures legacy select() branches on --cpu still trigger correctly even when the build is driven by --platforms.
Platform mappings are a temporary migration aid — once all rules in your project support --platforms, remove them.
The default target platform is @platforms//host, which auto-detects your machine. For cross-compilation, define platform() targets with the appropriate constraint values, then set --platforms in .bazelrc named configs so developers can build for any target with a single --config flag. Toolchain resolution, select(), and target_compatible_with all key off whatever --platforms resolves to.
Check your understanding · 3 questions
1.A team uses --cpu=k8 and --crosstool_top=//toolchains:linux_gcc for C++ builds. They want to migrate to --platforms. What tool bridges the gap during migration?
Select one answer
2.True or false about --platforms and its defaults:
Choose True or False for each sentence
3.Why is --platforms the preferred long-term approach over per-language flags like --android_cpu or --javabase?
Select one answer
Footnotes
-
Platforms — Specifying platforms section, describing how
--platformsdrives toolchain resolution,select(), and target compatibility ↩ -
Platforms — Generally useful constraints and platforms.
@platforms//hostauto-detects host OS and CPU ↩ -
Migrating to Platforms — Migrating your project section.
--platformswith project-defined platform targets ↩ -
Configurable Builds - Part 1 — The Problem section on language-specific ad-hoc flags (
--cpu,--crosstool_top,--define) ↩ -
Migrating to Platforms — Migration goal, platform mappings, and per-language status ↩1 ↩2 ↩3