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
26 changes: 15 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,6 @@ jobs:
'deps(@v8//:icu/d8) + deps(@nodejs_icu_26_3_1//:icu) + deps(@nodejs_crates_26_3_1//:cargo_files)'
>/dev/null

- name: Test consumer source contract
working-directory: tests/integration
run: |
bazel test \
--config=ci \
--config=remote \
--jobs=64 \
--lockfile_mode=error \
//:source_contract_test

- name: Build bundled ICU
run: |
bazel build \
Expand All @@ -54,6 +44,21 @@ jobs:
fi
grep -F 'Unsupported Node.js version "26.3.2"; supported versions are 26.3.1' <<<"$output"

consumer-integration:
name: Consumer integration
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Test source-built Node.js toolchains
working-directory: tests/integration
run: |
bazel test \
--config=ci \
--config=release \
--lockfile_mode=error \
//...

windows-x86_64:
name: Windows x86_64 remote build / Node.js 26.3.1
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -182,5 +187,4 @@ jobs:
"--strategy=TestRunner=${{ matrix.system.test_strategy }}" \
-- \
//tests/... \
-//tests/integration/... \
"${{ matrix.system.upstream_target }}"
8 changes: 7 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ bazel_dep(name = "llvm", version = "0.8.8")
bazel_dep(name = "platforms", version = "1.1.0")
bazel_dep(name = "rules_cc", version = "0.2.19")
bazel_dep(name = "rules_cc_autoconf", version = "0.12.1")
bazel_dep(name = "rules_nodejs", version = "6.7.5")
bazel_dep(name = "rules_python", version = "1.7.0")
bazel_dep(name = "rules_rs", version = "0.0.86")
bazel_dep(name = "rules_shell", version = "0.6.1")
Expand Down Expand Up @@ -115,7 +116,12 @@ register_toolchains(

nodejs = use_extension("//nodejs:extensions.bzl", "nodejs", dev_dependency = True)
nodejs.version(version = "26.3.1")
use_repo(nodejs, "nodejs_26_3_1", "nodejs_crates_26_3_1", "nodejs_icu_26_3_1", "v8")
use_repo(nodejs, "nodejs_26_3_1", "nodejs_crates_26_3_1", "nodejs_icu_26_3_1", "nodejs_toolchains", "v8")

register_toolchains(
"@nodejs_toolchains//:all",
dev_dependency = True,
)

nodejs_crates = use_extension("@rules_rs//rs:extensions.bzl", "crate", dev_dependency = True)
nodejs_crates.from_cargo(
Expand Down
3 changes: 3 additions & 0 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ The Windows build runs compilation and linking on remote Linux workers. CI
downloads the resulting `node.exe` and executes it on native Windows x86_64
and Windows arm64 runners.

## Consumer toolchains

The module exposes `rules_nodejs` execution and runtime toolchains backed by
the source-built Node.js binary. Consumers select the release and register the
stable toolchain repository:

```starlark
bazel_dep(name = "nodejs", version = "0.0.0")

nodejs = use_extension("@nodejs//nodejs:extensions.bzl", "nodejs")
nodejs.version(version = "26.3.1")
use_repo(nodejs, "nodejs_26_3_1", "nodejs_toolchains")

register_toolchains("@nodejs_toolchains//:all")
```

`@nodejs_toolchains` provides both `@rules_nodejs//nodejs:toolchain_type` for
build actions and `@rules_nodejs//nodejs:runtime_toolchain_type` for programs
that run on the selected target platform. Linux and macOS x86_64 and arm64 are
currently registered.

## Source and configuration

`@nodejs_26_3_1` contains the pinned Node.js source and the generated release
Expand Down
1 change: 1 addition & 0 deletions REPO.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignore_directories(["tests/integration"])
14 changes: 12 additions & 2 deletions nodejs.BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ cc_binary(
"-Wl,--export-dynamic",
"-Wl,--hash-style=sysv",
],
":target_macos": [],
":target_macos": ["-Wl,-export_dynamic"],
":target_windows": [
"$(location :node_windows_manifest_resource)",
"$(location :node_windows_resource)",
Expand Down Expand Up @@ -817,9 +817,19 @@ cc_library(
"deps/uv/include",
"src",
],
deps = ["//deps/v8:node_addon_headers"],
deps = [
":openssl_addon_headers",
"//deps/v8:node_addon_headers",
],
)

filegroup(
name = "npm_files",
srcs = glob(["deps/npm/**"]),
)

exports_files(["deps/npm/bin/npm-cli.js"])

cc_library(
name = "js_native_api_test_headers",
hdrs = [
Expand Down
1 change: 1 addition & 0 deletions nodejs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
exports_files([
"compiler_options.bzl",
"extensions.bzl",
"toolchain.bzl",
])
10 changes: 9 additions & 1 deletion nodejs/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load(
"nodejs_doc_dependencies_repository",
"nodejs_icu_repository",
"nodejs_source_repository",
"nodejs_toolchains_repository",
"nodejs_v8_repository",
)
load("//nodejs/private:versions.bzl", "NODEJS_RELEASES")
Expand Down Expand Up @@ -96,6 +97,13 @@ def _nodejs_impl(module_ctx):
urls = release.urls,
)

if len(requested_versions) == 1:
selected_version = sorted(requested_versions.keys())[0]
nodejs_toolchains_repository(
name = "nodejs_toolchains",
nodejs_repository_name = NODEJS_RELEASES[selected_version].repository_name,
)

root_direct_deps = [
repository_name
for version in sorted(root_requested_versions.keys())
Expand All @@ -105,7 +113,7 @@ def _nodejs_impl(module_ctx):
NODEJS_RELEASES[version].icu_repository_name,
NODEJS_RELEASES[version].v8_repository_name,
]
]
] + (["nodejs_toolchains"] if len(root_requested_versions) == 1 else [])
root_direct_dev_deps = []
if not module_ctx.root_module_has_non_dev_dependency:
root_direct_dev_deps = root_direct_deps
Expand Down
64 changes: 64 additions & 0 deletions nodejs/private/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,67 @@ nodejs_icu_repository = repository_rule(
"urls": attr.string_list(mandatory = True),
},
)

