…
|
||
---|---|---|
.. | ||
BUILD | ||
MODULE.bazel | ||
README.md | ||
WORKSPACE | ||
show_all_outputs.bzl |
README.md
Where are my files?
Finding the exact path to the files created by a target.
Most of the time, Bazel users do not need to know the path to the artifacts created for any given target. A notable exception is for users of packaging rules. You typically create an RPM or Debian packaged file for the explicit purpose of taking it from your machine and giving it to someone else.
Users often create scripts to push bazel build
outputs to other places and
need to know the path to those outputs. This can be a challenge for rules which
may create the file name by combining a base part with a version number,
and maybe a CPU architecture. We don't do find them with shell wildcards
like bazel-bin/my-pkg/pkg-*.deb
. That is brittle. Fortunately, Bazel
provide all the tools we need to get the precise path of an output.
Using cquery to find the exact path to the outputs created for a target.
We can use Bazel's cquery command to find information about a target.
In Bazel 5.3.0 or later, there is a --output=files
flag that provides this info directly. (PR)
Older versions of Bazel require a small Starlark program to be supplied. Specifically we use cquery's Starlark output to inspect a target and print exactly what we need. Let's try it:
bazel build :deb
bazel cquery :deb --output=starlark --starlark:file=show_all_outputs.bzl 2>/dev/null
That should produce something like
deb: bazel-out/k8-fastbuild/bin/mwp_3.14_all.deb
changes: bazel-out/k8-fastbuild/bin/mwp_3.changes
How it works
show_all_outputs.bzl is a Starlark script that must contain a function with the
name format
, that takes a single argument. The argument is typically named
target, and is a configured Bazel target, as you might have access to while
writing a custom rule. We can inspect its providers and print them in a useful
way.
For pkg_deb, there are two files, the .deb file and the .changes, and both are passed along in the rule's OutputGroupInfo provider. This snippet below (from show_all_outputs.bzl) prints them.
def format(target):
provider_map = providers(target)
output_group_info = provider_map["OutputGroupInfo"]
# Look at the attributes of the provider. Visit the depsets.
ret = []
for attr in dir(output_group_info):
if attr.startswith("_"):
continue
attr_value = getattr(output_group_info, attr)
if type(attr_value) == "depset":
for file in attr_value.to_list():
ret.append("%s: %s" % (attr, file.path))
return "\n".join(ret)
A full explanation of why this works is beyond the scope of this example. It requires some knowledge of how to write custom Bazel rules. See the Bazel documentation for more information.
Using an implicit output as input to another rule.
Sometimes a rule will create an implicit output that the user does not explicitly specify as an attribute of the target. The .changes file from pkg_deb is an example. If we want another rule to depend on an implicitly created file, we can do that with a filegroup that specifies the specific output group containing that file.
In the example below, :deb
is a rule producing an explicit .deb output
and an implicit .changes output. We refer to the .changes file using the
filegroup
and specifying the desired output group name. Then, any rule
can use this filegroup
as an input.
pkg_deb(name = "deb", ...)
filegroup(
name = "the_changes_file",
srcs = [":deb"],
output_group = "changes",
)
genrule(
name = "use_changes_file",
srcs = [":the_changes_file"],
outs = ["copy_of_changes.txt"],
cmd = "cp $(location :the_changes_file) $@",
)