> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# A better genrule

> Use Bazel's run_binary as a hermetic alternative to genrule, adapting an executable into a deterministic action with declared inputs and outputs.

### Key building block: `run_binary`

This rule is an "adapter" from an executable (something you could `bazel run`) to an action (something you can `bazel build`).

The executable (called a "tool" here) is run in a single action
which spawns that executable given some declared inputs, and produces some declared, default outputs.

:::caution
Bazel's built-in [`genrule`](https://bazel.build/reference/be/general#genrule)
looks a lot like `run_binary`, but it's best to avoid it.

* Arbitrary bash one-liner, commonly non-hermetic
* Bash dependency hurts portability
* Subtly different semantics for `expand_location`, `stamp`, etc.

:::

Here's a sample usage, which runs `my_tool` with three arguments to produce a folder called `dir_a`:

1. The path to `some.file` which is the only input
2. An `-outdir` flag, which we know from reading the CLI documentation for my\_tool.
   * We're always required to predict what path the tool will write to.
     If you get it wrong, Bazel will error that the "output was not produced".
3. A syntax-sugar shorthand for "the output folder Bazel assigns for this action"

```python theme={null}
load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary")

run_binary(
    name = "dir_a",
    srcs = ["some.file"],
    args = ["$(execpath some.file)", "--outdir", "$(@D)"],
    out_dirs = ["dir_a"],
    tool = ":my_tool",
)

```

:::note
The `js_run_binary` rule takes it a step further, adding the ability to:

* capture stdout/stderr/exit code as "outputs"
* `chdir` to a specific working directory
* throw away log-spam on success

Follow [https://github.com/bazel-contrib/bazel-lib/issues/575](https://github.com/bazel-contrib/bazel-lib/issues/575)
:::

### Making tools work

The `tool` in `run_binary` can be any executable.
However some tools don't work the way Bazel expects.
This can usually be fixed without having to change the tool, which is good since most tools are
written by third-parties who don't care about your Bazel migration problems!

:::caution
Google engineers got in the habit of rewriting everything to work with Blaze.
Do not follow their lead! Changing more than one thing at a time makes your migration riskier.
:::

You can make most tools work under Bazel by asking:
"How can the tool tell that it's running under Bazel?"

There are three ways to make the tool think it's still running outside Bazel:

1. "Monkey-patch" the runtime
   * Node.js `-require` flag to run
   * JVM has a classpath, you can inject a shadowing class
2. In-process wrapper
   * Peel one layer off the tool's CLI
   * Write your own CLI that calls its entry point
3. Parent process wrapper
   * Often a short Bash script

:::note Read more
[things a program must not do under bazel](/blog/things-a-program-must-not-do-under-bazel)
:::
