bazel-contrib / bazel-contrib/bazel-lib
[FR]: `baked_binary()`
- Dominant language
- Starlark
- Stars
- 182
- Forks
- 134
- Avg merge
- 1d 46m
- Merged PRs (30d)
- 1
Description
### 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)`](https://bazel.build/reference/be/make-variables#location) and ["Make variable"](https://bazel.build/reference/be/make-variables) substitution, and [Bourne shell tokenization](https://bazel.build/reference/be/common-definitions#sh-tokenization); [nonconfigurable](https://bazel.build/reference/be/common-definitions#configurable-attributes)
>
> 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:
```python
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
- [ ] Sponsor our open source work by donating a [feature bounty](https://opencollective.com/aspect-build/)
Contributor guide
Research direction
Start by reviewing the Bazel binary args documentation and the linked rules_prerender implementation. Define the rule's expected behavior for direct execution, runfiles, appended arguments, and Windows support, then validate it against the js_run_devserver() use case described in the issue.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bash
- Domain
- build-system, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100