Build and run a target under several configurations in a single Bazel
invocation, e.g. with multiple toolchains, compilation modes, or target
platforms. Each configuration defines a <name>.<config> target:
bazel test //... runs a test in all configurations, and
bazel run //:app.opt runs a binary in one configuration.
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("@rules_multitransition//:multitransition.bzl", "multitransition")
multitransition.test(
cc_test,
name = "my_test",
srcs = ["my_test.cpp"],
transitions = [
multitransition.transition(
name = "gcc",
extra_toolchains = ["@gcc_toolchain//:..."],
),
multitransition.transition(
name = "llvm-opt",
compilation_mode = "opt",
extra_toolchains = ["@llvm_toolchain//:..."],
),
],
)$ bazel test //... # runs //:my_test.gcc and //:my_test.llvm-optAdd the module to MODULE.bazel. Requires Bazel 7.1.0 or later.
bazel_dep(name = "rules_multitransition", version = "0.1.0")Each macro takes the rule to instantiate, its name, and a list of
configurations. The remaining arguments are passed to the rule. Every macro
creates <name> and one <name>.<config> target per configuration:
| Macro | <name>.<config> |
Also creates |
|---|---|---|
multitransition.test |
a test, run by bazel test |
<name>.suite |
multitransition.binary |
an executable, run by bazel run |
|
multitransition.target |
any target, optionally with an aspect |
See examples for each macro in use.
multitransition.transition describes one configuration. These native
options can be set as keyword arguments without any setup:
compilation_modecoptcxxoptextra_toolchainsfeatureslinkoptplatform(a single label, sets--platforms)
Values are converted to the type of the option. Values of list options are appended to the current value.
The target platform is the exception: set it with platform and a single
label, which replaces --platforms. Setting platforms directly fails: a
transition supports only one target platform
(bazelbuild/bazel#10154).
For features, prefix a feature with - to disable it. A negative feature
overrides a positive one: a configuration can disable a feature enabled on the
command line, but can't enable one disabled there.
Other native options and Starlark build settings must be declared first (see
Declaring flags, aspects, and
providers).
Set them in flags, with //command_line_option:<name> or the build
setting's label as the key:
multitransition.transition(
name = "cortex-m3",
platform = "@cortex_m//platform:lm3s6965evb",
flags = {
"//command_line_option:conlyopt": ["-std=c11"],
"@cortex_m//config:semihosting": True,
},
)multitransition.test creates a test variant per configuration, and a
test_suite named
<name>.suite of the variants. By default the untransitioned <name> is
skipped and bazel test //... runs only the variants:
$ bazel test //... # every variant
$ bazel test //:my_test.suite # every variant of 'my_test'
$ bazel test //:my_test.gcc # one varianttags, size, timeout, target_compatible_with, and visibility apply to
every target the macro creates.
multitransition.binary creates an executable variant per configuration:
multitransition.binary(
cc_binary,
name = "app",
srcs = ["app.cpp"],
transitions = [
multitransition.transition(
name = "opt",
compilation_mode = "opt",
),
],
)$ bazel run //:app.optUnlike with multitransition.test, the untransitioned <name> is an ordinary
binary: bazel run //:app runs it without any transition.
multitransition.target creates a variant per configuration that forwards
the providers of the target built in that configuration. <name> is an
ordinary target that others can depend on.
The variants forward CcInfo, DebugPackageInfo, InstrumentedFilesInfo,
and OutputGroupInfo. The variants of multitransition.test and
multitransition.binary also forward RunEnvironmentInfo. A rule can't
enumerate the providers of a target (see
bazelbuild/bazel#27123).
Other providers, e.g. PyInfo or your own, must be declared (see
Declaring flags, aspects, and
providers).
A configuration may also apply an aspect with multitransition.aspect. The
default output of that variant is then the aspect's output_group:
CLANG_TIDY = multitransition.transition(
name = "clang-tidy-report",
aspect = multitransition.aspect(
aspect = "@rules_clang_tidy//:aspects.bzl%check",
output_group = "report",
),
)
multitransition.target(
cc_library,
name = "lib",
srcs = ["lib.cpp"],
transitions = [CLANG_TIDY],
)
multitransition.target(
cc_test,
name = "lib_test",
testonly = True,
srcs = ["lib_test.cpp"],
transitions = [CLANG_TIDY],
deps = [":lib"],
)$ bazel build //:lib.clang-tidy-report //:lib_test.clang-tidy-reportThe rule may be a test or a binary. A test needs testonly = True: its
variants depend on it. lib_test is then an ordinary test, and
lib_test.clang-tidy-report is the aspect's output. multitransition.test
and multitransition.binary don't accept an aspect: their variants must be
runnable.
With Bazel 7, a multitransition.target of a test fails with conflicting
actions when the test and a variant are built together, e.g. with
bazel test //..., and the variant's configuration only applies an aspect.
Bazel 7 trims the test options from the variant, which builds the test in a
second configuration with the same output directory. Pass
--experimental_retain_test_configuration_across_testonly to keep the test
options for testonly targets.
The aspect must be declared (see Declaring flags, aspects, and providers).
To transition a target defined elsewhere, e.g. in another package or
repository, pass alias as
the rule with actual:
multitransition.test(
alias,
name = "dep_test",
actual = "@some_repo//:test",
transitions = [...],
)
multitransition.binary(
alias,
name = "dep_tool",
actual = "@some_repo//:tool",
transitions = [...],
)
multitransition.target(
alias,
name = "dep",
actual = "@some_repo//:lib",
transitions = [...],
)The variants are dep_test.<config>, dep_tool.<config>, and
dep.<config>. Note that alias has no size or timeout attributes; test
variants use the defaults.
To reuse a set of configurations, wrap multitransition.test in your own
macro:
# //:cc_multi_test.bzl
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("@rules_multitransition//:multitransition.bzl", "multitransition")
TRANSITIONS = [
multitransition.transition(...),
...
]
def cc_multi_test(*, transitions = TRANSITIONS, **kwargs):
multitransition.test(cc_test, transitions = transitions, **kwargs)Native options other than those listed above, Starlark build settings,
aspects, and providers to forward other than the built-in ones must be
declared in MODULE.bazel. Declarations from all modules in the dependency
graph are combined:
# MODULE.bazel
multitransition_config = use_extension(
"@rules_multitransition//:extensions.bzl",
"multitransition_config",
dev_dependency = True,
)
multitransition_config.extend(
aspects = [
"@rules_clang_tidy//:aspects.bzl%check",
],
command_line_options = [
"conlyopt",
],
flags = [
"@cortex_m//config:semihosting",
],
providers = [
"@rules_python//python:py_info.bzl%PyInfo",
],
)Use dev_dependency = True when only your own module uses the declarations.
Omit it if your module provides a macro that other modules call with these
options; the declarations then apply when your module is a dependency.
Two flags in @rules_multitransition//config disable the variants, e.g. to
build and run without any transition:
multitransition_test: WhenFalse, only the underlying<name>test of eachmultitransition.testis compatible, built and run without a transition. Defaults toTrue, which skips<name>and runs the variants.multitransition_binary: WhenFalse, the variants of eachmultitransition.binaryare incompatible, leaving only<name>, which is always compatible. Defaults toTrue, which builds the variants too.
$ bazel test \
--@rules_multitransition//config:multitransition_test=False \
//:my_test
$ bazel build \
--@rules_multitransition//config:multitransition_binary=False \
//...multitransition.bzlmultitransition.testmultitransition.binarymultitransition.targetmultitransition.transitionmultitransition.aspect
extensions.bzlmultitransition_config