blob: 84f8bf3d4801b84df3948a7808e6e11eb7c432bc [file] [view]
# use_execroot_entry_point
This page describes the `use_execroot_entry_point` option on `js_run_binary`
and provides guidance on when to use each value. The short version is that
`use_execroot_entry_point=True` sets up a directory layout that is more
friendly to some JavaScript tools, but at the expense of a performance hit and
issues with cross-platform builds.
## Background
When a `js_binary` is used as a tool in `js_run_binary`, Bazel runs it as a
build action on the exec platform. The execroot is the root of the build
sandbox; beneath it sits `bazel-out/`, which contains output directories for
both the exec and target configurations. The tool's sources can therefore
potentially appear in up to three places:
- **Exec-platform bin** (`bazel-out/<exec-cfg>/bin/`): where build artifacts for
the exec platform land.
- **Runfiles tree** (`bazel-out/<exec-cfg>/bin/path/to/my_binary.runfiles/`):
where the tool's runtime dependencies (including `node_modules`) are
symlinked and made available to the build action.
- **Target-platform bin** (`bazel-out/<target-cfg>/bin/`): where the `srcs` of
the `js_run_binary` action land. This is also the default working directory
for the build action, though it can be adjusted via the `chdir` attribute.
Resolving the same package in more than one location can result in subtle bugs,
so this is a potential danger here given that the same sources can appear in up
to three places. As described below, `use_execroot_entry_point = True`
addresses the problem by keeping all sources in the target-platform bin
directory, but this has downsides.
## What `use_execroot_entry_point` does
**`use_execroot_entry_point = True` (the current default):**
The entry point used is the one in the target-platform bin
directory--confusingly called the "execroot entry point" even though the
execroot encompasses the whole sandbox. In order for the tool's sources to land
in that directory, they end up being rebuilt for the target platform. With
everything consolidated in `bazel-out/<target-cfg>/bin/`, Node.js sees a single
`node_modules` tree. This can be the right choice for some frameworks such as
Next.js, which expects inputs and outputs to be in the same directory tree.
The tradeoff is that if the exec platform differs from the target platform (for
example, cross-compiling from macOS to Linux), target-platform artifacts such as
native Node.js addons are rebuilt for the target and may fail to run on the exec
platform.
**`use_execroot_entry_point = False`:**
The entry point used is the one from the runfiles tree. All code executed during
the build action runs from the runfiles tree, which avoids cross-platform
issues. However, you must ensure that any code executed during the build (for
example, JavaScript config files for tools like Webpack or Rspack) is a declared
dependency of the `js_binary` tool, not merely a source file passed to
`js_run_binary`. Source files in the `js_run_binary`'s `srcs` will land in the
target-platform bin directory and will therefore not be visible to the tool's
runfiles resolution.
## Recommendation
We recommend setting `use_execroot_entry_point = False`, unless your tool
requires its config and outputs to be adjacent to each other in the same
directory (such as Next.js for example). If you do this and ensure that all
code executed during the build is declared as a dependency of the `js_binary`,
then your build will work reliably even in cross-platform situations.
To disable `use_execroot_entry_point` by default, pass the build flag:
```
--@aspect_rules_js//js:use_execroot_entry_point=False
```
You may want to set it in your `.bazelrc` as follows:
```
common --@aspect_rules_js//js:use_execroot_entry_point=False
```
Individual targets can still override the flag by explicitly setting
`use_execroot_entry_point = True` or `use_execroot_entry_point = False`.
In a future major version, we will likely disable the `use_execroot_entry_point`
behavior by default.
### Example
A simple demonstration of the recommended way to set up a `js_run_binary`
target is the [Rspack example](../examples/rspack/BUILD.bazel). This particular
case *requires* `use_execroot_entry_point = False`, because otherwise the
cross-platform build in that file would fail as a result of Bazel trying to use
the wrong Rspack binary. Below is the key part, edited slightly for brevity:
```
js_library(
name = "rspack_config",
srcs = ["rspack.config.cjs"],
deps = [":node_modules/@rspack/cli"],
)
bin.rspack(
name = "rspack_build",
srcs = ["rspack_entry.js"],
outs = ["rspack/main.bundle.js"],
chdir = package_name(),
data = [":rspack_config"],
fixed_args = [
"build",
"--config",
"$$RUNFILES_DIR/$(rlocationpath :rspack_config)",
],
use_execroot_entry_point = False,
)
```
Note that in this case `bin.rspack()` is a generated macro that creates both
the `js_binary` for Rspack *and* the `js_run_binary` target that runs it.
Key points:
- The `rspack.config.cjs` file is wrapped in a `js_library` and taken as a
`data` dependency of the `js_binary`. This ensures that the config file and
its dependencies are built for the exec platform, which is appropriate
since they will run during the build action. They will land in the runfiles
directory adjacent to the other exec-platform sources, which will allow
module resolution to proceed correctly.
- `chdir = package_name()` causes the working directory to be
`bazel-out/<target-cfg>/bin/rspack`. This is not strictly necessary, but it
is convenient to have the outputs go directly in the build action's current
directory.
- The config file (`rspack.config.cjs`) refers to `process.cwd()`, not
`__dirname`, for specifying the output path. This is key, because the config
file (and therefore `__dirname`) will be in the runfiles directory and not
the output tree.
- We refer to `"$$RUNFILES_DIR/$(rlocationpath :rspack_config)"` in
`fixed_args`. `$(rlocationpath ...)` is evaluated at analysis time and
determines the path to the config file within the runfiles directory. This
argument must go in `fixed_args` rather than `args`, to allow `$RUNFILES_DIR`
to be evaluated at run time. Note the double dollar sign (`$$`) to prevent
the `js_binary` implementation from attempting to evaluate that variable at
analysis time.