There are many instances where it would be really convenient to run multiple targets at once. By default, Bazel will not execute all targets even if you pass multiple ones:
bazel run //:lint //:format
In this case, only one of them would be executed.
Enter rules_multirun
rules_multirun is a set of rules that helps with running multiple targets either sequentially or in parallel. It is developed and maintained by Keith Smiley.
Running multiple targets
It is extremely easy to get started. First, load the multirun rule and use it like this:
load("@rules_multirun//:defs.bzl", "multirun")
multirun(
name = "xcodeproj",
testonly = True,
commands = ["//apps/app1:xcodeproj", "//apps/app2:xcodeproj"],
jobs = 0,
)
Here, I used the multirun rule to create a single runnable target that generates Xcode projects for two of my apps:
bazel run //:xcodeproj
Execution modes
The jobs attribute specifies whether targets should run sequentially or in parallel. The default value is 1, which means that targets will run one after the other. In the example above, I explicitly set it to 0 to make sure both Xcode projects are generated in parallel.
This is something that needs to be decided on a case-by-case basis, since parallel execution might not be a good fit for tools that modify files.
Why testonly
This is typically not required, but given the specifics of rules_xcodeproj and my project setup, I need to pass it in this scenario.
That is because xcodeproj passes testonly = True as soon as you add test targets, and it does that to satisfy Bazel’s restriction that non-test targets cannot depend on test-only targets.
So, like I said, this is typically not needed, but you might run into it, so I figured it was worth explaining.
Other rules from rules_multirun
rules_multirun is a set of rules, not just the multirun rule. There is a rule for configuring individual targets and commands, rules for executing targets with transitions, and more.
It is best to consult the rules_multirun docs on GitHub for the full list of available options.
Conclusion
This is a ruleset that I find extremely convenient in my daily work, and I tend to strive to optimize that last mile of developer experience whenever I can.
One thing I intentionally changed: your intro said Bazel executes only the “first” target, but then the example said only format runs. I made it “only one of them” to avoid the contradiction.