-
-
Notifications
You must be signed in to change notification settings - Fork 167
Expand file tree
/
Copy pathjs_run_binary.bzl
More file actions
402 lines (313 loc) · 18.7 KB
/
js_run_binary.bzl
File metadata and controls
402 lines (313 loc) · 18.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
"""Runs a js_binary as a build action.
This macro wraps Aspect bazel-lib's run_binary (https://github.com/bazel-contrib/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("@bazel_lib//lib:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
load("@bazel_lib//lib:run_binary.bzl", _run_binary = "run_binary")
load("@bazel_lib//lib:utils.bzl", bazel_lib_utils = "utils")
load(":js_helpers.bzl", _envs_for_log_level = "envs_for_log_level")
load(":js_info_files.bzl", _js_info_files = "js_info_files")
load(":js_library.bzl", _js_library = "js_library")
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,
use_execroot_entry_point = True,
copy_srcs_to_bin = True,
include_sources = True,
include_types = False,
include_transitive_sources = True,
include_transitive_types = False,
include_npm_sources = True,
log_level = None,
mnemonic = "JsRunBinary",
progress_message = None,
execution_requirements = None,
stamp = 0,
patch_node_fs = True,
allow_execroot_entry_point_with_no_copy_data_to_bin = False,
use_default_shell_env = False,
**kwargs):
"""Wrapper around @bazel_lib `run_binary` that adds convenience attributes for using a `js_binary` tool.
This rule does not require Bash `native.genrule`.
The following environment variables are made available to the Node.js runtime based on available Bazel [Make variables](https://bazel.build/reference/be/make-variables#predefined_variables):
* BAZEL_BINDIR: the bazel bin directory; equivalent to the `$(BINDIR)` Make variable of the `js_run_binary` target
* BAZEL_COMPILATION_MODE: One of `fastbuild`, `dbg`, or `opt` as set by [`--compilation_mode`](https://bazel.build/docs/user-manual#compilation-mode); equivalent to `$(COMPILATION_MODE)` Make variable of the `js_run_binary` target
* BAZEL_TARGET_CPU: the target cpu architecture; equivalent to `$(TARGET_CPU)` Make variable of the `js_run_binary` target
The following environment variables are made available to the Node.js runtime based on the rule context:
* BAZEL_BUILD_FILE_PATH: the path to the BUILD file of the bazel target being run; equivalent to `ctx.build_file_path` of the `js_run_binary` target's rule context
* BAZEL_PACKAGE: the package of the bazel target being run; equivalent to `ctx.label.package` of the `js_run_binary` target's rule context
* BAZEL_TARGET_NAME: the full label of the bazel target being run; a stringified version of `ctx.label` of the `js_run_binary` target's rule context
* BAZEL_TARGET: the name of the bazel target being run; equivalent to `ctx.label.name` of the `js_run_binary` target's rule context
* BAZEL_WORKSPACE: the bazel repository name; equivalent to `ctx.workspace_name` of the `js_run_binary` target's rule context
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/bazel-contrib/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 build action in.
This overrides the chdir value if set on the `js_binary` tool target.
By default, `js_binary` tools run in the root of the output tree. For more context on why, please read the
aspect_rules_js README
https://github.com/aspect-build/rules_js/tree/dbb5af0d2a9a2bb50e4cf4a96dbc582b27567155#running-nodejs-programs.
To run in the directory containing the js_run_binary in the output tree, use
`chdir = package_name()` (or if you're in a macro, use `native.package_name()`).
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: Output file to capture the stderr of the binary to.
This can later be used as an input to another target subject to the same semantics as `outs`.
If the binary creates outputs and these are declared, they must still be created.
stdout: Output file to capture the stdout of the binary.
This can later be used as an input to another target subject to the same semantics as `outs`.
If the binary creates outputs and these are declared, they must still be created.
exit_code_out: Output file to capture the exit code of the binary to.
This 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.
use_execroot_entry_point: Use the `entry_point` script of the `js_binary` `tool` that is in the execroot output tree
instead of the copy that is in runfiles.
Runfiles of `tool` are all hoisted to `srcs` of the underlying `run_binary` so they are included as execroot
inputs to the action.
Using the entry point script that is in the execroot output tree means that there will be no conflicting
runfiles `node_modules` in the node_modules resolution path which can confuse npm packages such as next and
react that don't like being resolved in multiple node_modules trees. This more closely emulates the
environment that tools such as Next.js see when they are run outside of Bazel.
When True, the `js_binary` tool must have `copy_data_to_bin` set to True (the default) so that all data files
needed by the binary are available in the execroot output tree. This requirement can be turned off with by
setting `allow_execroot_entry_point_with_no_copy_data_to_bin` to True.
copy_srcs_to_bin: When True, all srcs files are copied to the output tree that are not already there.
include_sources: see `js_info_files` documentation
include_types: see `js_info_files` documentation
include_transitive_sources: see `js_info_files` documentation
include_transitive_types: see `js_info_files` documentation
include_npm_sources: see `js_info_files` 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.
allow_execroot_entry_point_with_no_copy_data_to_bin: Turn off validation that the `js_binary` tool
has `copy_data_to_bin` set to True when `use_execroot_entry_point` is set to True.
See `use_execroot_entry_point` doc for more info.
use_default_shell_env: Passed to the underlying ctx.actions.run.
May introduce non-determinism when True; use with care!
See e.g. https://github.com/bazelbuild/bazel/issues/4912
Refer to https://bazel.build/rules/lib/builtins/actions#run for more details.
**kwargs: Additional arguments
"""
# Friendly fail if user has specified invalid parameters
if "data" in kwargs.keys():
fail("Use srcs instead of data in js_run_binary: https://docs.aspect.build/bazel/javascript/aspect_rules_js/js_defs#function-js_run_binary")
if "deps" in kwargs.keys():
fail("Use srcs instead of deps in js_run_binary: https://docs.aspect.build/bazel/javascript/aspect_rules_js/js_defs#function-js_run_binary")
extra_srcs = []
# Hoist js provider files to DefaultInfo
make_js_info_files_target = (include_transitive_sources or
include_types or
include_npm_sources)
if make_js_info_files_target:
js_info_files_name = "{}_js_info_files".format(name)
_js_info_files(
name = js_info_files_name,
srcs = srcs,
include_sources = include_sources,
include_types = include_types,
include_transitive_sources = include_transitive_sources,
include_transitive_types = include_transitive_types,
include_npm_sources = include_npm_sources,
# Always tag the target manual since we should only build it when the final target is built.
tags = kwargs.get("tags", []) + ["manual"],
# Always propagate the testonly attribute
testonly = kwargs.get("testonly", False),
)
extra_srcs.append(":{}".format(js_info_files_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,
# Always tag the target manual since we should only build it when the final target is built.
tags = kwargs.get("tags", []) + ["manual"],
# Always propagate the testonly attribute
testonly = kwargs.get("testonly", False),
)
extra_srcs.append(":{}".format(copy_to_bin_name))
# Automatically add common and useful make variables to the environment for js_run_binary build targets
fixed_env = {
"BAZEL_BINDIR": "$(BINDIR)",
"BAZEL_BUILD_FILE_PATH": "$(BUILD_FILE_PATH)",
"BAZEL_COMPILATION_MODE": "$(COMPILATION_MODE)",
"BAZEL_PACKAGE": native.package_name(),
"BAZEL_TARGET_CPU": "$(TARGET_CPU)",
"BAZEL_TARGET_NAME": name,
"BAZEL_TARGET": "$(TARGET)",
"BAZEL_WORKSPACE": "$(WORKSPACE)",
}
# Configure working directory to `chdir` is set
if chdir:
normalized_chdir = chdir
repo = native.repo_name()
# Normalize workspace-relative chdir for external repositories so callers can pass
# native.package_name() without worrying about external/ prefixing.
# - Leave absolute paths and already-external paths untouched.
# - For external repos, prefix with "external/<repo>/".
if (
repo and
not (chdir.startswith("external/") or chdir.startswith("/")) and
not chdir.startswith("@")
):
if chdir == ".":
normalized_chdir = "external/{}".format(repo)
else:
normalized_chdir = "external/{}/{}".format(repo, chdir)
fixed_env["JS_BINARY__CHDIR"] = normalized_chdir
# Configure capturing stdout, stderr and/or the exit code
extra_outs = []
if stdout:
fixed_env["JS_BINARY__STDOUT_OUTPUT_FILE"] = "$(execpath {})".format(stdout)
extra_outs.append(stdout)
if stderr:
fixed_env["JS_BINARY__STDERR_OUTPUT_FILE"] = "$(execpath {})".format(stderr)
extra_outs.append(stderr)
if exit_code_out:
fixed_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:
fixed_env["JS_BINARY__SILENT_ON_SUCCESS"] = "1"
# Disable node patches if requested
if patch_node_fs:
fixed_env["JS_BINARY__PATCH_NODE_FS"] = "1"
else:
# Set explicitly to "0" so disable overrides any enable in the js_binary
fixed_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):
fixed_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 or *_test variant instead.
See https://github.com/aspect-build/rules_js/tree/main/docs#using-binaries-published-to-npm
""".format(
name = bazel_lib_utils.to_label(name),
))
# Configure run from execroot
if use_execroot_entry_point:
fixed_env["JS_BINARY__USE_EXECROOT_ENTRY_POINT"] = "1"
# hoist all runfiles to srcs when running from execroot
js_runfiles_lib_name = "{}_runfiles_lib".format(name)
_js_library(
name = js_runfiles_lib_name,
srcs = [tool],
# Always tag the target manual since we should only build it when the final target is built.
tags = kwargs.get("tags", []) + ["manual"],
# Always propagate the testonly attribute
testonly = kwargs.get("testonly", False),
)
js_runfiles_name = "{}_runfiles".format(name)
native.filegroup(
name = js_runfiles_name,
output_group = "runfiles",
srcs = [":{}".format(js_runfiles_lib_name)],
# Always tag the target manual since we should only build it when the final target is built.
tags = kwargs.get("tags", []) + ["manual"],
# Always propagate the testonly attribute
testonly = kwargs.get("testonly", False),
)
extra_srcs.append(":{}".format(js_runfiles_name))
if allow_execroot_entry_point_with_no_copy_data_to_bin:
fixed_env["JS_BINARY__ALLOW_EXECROOT_ENTRY_POINT_WITH_NO_COPY_DATA_TO_BIN"] = "1"
_run_binary(
name = name,
tool = tool,
env = fixed_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,
use_default_shell_env = use_default_shell_env,
**kwargs
)