blob: c251019ed1c0b7842f30d56aa053ed1fc5e2d3af [file] [log] [blame]
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
run_binary() build rule implementation.
Runs a binary as a build action. This rule does not require Bash (unlike native.genrule()).
"""
load("//lib:dicts.bzl", "dicts")
def _impl(ctx):
tool_as_list = [ctx.attr.tool]
tool_inputs, tool_input_mfs = ctx.resolve_tools(tools = tool_as_list)
args = [
# Expand $(location) / $(locations) in args.
#
# To keep the rule simple, do not expand Make Variables (like *_binary.args usually would).
# (We can add this feature later if users ask for it.)
#
# Also for simple implementation and usage, do not Bash-tokenize the arguments. Without
# tokenization the user can write args=["a b"] to pass (a b) as one argument, but with
# tokenization they would have to write args=["'a b'"] or args=["a\\ b"]. There's no
# documented tokenization function anyway (as of 2019-05-21 ctx.tokenize exists but is
# undocumented, see https://github.com/bazelbuild/bazel/issues/8389).
ctx.expand_location(a, tool_as_list) if "$(location" in a else a
for a in ctx.attr.args
]
envs = {
# Expand $(location) / $(locations) in the values.
k: ctx.expand_location(v, tool_as_list) if "$(location" in v else v
for k, v in ctx.attr.env.items()
}
ctx.actions.run(
outputs = ctx.outputs.outs,
inputs = ctx.files.srcs,
tools = tool_inputs,
executable = ctx.executable.tool,
arguments = args,
mnemonic = "RunBinary",
use_default_shell_env = False,
env = dicts.add(ctx.configuration.default_shell_env, envs),
input_manifests = tool_input_mfs,
)
return DefaultInfo(
files = depset(ctx.outputs.outs),
runfiles = ctx.runfiles(files = ctx.outputs.outs),
)
run_binary = rule(
implementation = _impl,
doc = "Runs a binary as a build action.<br/><br/>This rule does not require Bash (unlike" +
" <code>native.genrule</code>).",
attrs = {
"tool": attr.label(
doc = "The tool to run in the action.<br/><br/>Must be the label of a *_binary rule," +
" of a rule that generates an executable file, or of a file that can be" +
" executed as a subprocess (e.g. an .exe or .bat file on Windows or a binary" +
" with executable permission on Linux). This label is available for" +
" <code>$(location)</code> expansion in <code>args</code> and <code>env</code>.",
executable = True,
allow_files = True,
mandatory = True,
cfg = "host",
),
"env": attr.string_dict(
doc = "Environment variables of the action.<br/><br/>Subject to " +
" <code><a href=\"https://docs.bazel.build/versions/main/be/make-variables.html#location\">$(location)</a></code>" +
" expansion.",
),
"srcs": attr.label_list(
allow_files = True,
doc = "Additional inputs of the action.<br/><br/>These labels are available for" +
" <code>$(location)</code> expansion in <code>args</code> and <code>env</code>.",
),
"outs": attr.output_list(
mandatory = True,
doc = "Output files generated by the action.<br/><br/>These labels are available for" +
" <code>$(location)</code> expansion in <code>args</code> and <code>env</code>.",
),
"args": attr.string_list(
doc = "Command line arguments of the binary.<br/><br/>Subject to" +
"<code><a href=\"https://docs.bazel.build/versions/main/be/make-variables.html#location\">$(location)</a></code>" +
" expansion.",
),
},
)