Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci-workflows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ jobs:
# https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#included-storage-and-minutes
smoke:
name: smoke (${{ matrix.workspace.path }}, ${{ matrix.os }})
if: github.ref_name == 'main' || contains(github.head_ref, 'macos') || contains(github.head_ref, 'windows')
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand Down
1 change: 1 addition & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ bazel_dep(name = "bazel_features", version = "1.41.0")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "hermetic_launcher", version = "0.0.15")

# Changes ensured by rules_js:
# 3.2.2: https://github.com/bazel-contrib/bazel-lib/commit/cac2d7855949d1b222fa26888892fbbe1d31015d
Expand Down
13 changes: 13 additions & 0 deletions e2e/no_shell_toolchain/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,16 @@ toolchain(
toolchain = "@nodejs_linux_amd64//:toolchain",
toolchain_type = "@rules_nodejs//nodejs:runtime_toolchain_type",
)

# Likewise for the launcher template that js_binary stamps its node launcher from.
# hermetic_launcher ships no iOS stub; as with the Node.js runtime above, any
# platform's works here since the js_binary is only built, never run.
toolchain(
name = "launcher_template_ios_arm64_toolchain",
target_compatible_with = [
"@platforms//os:ios",
"@platforms//cpu:arm64",
],
toolchain = "@hermetic_launcher//launcher/private/prebuilt:template_x86_64_linux_prebuilt",
toolchain_type = "@hermetic_launcher//launcher:template_toolchain_type",
)
2 changes: 2 additions & 0 deletions e2e/no_shell_toolchain/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ bazel_dep(name = "aspect_rules_js", version = "0.0.0", dev_dependency = True)
bazel_dep(name = "bazel_skylib", version = "1.8.1", dev_dependency = True)
bazel_dep(name = "platforms", version = "0.0.11", dev_dependency = True)
bazel_dep(name = "rules_nodejs", version = "6.6.0", dev_dependency = True)
bazel_dep(name = "hermetic_launcher", version = "0.0.15", dev_dependency = True)

