Skip to content
Open
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
55 changes: 54 additions & 1 deletion e2e/smoke/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load(":defs.bzl", "my_nodejs")
load("@rules_nodejs//nodejs:toolchain.bzl", "nodejs_toolchain")
load(":defs.bzl", "check_node_data", "my_nodejs")

not_windows = select({
# There isn't a published rosetta binary for windows as of Feb 2024
Expand Down Expand Up @@ -339,3 +340,55 @@ diff_test(
file1 = "write_node_version_24",
file2 = "thing_toolchain_24",
)

################################################
# Test that node_data files are available in the sandbox

write_file(
name = "write_companion",
out = "companion.txt",
content = ["companion_data"],
)

# checks that each path in argv[2..] exists, writes "found" or "not_found"
write_file(
name = "write_check_node_data",
out = "check_node_data.js",
content = [
"const fs = require('fs');",
"const paths = process.argv.slice(3);",
"const allExist = paths.every(p => fs.existsSync(p));",
"fs.writeFileSync(process.argv[2], allExist ? 'found' : 'not_found');",
],
)

write_file(
name = "write_expected_found",
out = "expected_found",
content = ["found"],
)

nodejs_toolchain(
name = "node_with_data_impl",
node = select({
"@bazel_tools//src/conditions:linux_x86_64": "@nodejs_linux_amd64//:node_bin",
"@bazel_tools//src/conditions:linux_aarch64": "@nodejs_linux_arm64//:node_bin",
"@bazel_tools//src/conditions:darwin_x86_64": "@nodejs_darwin_amd64//:node_bin",
"@bazel_tools//src/conditions:darwin_arm64": "@nodejs_darwin_arm64//:node_bin",
"@bazel_tools//src/conditions:windows": "@nodejs_windows_amd64//:node_bin",
}),
node_data = [":companion.txt"],
)

check_node_data(
name = "run_node_data",
out = "node_data_result",
entry_point = "check_node_data.js",
toolchain = ":node_with_data_impl",
)

diff_test(
name = "test_node_data",
file1 = "expected_found",
file2 = "node_data_result",
)
37 changes: 36 additions & 1 deletion e2e/smoke/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ def _my_nodejs_impl(ctx):
nodeinfo = ctx.attr.toolchain[platform_common.ToolchainInfo].nodeinfo
else:
nodeinfo = ctx.toolchains["@rules_nodejs//nodejs:toolchain_type"].nodeinfo

inputs = depset(
[ctx.file.entry_point],
transitive = [nodeinfo.node_data] if hasattr(nodeinfo, "node_data") else None,
)

ctx.actions.run(
inputs = [ctx.file.entry_point],
inputs = inputs,
executable = nodeinfo.node,
arguments = [ctx.file.entry_point.path, ctx.outputs.out.path],
outputs = [ctx.outputs.out],
Expand All @@ -22,3 +28,32 @@ my_nodejs = rule(
},
toolchains = ["@rules_nodejs//nodejs:toolchain_type"],
)

def _check_node_data_impl(ctx):
"""Test rule that verifies node_data files are present in the action sandbox."""
nodeinfo = ctx.attr.toolchain[platform_common.ToolchainInfo].nodeinfo

node_data = nodeinfo.node_data if hasattr(nodeinfo, "node_data") else depset()
node_data_list = node_data.to_list()
if not node_data_list:
fail("Expected node_data to contain files")

# Pass each node_data file path as an arg so the JS script can check existence
args = [ctx.file.entry_point.path, ctx.outputs.out.path] + [f.path for f in node_data_list]

ctx.actions.run(
inputs = depset([ctx.file.entry_point], transitive = [node_data]),
executable = nodeinfo.node,
arguments = args,
outputs = [ctx.outputs.out],
)
return []

check_node_data = rule(
implementation = _check_node_data_impl,
attrs = {
"entry_point": attr.label(allow_single_file = True),
"out": attr.output(),
"toolchain": attr.label(mandatory = True),
},
)
10 changes: 9 additions & 1 deletion nodejs/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ For backward compability, if set then npm_path will be set to the runfiles path

For backward compability, npm_path is set to the runfiles path of npm if npm is set.
""",
"node_data": """Additional runtime files required by the Node.js executable (depset of Files).""",
"npm_sources": """Additional source files required to run npm""",
"headers": """Optional.\

Expand Down Expand Up @@ -86,14 +87,16 @@ def _nodejs_toolchain_impl(ctx):
"NPM_PATH": ctx.file.npm.path if ctx.attr.npm else ctx.attr.npm_path,
})
files = [f for f in [ctx.file.node, ctx.file.npm] if f]
node_data = depset(ctx.files.node_data)
default = DefaultInfo(
files = depset(files),
runfiles = ctx.runfiles(files = files),
runfiles = ctx.runfiles(files = files, transitive_files = node_data),
)
npm_sources = depset([ctx.file.npm] + ctx.files.npm_srcs)
nodeinfo = NodeInfo(
node = ctx.file.node,
node_path = ctx.attr.node_path,
node_data = node_data,
npm = ctx.file.npm,
npm_path = ctx.attr.npm_path if ctx.attr.npm_path else (_to_manifest_path(ctx, ctx.file.npm) if ctx.file.npm else ""), # _to_manifest_path for backward compat
npm_sources = npm_sources,
Expand Down Expand Up @@ -131,6 +134,7 @@ _nodejs_toolchain = rule(
allow_single_file = True,
),
"node_path": attr.string(),
"node_data": attr.label_list(allow_files = True),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve node_data in exec configuration

The new node_data attribute is declared without cfg = "exec", while node is explicitly exec-configured. In toolchain rules this causes node_data labels/selects to resolve in the target configuration, so cross-platform builds can pair an exec-platform Node binary with target-platform companion files (for example, host x86_64 tool with arm64-target node_data), which can break wrapper scripts or native runtime dependencies at execution time.

Useful? React with 👍 / 👎.

"npm": attr.label(allow_single_file = True),
"npm_path": attr.string(),
"npm_srcs": attr.label_list(),
Expand All @@ -142,6 +146,7 @@ def nodejs_toolchain(
name,
node = None,
node_path = "",
node_data = [],
npm = None,
npm_path = "",
npm_srcs = [],
Expand Down Expand Up @@ -206,6 +211,8 @@ def nodejs_toolchain(

Only one of `node` and `node_path` may be set.

node_data: Additional runtime files required by the Node.js executable.

npm: Npm JavaScript entry point

npm_path: Path to npm JavaScript entry point.
Expand Down Expand Up @@ -252,6 +259,7 @@ WARNING: npm_files attribute of nodejs_toolchain is deprecated; use npm_srcs ins
name = name,
node = node,
node_path = node_path,
node_data = node_data,
npm = npm,
npm_path = npm_path,
npm_srcs = npm_srcs,
Expand Down
Loading