_TOOLCHAIN_PLATFORMS = [
("linux", "aarch64"),
("linux", "x86_64"),
("macos", "aarch64"),
("macos", "x86_64"),
]

def _nodejs_toolchains_repository_impl(repository_ctx):
repository_name = repository_ctx.attr.nodejs_repository_name
implementation = """load("@nodejs//nodejs:toolchain.bzl", "source_nodejs_toolchain")

package(default_visibility = ["//visibility:public"])

source_nodejs_toolchain(
name = "execution_toolchain",
headers = "@{repository_name}//:node_addon_headers",
node = "@{repository_name}//:node",
npm = "@{repository_name}//:deps/npm/bin/npm-cli.js",
npm_srcs = ["@{repository_name}//:npm_files"],
)

source_nodejs_toolchain(
name = "runtime_toolchain",
headers = "@{repository_name}//:node_addon_headers",
node = "@{repository_name}//:node",
npm = "@{repository_name}//:deps/npm/bin/npm-cli.js",
npm_srcs = ["@{repository_name}//:npm_files"],
)
""".format(repository_name = repository_name)

registrations = []
for os, cpu in _TOOLCHAIN_PLATFORMS:
registrations.append("""
toolchain(
name = "nodejs_{os}_{cpu}",
exec_compatible_with = [
"@platforms//os:{os}",
"@platforms//cpu:{cpu}",
],
toolchain = ":execution_toolchain",
toolchain_type = "@rules_nodejs//nodejs:toolchain_type",
)

toolchain(
name = "nodejs_runtime_{os}_{cpu}",
target_compatible_with = [
"@platforms//os:{os}",
"@platforms//cpu:{cpu}",
],
toolchain = ":runtime_toolchain",
toolchain_type = "@rules_nodejs//nodejs:runtime_toolchain_type",
)
""".format(cpu = cpu, os = os))

repository_ctx.file("BUILD.bazel", implementation + "".join(registrations))

