blob: 788de9ff897eafb5e11cbc13564ecb87e77ea124 [file]
"""Runs a js_binary as a build action.
This macro wraps Aspect bazel-lib's run_binary (https://github.com/aspect-build/bazel-lib/blob/main/lib/run_binary.bzl)
and adds attributes and features specific to rules_js's js_binary.
Load this with,
```starlark
load("@aspect_rules_js//js:defs.bzl", "js_run_binary")
```
"""
load("@aspect_bazel_lib//lib:run_binary.bzl", _run_binary = "run_binary")
load("@aspect_bazel_lib//lib:utils.bzl", "to_label")
load("@aspect_bazel_lib//lib:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
load("@bazel_skylib//lib:dicts.bzl", "dicts")
load(":js_binary_helpers.bzl", _envs_for_log_level = "envs_for_log_level")
load(":js_filegroup.bzl", _js_filegroup = "js_filegroup")
def js_run_binary(
name,
tool,
env = {},
srcs = [],
outs = [],
out_dirs = [],
args = [],
chdir = None,
stdout = None,
stderr = None,
exit_code_out = None,
silent_on_success = True,
copy_srcs_to_bin = True,
include_transitive_sources = True,
include_declarations = False,
include_npm_linked_packages = True,
log_level = None,
mnemonic = "JsRunBinary",
progress_message = None,
execution_requirements = None,
stamp = 0,
patch_node_fs = True,
**kwargs):
"""Wrapper around @aspect_bazel_lib `run_binary` that adds convienence attributes for using a `js_binary` tool.
This rule does not require Bash `native.genrule`.
Args:
name: Target name
tool: The tool to run in the action.
Should be a js_binary rule. Use Aspect bazel-lib's run_binary
(https://github.com/aspect-build/bazel-lib/blob/main/lib/run_binary.bzl)
for other *_binary rule types.
env: Environment variables of the action.
Subject to `$(location)` and make variable expansion.
srcs: Additional inputs of the action.
These labels are available for `$(location)` expansion in `args` and `env`.
outs: Output files generated by the action.
These labels are available for `$(location)` expansion in `args` and `env`.
out_dirs: Output directories generated by the action.
These labels are _not_ available for `$(location)` expansion in `args` and `env` since
they are not pre-declared labels created via attr.output_list(). Output directories are
declared instead by `ctx.actions.declare_directory`.
args: Command line arguments of the binary.
Subject to `$(location)` and make variable expansion.
chdir: Working directory to run the binary or test in, relative to the workspace.
This overrides the chdir value if set on the js_binary tool target.
By default, Bazel always runs in the workspace root.
To run in the directory containing the js_run_binary under the source tree, use
`chdir = package_name()` (or if you're in a macro, use `native.package_name()`).
To run in the output directory where the js_run_binary writes outputs, use
`chdir = "$(RULEDIR)"`
WARNING: this will affect other paths passed to the program, either as arguments or in configuration files,
which are workspace-relative.
You may need `../../` segments to re-relativize such paths to the new working directory.
stderr: set to capture the stderr of the binary to a file, which can later be used as an input to another target
subject to the same semantics as `outs`
stdout: set to capture the stdout of the binary to a file, which can later be used as an input to another target
subject to the same semantics as `outs`
exit_code_out: set to capture the exit code of the binary to a file, which can later be used as an input to another target
subject to the same semantics as `outs`. Note that setting this will force the binary to exit 0.
If the binary creates outputs and these are declared, they must still be created
silent_on_success: produce no output on stdout nor stderr when program exits with status code 0.
This makes node binaries match the expected bazel paradigm.
copy_srcs_to_bin: When True, all srcs files are copied to the output tree that are not already there.
include_transitive_sources: see `js_filegroup` documentation
include_declarations: see `js_filegroup` documentation
include_npm_linked_packages: see `js_filegroup` documentation
log_level: Set the logging level of the `js_binary` tool.
This overrides the log level set on the `js_binary` tool target.
mnemonic: A one-word description of the action, for example, CppCompile or GoLink.
progress_message: Progress message to show to the user during the build, for example,
"Compiling foo.cc to create foo.o". The message may contain %{label}, %{input}, or
%{output} patterns, which are substituted with label string, first input, or output's
path, respectively. Prefer to use patterns instead of static strings, because the former
are more efficient.
execution_requirements: Information for scheduling the action.
For example,
```
execution_requirements = {
"no-cache": "1",
},
```
See https://docs.bazel.build/versions/main/be/common-definitions.html#common.tags for useful keys.
stamp: Whether to include build status files as inputs to the tool. Possible values:
- `stamp = 0 (default)`: Never include build status files as inputs to the tool.
This gives good build result caching.
Most tools don't use the status files, so including them in `--stamp` builds makes those
builds have many needless cache misses.
(Note: this default is different from most rules with an integer-typed `stamp` attribute.)
- `stamp = 1`: Always include build status files as inputs to the tool, even in
[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds.
This setting should be avoided, since it is non-deterministic.
It potentially causes remote cache misses for the target and
any downstream actions that depend on the result.
- `stamp = -1`: Inclusion of build status files as inputs is controlled by the
[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
Stamped targets are not rebuilt unless their dependencies change.
Default value is `0` since the majority of js_run_binary targets in a build graph typically do not use build
status files and including them for all js_run_binary actions whenever `--stamp` is set would result in
invalidating the entire graph and would prevent cache hits. Stamping is typically done in terminal targets
when building release artifacts and stamp should typically be set explicitly in these targets to `-1` so it
is enabled when the `--stamp` flag is set.
When stamping is enabled, an additional two environment variables will be set for the action:
- `BAZEL_STABLE_STATUS_FILE`
- `BAZEL_VOLATILE_STATUS_FILE`
These files can be read and parsed by the action, for example to pass some values to a bundler.
patch_node_fs: Patch the to Node.js `fs` API (https://nodejs.org/api/fs.html) for this node program
to prevent the program from following symlinks out of the execroot, runfiles and the sandbox.
When enabled, `js_binary` patches the Node.js sync and async `fs` API functions `lstat`,
`readlink`, `realpath`, `readdir` and `opendir` so that the node program being
run cannot resolve symlinks out of the execroot and the runfiles tree. When in the sandbox,
these patches prevent the program being run from resolving symlinks out of the sandbox.
When disabled, node programs can leave the execroot, runfiles and sandbox by following symlinks
which can lead to non-hermetic behavior.
**kwargs: Additional arguments
"""
# Friendly fail if user has specified data instead of srcs
data = kwargs.pop("data", None)
if data != None:
fail("Use srcs instead of data in js_run_binary: https://github.com/aspect-build/rules_js/blob/main/docs/js_run_binary.md#js_run_binary-srcs.")
extra_srcs = []
# Hoist js provider files to DefaultInfo
make_js_filegroup_target = (include_transitive_sources or
include_declarations or
include_npm_linked_packages)
if make_js_filegroup_target:
js_filegroup_name = "{}_js_filegroup".format(name)
_js_filegroup(
name = js_filegroup_name,
srcs = srcs,
include_transitive_sources = include_transitive_sources,
include_declarations = include_declarations,
include_npm_linked_packages = include_npm_linked_packages,
tags = kwargs.get("tags"),
)
extra_srcs.append(":{}".format(js_filegroup_name))
# Copy srcs to bin
if copy_srcs_to_bin:
copy_to_bin_name = "{}_copy_srcs_to_bin".format(name)
_copy_to_bin(
name = copy_to_bin_name,
srcs = srcs,
tags = kwargs.get("tags"),
)
extra_srcs.append(":{}".format(copy_to_bin_name))
# Automatically add common and useful make variables to the environment for js_run_binary build targets
extra_env = {
"BAZEL_BINDIR": "$(BINDIR)",
"BAZEL_BUILD_FILE_PATH": "$(BUILD_FILE_PATH)",
"BAZEL_COMPILATION_MODE": "$(COMPILATION_MODE)",
"BAZEL_TARGET_CPU": "$(TARGET_CPU)",
"BAZEL_TARGET": "$(TARGET)",
"BAZEL_WORKSPACE": "$(WORKSPACE)",
}
# Configure working directory to `chdir` is set
if chdir:
extra_env["JS_BINARY__CHDIR"] = chdir
# Configure capturing stdout, stderr and/or the exit code
extra_outs = []
if stdout:
extra_env["JS_BINARY__STDOUT_OUTPUT_FILE"] = "$(execpath {})".format(stdout)
extra_outs.append(stdout)
if stderr:
extra_env["JS_BINARY__STDERR_OUTPUT_FILE"] = "$(execpath {})".format(stderr)
extra_outs.append(stderr)
if exit_code_out:
extra_env["JS_BINARY__EXIT_CODE_OUTPUT_FILE"] = "$(execpath {})".format(exit_code_out)
extra_outs.append(exit_code_out)
# Configure silent on success
if silent_on_success:
extra_env["JS_BINARY__SILENT_ON_SUCCESS"] = "1"
# Disable node patches if requested
if patch_node_fs:
extra_env["JS_BINARY__PATCH_NODE_FS"] = "1"
else:
# Set explicitly to "0" so disable overrides any enable in the js_binary
extra_env["JS_BINARY__PATCH_NODE_FS"] = "0"
# Configure log_level if specified
if log_level:
for log_level_env in _envs_for_log_level(log_level):
extra_env[log_level_env] = "1"
if not stdout and not stderr and not exit_code_out and (len(outs) + len(out_dirs) < 1):
# run_binary will produce the actual error, but we want to give an additional JS-specific
# warning message here. Note that as a macro, we can't tell the name of the rule provided
# by the users BUILD file (e.g. for "typescript_bin.tsc(outs = [])" we'd wish to say
# "try using tsc_binary instead")
# buildifier: disable=print
print("""\
WARNING: {name} is not configured to produce outputs.
If this is a generated bin from package_json.bzl, consider using the *_binary variant instead.
""".format(
name = to_label(name),
))
_run_binary(
name = name,
tool = tool,
env = dicts.add(extra_env, env),
srcs = srcs + extra_srcs,
outs = outs + extra_outs,
out_dirs = out_dirs,
args = args,
mnemonic = mnemonic,
progress_message = progress_message,
execution_requirements = execution_requirements,
stamp = stamp,
**kwargs
)