local_path_override(
module_name = "aspect_rules_js",
Expand All @@ -18,6 +19,7 @@ use_repo(node, "nodejs_linux_amd64")

register_toolchains(
"//:nodejs_ios_arm64_toolchain",
"//:launcher_template_ios_arm64_toolchain",
dev_dependency = True,
)

Expand Down
2 changes: 1 addition & 1 deletion js/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def js_binary(**kwargs):

* JS_BINARY__NODE_BINARY: the Node.js binary path run by the `js_binary` target
* JS_BINARY__NPM_BINARY: the npm binary path; this is available when `include_npm` is `True` on the `js_binary` target
* JS_BINARY__NODE_WRAPPER: the Node.js wrapper script used to run Node.js which is available as `node` on the `PATH` at runtime
* JS_BINARY__NODE_WRAPPER: the Node.js launcher binary used to run Node.js which is available as `node` on the `PATH` at runtime
* JS_BINARY__RUNFILES: the absolute path to the Bazel runfiles directory
* JS_BINARY__EXECROOT: the absolute path to the root of the execution root for the action; if in the sandbox, this path absolute path to the root of the execution root within the sandbox

Expand Down
3 changes: 1 addition & 2 deletions js/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package(default_visibility = ["//visibility:public"])

exports_files([
"js_binary.sh.tpl",
"node_bin/node",
"node_bin_windows/node.bat",
"npm_bin/npm",
"npm_bin_windows/npm.bat",
"js_image_layer.mjs",
Expand Down Expand Up @@ -50,6 +48,7 @@ bzl_library(
"@bazel_lib//lib:windows_utils",
"@bazel_skylib//lib:dicts",
"@bazel_tools//tools/build_defs/repo:cache.bzl",
"@hermetic_launcher//launcher:lib_bzl",
],
)

Expand Down
98 changes: 86 additions & 12 deletions js/private/js_binary.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ load("@bazel_lib//lib:copy_to_bin.bzl", "COPY_FILE_TO_BIN_TOOLCHAINS")
load("@bazel_lib//lib:directory_path.bzl", "DirectoryPathInfo")
load("@bazel_lib//lib:expand_make_vars.bzl", "expand_locations", "expand_variables")
load("@bazel_lib//lib:windows_utils.bzl", "create_windows_native_launcher_script")
load("@hermetic_launcher//launcher:lib.bzl", "launcher")
load(":bash.bzl", "BASH_INITIALIZE_RUNFILES")
load(":js_helpers.bzl", "LOG_LEVELS", "envs_for_log_level", "gather_files_from_js_infos", "gather_runfiles")

Expand Down Expand Up @@ -249,17 +250,8 @@ _ATTRS = {
default = Label("//js/private:js_binary.sh.tpl"),
allow_single_file = True,
),
# Windows gets its own separate directory for node and npm wrappers. This
# ensures that the bash scripts do not end up on the PATH when we build for
# Windows.
"_node_wrapper_sh": attr.label(
default = Label("//js/private:node_bin/node"),
allow_single_file = True,
),
"_node_wrapper_bat": attr.label(
default = Label("//js/private:node_bin_windows/node.bat"),
allow_single_file = True,
),
# Windows gets its own separate directory for the npm wrapper. This ensures
# that the bash script does not end up on the PATH when we build for Windows.
"_npm_wrapper_sh": attr.label(
default = Label("//js/private:npm_bin/npm"),
allow_single_file = True,
Expand Down Expand Up @@ -297,6 +289,78 @@ def _generates_coverage_report(ctx):
ctx.attr.testonly and
ctx.configuration.coverage_enabled)

# Resolved here as Labels rather than used as the bare strings hermetic_launcher
# exposes: under --incompatible_auto_exec_groups a string toolchain type is resolved
# against the repository mapping of whichever module is being built, so a consumer
# that does not itself depend on hermetic_launcher cannot resolve the name. A Label
# is resolved against this file's own mapping at load time instead.
_FINALIZER_TOOLCHAIN_TYPE = Label(launcher.finalizer_toolchain_type)
_TEMPLATE_TOOLCHAIN_TYPE = Label(launcher.template_toolchain_type)

def _compile_stub(ctx, embedded_args, transformed_args, output_file):
"""Stamps a launcher binary from the prebuilt template stub.

This is `launcher.compile_stub` reimplemented so the finalizer toolchain can be
named by Label; see the comment on _FINALIZER_TOOLCHAIN_TYPE. Drop this in favour
of the upstream helper once it takes Labels.
"""
template = ctx.toolchains[_TEMPLATE_TOOLCHAIN_TYPE].templatetoolchaininfo.template_exe
args = ctx.actions.args()
args.add("--template", template)
args.add("-o", output_file)
args.add_joined("--transform", transformed_args, join_with = ",")
args.add("--")
args.add_all(embedded_args)
ctx.actions.run(
outputs = [output_file],
executable = ctx.toolchains[_FINALIZER_TOOLCHAIN_TYPE].finalizer_info.finalizer,
arguments = [args],
inputs = [template],
toolchain = _FINALIZER_TOOLCHAIN_TYPE,
mnemonic = "JsLauncher",
progress_message = "Stamping launcher %{output}",
)

def _rlocation_path(ctx, file):
"""The runfiles-root-relative path of a file, as the launcher binaries resolve it."""
if file.short_path.startswith("../"):
return file.short_path[3:]
return "{}/{}".format(ctx.workspace_name, file.short_path)

def _compile_node_launcher(ctx, nodeinfo, output, args):
"""Compiles a launcher binary that runs node with `args` prepended to its argv.

Each entry of `args` is either a File, which is resolved through runfiles when the
launcher runs, or a string, which is embedded verbatim.
"""
if nodeinfo.node:
embedded_args, transformed_args = launcher.args_from_entrypoint(nodeinfo.node)
elif nodeinfo.node_path.startswith("/"):
# A node_toolchain may name a non-hermetic node by absolute path rather than
# provide a File. The launcher passes absolute paths through untouched, so
# there is nothing for it to resolve.
embedded_args, transformed_args = [nodeinfo.node_path], []
else:
# A relative node_path is relative to this workspace within the runfiles tree,
# matching how the launcher script resolves it.
embedded_args, transformed_args = ["{}/{}".format(ctx.workspace_name, nodeinfo.node_path)], [0]

for arg in args:
if type(arg) == "File":
embedded_args, transformed_args = launcher.append_runfile(
file = arg,
embedded_args = embedded_args,
transformed_args = transformed_args,
)
else:
embedded_args, transformed_args = launcher.append_embedded_arg(
arg = arg,
embedded_args = embedded_args,
transformed_args = transformed_args,
)

_compile_stub(ctx, embedded_args, transformed_args, output)

def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_prefix_rule, fixed_args, fixed_env, is_windows):
# Explicitly disable node fs patches on Windows:
# https://github.com/aspect-build/rules_js/issues/1137
Expand Down Expand Up @@ -390,7 +454,14 @@ def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_pre
if ctx.attr.expand_args:
fixed_args = [expand_variables(ctx, expand_locations(ctx, fixed_arg, ctx.attr.data)) for fixed_arg in fixed_args]

node_wrapper = ctx.file._node_wrapper_bat if is_windows else ctx.file._node_wrapper_sh
# Windows needs the .exe suffix for this to be found by a bare `node` on the PATH.
# The launcher script puts the parent directory on the PATH, so the launcher gets a
# directory to itself and cannot shadow the npm wrapper sitting next to it.
node_wrapper = ctx.actions.declare_file("{}_/node_bin/node{}".format(
ctx.label.name,
".exe" if is_windows else "",
))
_compile_node_launcher(ctx, nodeinfo, node_wrapper, ["--require", ctx.file._node_patches])
toolchain_files = [node_wrapper]

npm_path = ""
Expand Down Expand Up @@ -421,6 +492,7 @@ def _bash_launcher(ctx, nodeinfo, entry_point_path, log_prefix_rule_set, log_pre
"{{log_prefix_rule}}": log_prefix_rule,
"{{node_options}}": "\n".join(node_options),
"{{node_patches}}": ctx.file._node_patches.short_path,
"{{node_patches_rlocation}}": _rlocation_path(ctx, ctx.file._node_patches),
"{{node_wrapper}}": node_wrapper.short_path,
"{{node}}": node_path,
"{{npm}}": npm_path,
Expand Down Expand Up @@ -625,6 +697,8 @@ js_binary_lib = struct(
# Optional: only referenced on Windows
config_common.toolchain_type("@bazel_tools//tools/sh:toolchain_type", mandatory = False),
"@rules_nodejs//nodejs:runtime_toolchain_type",
_FINALIZER_TOOLCHAIN_TYPE,
_TEMPLATE_TOOLCHAIN_TYPE,
] + COPY_FILE_TO_BIN_TOOLCHAINS,
)

Expand Down
9 changes: 7 additions & 2 deletions js/private/js_binary.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ fi

if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then
export JS_BINARY__NODE_WRAPPER
JS_BINARY__NODE_WRAPPER=$(resolve_execroot_src_path "{{node_wrapper}}")
JS_BINARY__NODE_WRAPPER=$(resolve_execroot_bin_path "{{node_wrapper}}")
else
export JS_BINARY__NODE_WRAPPER="$JS_BINARY__RUNFILES/{{workspace_name}}/{{node_wrapper}}"
fi
Expand All @@ -353,7 +353,12 @@ if [ "${JS_BINARY__NO_RUNFILES:-}" ]; then
export JS_BINARY__NODE_PATCHES
JS_BINARY__NODE_PATCHES=$(resolve_execroot_src_path "{{node_patches}}")
else
export JS_BINARY__NODE_PATCHES="$JS_BINARY__RUNFILES/{{workspace_name}}/{{node_patches}}"
# Spelled as a runfiles-root-relative path rather than via
# "{{workspace_name}}/../", so that this is byte-for-byte the path the node
# launcher binary passes to --require. A child process inherits this one in
# execArgv and gets the launcher's as well; if the two spellings differ, node
# loads the patches twice and the second application fails.
export JS_BINARY__NODE_PATCHES="$JS_BINARY__RUNFILES/{{node_patches_rlocation}}"
fi
if [ ! -f "$JS_BINARY__NODE_PATCHES" ]; then
logf_fatal "node patches '%s' not found" "$JS_BINARY__NODE_PATCHES"
Expand Down
5 changes: 0 additions & 5 deletions js/private/node_bin/node

This file was deleted.

3 changes: 0 additions & 3 deletions js/private/node_bin_windows/node.bat

This file was deleted.

11 changes: 10 additions & 1 deletion js/private/test/image/asserts.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ SKIP_ON_WINDOWS = select({
# js_binary launcher scripts have unstable sizes across Bazel versions.
_UNSTABLE_SIZE_BASENAMES = ["bin", "bin2"]

# The node launcher binary is stamped from a prebuilt hermetic_launcher stub, so its
# size moves with the stub's platform and version. Matched by path rather than
# basename so the real node binary's size stays pinned.
_UNSTABLE_SIZE_PATHS = ["node_bin/node"]

# buildifier: disable=function-docstring
def assert_tar_listing(name, actual, expected):
launcher_alt = "|".join(_UNSTABLE_SIZE_BASENAMES)
stub_alt = "|".join([p.replace("/", "\\/") for p in _UNSTABLE_SIZE_PATHS])

# `$$` escapes `$` for Bazel genrule cmd Make-variable expansion.
size_sanitize = "sed -E '/\\/({})$$/ s/[0-9]+ Jan/xxxxx Jan/'".format(launcher_alt)
size_sanitize = (
"sed -E -e '/\\/({})$$/ s/[0-9]+ Jan/xxxxx Jan/'".format(launcher_alt) +
" -e '/_\\/({})$$/ s/[0-9]+ Jan/xxxxx Jan/'".format(stub_alt)
)

# Normalize bzlmod canonical repo separators: ~~/~ (Bazel 7) -> ++/+ (Bazel 8+).
repo_sep_normalize = (
Expand Down
4 changes: 2 additions & 2 deletions js/private/test/image/custom_owner_test_app.listing
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runf
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node_bin/
-r-xr-xr-x 0 100 0 133 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node_bin/node
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image-fixture-a/
Expand All @@ -17,4 +15,6 @@ drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runf
-r-xr-xr-x 0 100 0 168 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image-fixture-d/package.json
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/
-r-xr-xr-x 0 100 0 xxxxx Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin
drwxr-xr-x 0 100 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/node_bin/
-r-xr-xr-x 0 100 0 xxxxx Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/node_bin/node
-r-xr-xr-x 0 100 0 20 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/main.js
4 changes: 2 additions & 2 deletions js/private/test/image/default_test_app.listing
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runf
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node_bin/
-r-xr-xr-x 0 0 0 133 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/node_bin/node
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image-fixture-a/
Expand All @@ -17,4 +15,6 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runf
-r-xr-xr-x 0 0 0 168 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image-fixture-d/package.json
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/
-r-xr-xr-x 0 0 0 xxxxx Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/bin
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/node_bin/
-r-xr-xr-x 0 0 0 xxxxx Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/bin_/node_bin/node
-r-xr-xr-x 0 0 0 20 Jan 1 1970 ./js/private/test/image/bin.runfiles/_main/js/private/test/image/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node_bin/
-r-xr-xr-x 0 0 0 133 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/node_bin/node
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/
-r-xr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/ㅑㅕㅣㅇ.ㄴㅅ
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/
-r-xr-xr-x 0 0 0 xxxxx Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/bin2
drwxr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/node_bin/
-r-xr-xr-x 0 0 0 xxxxx Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/bin2_/node_bin/node
-r-xr-xr-x 0 0 0 0 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/empty empty.ㄴㅅ
-r-xr-xr-x 0 0 0 20 Jan 1 1970 ./app/js/private/test/image/non_ascii/bin2.runfiles/_main/js/private/test/image/non_ascii/main.js
Loading