nodejs_toolchains_repository = repository_rule(
implementation = _nodejs_toolchains_repository_impl,
attrs = {
"nodejs_repository_name": attr.string(mandatory = True),
},
doc = "Creates rules_nodejs toolchains backed by a source-built Node.js repository.",
)
64 changes: 64 additions & 0 deletions nodejs/toolchain.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""rules_nodejs runtime toolchain support for source-built Node.js."""

load("@rules_cc//cc:defs.bzl", "CcInfo")
load("@rules_nodejs//nodejs:toolchain.bzl", "NodeInfo")

def _manifest_path(ctx, file):
if file.short_path.startswith("../"):
return "external/" + file.short_path[3:]
return ctx.workspace_name + "/" + file.short_path

def _source_nodejs_toolchain_impl(ctx):
node = ctx.executable.node
npm = ctx.file.npm
npm_sources = depset(([npm] if npm else []) + ctx.files.npm_srcs)
files = [node] + ([npm] if npm else [])
default = DefaultInfo(
files = depset(files),
runfiles = ctx.runfiles(files = files, transitive_files = npm_sources),
)
nodeinfo = NodeInfo(
headers = struct(
providers_map = {
"CcInfo": ctx.attr.headers[CcInfo],
"DefaultInfo": ctx.attr.headers[DefaultInfo],
},
),
node = node,
node_path = "",
npm = npm,
npm_files = npm_sources.to_list(),
npm_path = _manifest_path(ctx, npm) if npm else "",
npm_sources = npm_sources,
target_tool_path = _manifest_path(ctx, node),
tool_files = [node],
)
template_variables = platform_common.TemplateVariableInfo({
"NODE_PATH": node.path,
"NPM_PATH": npm.path if npm else "",
})
return [
default,
platform_common.ToolchainInfo(
default = default,
nodeinfo = nodeinfo,
template_variables = template_variables,
),
template_variables,
]

source_nodejs_toolchain = rule(
implementation = _source_nodejs_toolchain_impl,
attrs = {
"headers": attr.label(mandatory = True),
"node": attr.label(
allow_single_file = True,
cfg = "target",
executable = True,
mandatory = True,
),
"npm": attr.label(allow_single_file = True),
"npm_srcs": attr.label_list(),
},
doc = "Provides source-built Node.js in the toolchain implementation's configuration.",
)
68 changes: 68 additions & 0 deletions tests/integration/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,73 @@
load(":execution_toolchain_check.bzl", "node_execution_check")
load("@aspect_rules_js//js:defs.bzl", "js_test")
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load("@rules_shell//shell:sh_test.bzl", "sh_test")

cc_binary(
name = "consumer_addon_shared",
srcs = ["consumer_addon.cc"],
defines = ["NODE_GYP_MODULE_NAME=consumer_addon"],
linkopts = select({
"@platforms//os:macos": ["-Wl,-undefined,dynamic_lookup"],
"//conditions:default": [],
}),
linkshared = True,
target_compatible_with = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = ["@rules_nodejs//nodejs/headers:current_node_cc_headers"],
)

genrule(
name = "consumer_addon",
srcs = [":consumer_addon_shared"],
outs = ["consumer_addon.node"],
cmd = "cp $< $@",
target_compatible_with = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)

js_test(
name = "runtime_toolchain_test",
args = ["$(rootpath :consumer_addon)"],
data = [":consumer_addon"],
entry_point = "runtime_toolchain_test.js",
include_npm = True,
target_compatible_with = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)

node_execution_check(
name = "execution_toolchain_check",
out = "execution_toolchain_version.txt",
script = "execution_toolchain_check.js",
target_compatible_with = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)

sh_test(
name = "execution_toolchain_test",
srcs = ["execution_toolchain_test.sh"],
args = ["$(rootpath :execution_toolchain_check)"],
data = [":execution_toolchain_check"],
target_compatible_with = select({
"@platforms//os:linux": [],
"@platforms//os:macos": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
)

sh_test(
name = "source_contract_test",
srcs = ["source_contract_test.sh"],
Expand Down
Loading
Loading