When I first started writing custom Bazel rules, I often created
separate rules for the build and run commands in Bazel. This
was a mistake.
For example, I once wrote rules that looked something like:
azure_function_app_build(
name = "my_app",
srcs = ["..."],
)
azure_function_app_run(
name = "run_app",
app = ":my_app",
)
My expectation that users would bazel build //:my_app and
bazel run //:run_app.
This separation is silly and unnecessary. A Bazel rule can support
both build and run at the same time. All you have to do is declare
the rule as executable and have it return a DefaultInfo() object with
both an executable and a files attribute.
For example, here is an example of a extremely simplistic cc_binary()
rule:
# my_cc_binary.bzl
def _impl(ctx):
executable = ctx.actions.declare_file(ctx.attr.name)
args = ctx.actions.args()
args.add("-o", executable)
args.add_all(ctx.files.srcs)
ctx.actions.run(
inputs = ctx.files.srcs,
outputs = [executable],
# This is for demo purposes. In a real Bazel rule you'd
# want to use cc_common.
executable = "/usr/bin/gcc",
arguments = [args],
)
return [
DefaultInfo(
executable = executable,
files = depset([executable]),
),
]
my_cc_binary = rule(
implementation = _impl,
attrs = {
"srcs": attr.label_list(
mandatory = True,
allow_files = True,
),
},
executable = True,
)
# BUILD
load("my_cc_binary.bzl", "my_cc_binary")
my_cc_binary(
name = "exe",
srcs = ["main.c"],
)
With this rule definition, you can generate the executable with
bazel build //:exe and run it with bazel run //:exe.
The above sample code can also be found at https://github.com/sengelha/practical_bazel_examples/tree/master/build_and_run_rule.