bazel-lib icon indicating copy to clipboard operation
bazel-lib copied to clipboard

[FR]: `baked_binary()`

Open dgp1130 opened this issue 2 years ago • 2 comments
trafficstars

What is the current behavior?

In Bazel by default, executable binaries have a built-in args attribute, so bazel run will automatically use those arguments. However, the args are dropped if the binary is invoked directly via bazel-bin/... or in the runfiles of another binary. Quoting from https://bazel.build/reference/be/common-definitions#common-attributes-binaries:

args - List of strings; optional; subject to $(location) and "Make variable" substitution, and Bourne shell tokenization; nonconfigurable

Command line arguments that Bazel will pass to the target when it is executed either by the run command or as a test. These arguments are passed before the ones that are specified on the bazel run or bazel test command line.

NOTE: The arguments are not passed when you run the target outside of Bazel (for example, by manually executing the binary in bazel-bin/).

For some rules (such as js_run_devserver()) executing a binary from runfiles is required and args is effectively useless, since you can't guarantee it will be present. It would be great to have an easy way to ensure hard-coded arguments in the BUILD file are always present, regardless of how the binary is executed.

Describe the feature

I propose a new baked_binary() rule which takes a baked_args attribute and generates a wrapper binary which hard-codes those arguments, with any subsequent arguments appended after it. This means arguments are "baked" into the binary and will be retained regardless of how the binary is executed. This would look like:

load("@aspect_bazel_lib//lib:baked_binary.bzl", "baked_binary")

sh_binary(
    name = "server",
    srcs = ["server.sh"],
    data = [":site"],
    # Can't rely on `args`, because it won't always be there.
    # args = ["path/to/site/"],
)

baked_binary(
    name = "baked_server",
    binary = ":server",
    # Bake the args so they are always present.
    baked_args = ["path/to/site"],
)

# When this runs `./baked_server --port 1234`, `path/to/site` is already included
# and does not need to be repeated.
sh_test(
    name = "test",
    srcs = ["test.sh"],
    data = [":baked_server"],
)

I made my own implementation of this in https://github.com/dgp1130/rules_prerender/commit/c0065dc2598be72120abb9ecc9bea8af20aed1ff#diff-2f9d8f02c056b3df3e9ff8b6fb76b84689e08c39773d3661dcfcd482cf13a267, though it isn't great since it requires Bash, doesn't support Windows, and needed some hacks to be compatible with js_run_devserver() (chdir messes up Bash runfiles).

Fund our work

dgp1130 avatar Feb 05 '23 07:02 dgp1130

Another examples of prior art is the command rule that is used with multirun: https://github.com/atlassian/bazel-tools/blob/master/multirun/README.md

@alexeagle You had some thoughts on this one if I recall correctly

gregmagolan avatar Feb 07 '23 19:02 gregmagolan

Yeah I had a general wrapped_binary concept that could do a bunch of little utility things, based on my "genrule is bestrule" principle. https://github.com/aspect-build/bazel-lib/pull/251 I haven't had time to come back to it though.

alexeagle avatar May 08 '23 15:05 alexeagle