Skip to content

Commit

Permalink
feat: add node_urls parameter to bzlmod toolchain in the node e…
Browse files Browse the repository at this point in the history
…xtension (#3763)

---------

Co-authored-by: Paolo Tranquilli <[email protected]>
  • Loading branch information
redsun82 and Paolo Tranquilli authored Sep 18, 2024
1 parent 20d9b25 commit 4c48449
Show file tree
Hide file tree
Showing 19 changed files with 197 additions and 33 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ jobs:
test:
uses: bazel-contrib/.github/.github/workflows/bazel.yaml@v6
with:
folders: '[".", "e2e/headers", "e2e/smoke", "e2e/nodejs_host"]'
folders: '[".", "e2e/headers", "e2e/smoke", "e2e/nodejs_host", "e2e/conflicting_toolchains"]'
# stardoc generated docs fail on diff_test with Bazel 6.4.0 so don't test against it in root repository
exclude: |
[
{"bazelversion": "6.4.0", "os": "macos-latest"},
{"bazelversion": "6.4.0", "os": "windows-latest"},
{"bazelversion": "6.4.0", folder: "."},
{"bazelversion": "6.4.0", bzlmodEnabled: true}
# this test is for bzlmod only
{folder: "e2e/conflicting_toolchains", bzlmodEnabled: false},
]
1 change: 1 addition & 0 deletions e2e/conflicting_toolchains/.bazelversion
1 change: 1 addition & 0 deletions e2e/conflicting_toolchains/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
error.txt
13 changes: 13 additions & 0 deletions e2e/conflicting_toolchains/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# this is set up to make bazel test //... pass
load("@bazel_skylib//rules:write_file.bzl", "write_file")

write_file(
name = "empty",
out = "empty.sh",
content = [],
)

sh_test(
name = "dummy",
srcs = [":empty"],
)
2 changes: 2 additions & 0 deletions e2e/conflicting_toolchains/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# this is set up to make bazel test //... pass
bazel_dep(name = "bazel_skylib", version = "1.7.1", dev_dependency = True)
22 changes: 22 additions & 0 deletions e2e/conflicting_toolchains/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# this is the integration test checking various combinations of conflicting nodejs toolchain definitions

set -eu

for test_attr in test_*; do
pushd $test_attr > /dev/null
attr=${test_attr#test_}
echo -n "testing conflict on $attr... "
if bazel mod tidy &> error.txt; then
echo "ERROR: bazel mod tidy should have failed with following MODULE.bazel:"
cat MODULE.bazel
exit 1
elif ! grep "conflicting toolchains" error.txt > /dev/null; then
echo "ERROR: expected bazel mod tidy to mention conflicting toolchains, found:"
cat error.txt
exit 1
else
echo "PASS"
fi
popd > /dev/null
done
14 changes: 14 additions & 0 deletions e2e/conflicting_toolchains/test_include_headers/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
bazel_dep(name = "rules_nodejs", version = "0.0.0", dev_dependency = True)
local_path_override(
module_name = "rules_nodejs",
path = "../../..",
)

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(
name = "mynode",
)
node.toolchain(
name = "mynode",
node_urls = ["file://whatever"],
)
1 change: 1 addition & 0 deletions e2e/conflicting_toolchains/test_node_urls/.bazelversion
14 changes: 14 additions & 0 deletions e2e/conflicting_toolchains/test_node_urls/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
bazel_dep(name = "rules_nodejs", version = "0.0.0", dev_dependency = True)
local_path_override(
module_name = "rules_nodejs",
path = "../../..",
)

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(
name = "mynode",
)
node.toolchain(
name = "mynode",
include_headers = True,
)
1 change: 1 addition & 0 deletions e2e/conflicting_toolchains/test_node_version/.bazelversion
14 changes: 14 additions & 0 deletions e2e/conflicting_toolchains/test_node_version/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
bazel_dep(name = "rules_nodejs", version = "0.0.0", dev_dependency = True)
local_path_override(
module_name = "rules_nodejs",
path = "../../..",
)

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(
name = "mynode",
)
node.toolchain(
name = "mynode",
node_version = "15.14.0",
)
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
bazel_dep(name = "rules_nodejs", version = "0.0.0", dev_dependency = True)
local_path_override(
module_name = "rules_nodejs",
path = "../../..",
)

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(
name = "mynode",
)
node.toolchain(
name = "mynode",
node_version_from_nvmrc = "//:.nvmrc",
)
25 changes: 25 additions & 0 deletions e2e/smoke/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ write_file(

# Files used in test cases later that contain the correct nodejs version
# that is imported into the workspace.
write_file(
name = "write_node_version_15",
out = "expected_node_15",
content = ["v15.14.0"],
)

write_file(
name = "write_node_version_17",
out = "expected_node_17",
Expand Down Expand Up @@ -268,3 +274,22 @@ diff_test(
file1 = "write_node_version_16",
file2 = "thing_toolchain_16",
)

my_nodejs(
name = "run_15",
out = "thing_toolchain_15",
entry_point = "version.js",
# using the select statement will download toolchains for all three platforms
# you can also just provide an individual toolchain if you don't want to download them all
toolchain = select({
"@bazel_tools//src/conditions:linux_x86_64": "@node15_linux_amd64//:toolchain",
"@bazel_tools//src/conditions:darwin": "@node15_darwin_amd64//:toolchain",
"@bazel_tools//src/conditions:windows": "@node15_windows_amd64//:toolchain",
}),
)

diff_test(
name = "test_node_version_15",
file1 = "write_node_version_15",
file2 = "thing_toolchain_15",
)
11 changes: 11 additions & 0 deletions e2e/smoke/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,21 @@ node.toolchain(
name = "node17",
node_version = "17.9.1",
)
node.toolchain(
name = "node15",
node_urls = [
"https://nodejs.org/dist/v{version}/{filename}",
"https://mirrors.dotsrc.org/nodejs/release/v{version}/{filename}",
],
node_version = "15.14.0",
)

# FIXME(6.0): a repo rule with name=foo should create a repo named @foo, not @foo_toolchains
use_repo(
node,
"node15_darwin_amd64",
"node15_linux_amd64",
"node15_windows_amd64",
"node17_darwin_amd64",
"node17_linux_amd64",
"node17_windows_amd64",
Expand Down
91 changes: 59 additions & 32 deletions nodejs/extensions.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
"extensions for bzlmod"

load(":repositories.bzl", "DEFAULT_NODE_REPOSITORY", "DEFAULT_NODE_VERSION", "nodejs_register_toolchains")
load(
":repositories.bzl",
"DEFAULT_NODE_REPOSITORY",
"DEFAULT_NODE_URL",
"DEFAULT_NODE_VERSION",
"nodejs_register_toolchains",
)

def _toolchain_repr(toolchain):
""" Return a `toolchain` tag object representation useful for diagnostics """
key_values = [(attr, getattr(toolchain, attr)) for attr in _ATTRS]
return ", ".join(["%s = %r" % (attr, value) for attr, value in key_values if value])

def _toolchains_equal(lhs, rhs):
""" Compare two `toolchain` tag objects """
for attr in _ATTRS:
if getattr(lhs, attr) != getattr(rhs, attr):
return False
return True

def _toolchain_extension(module_ctx):
registrations = {}
Expand All @@ -14,54 +32,63 @@ def _toolchain_extension(module_ctx):
# Prioritize the root-most registration of the default node toolchain version and
# ignore any further registrations (modules are processed breadth-first)
continue
if toolchain.node_version == registrations[toolchain.name].node_version and toolchain.node_version_from_nvmrc == registrations[toolchain.name].node_version_from_nvmrc:
if not _toolchains_equal(toolchain, registrations[toolchain.name]):
fail("Multiple conflicting toolchains declared:\n* {}\n* {}".format(
_toolchain_repr(toolchain),
_toolchain_repr(registrations[toolchain.name]),
))
else:
# No problem to register a matching toolchain twice
continue
fail("Multiple conflicting toolchains declared for name {} ({} and {})".format(
toolchain.name,
toolchain.node_version,
registrations[toolchain.name],
))
else:
registrations[toolchain.name] = struct(
node_version = toolchain.node_version,
node_version_from_nvmrc = toolchain.node_version_from_nvmrc,
include_headers = toolchain.include_headers,
)
registrations[toolchain.name] = toolchain

for k, v in registrations.items():
nodejs_register_toolchains(
name = k,
node_version = v.node_version,
node_version_from_nvmrc = v.node_version_from_nvmrc,
node_urls = v.node_urls,
include_headers = v.include_headers,
register = False,
)

node = module_extension(
implementation = _toolchain_extension,
tag_classes = {
"toolchain": tag_class(attrs = {
"name": attr.string(
doc = "Base name for generated repositories",
default = DEFAULT_NODE_REPOSITORY,
),
"node_version": attr.string(
doc = "Version of the Node.js interpreter",
default = DEFAULT_NODE_VERSION,
),
"node_version_from_nvmrc": attr.label(
allow_single_file = True,
doc = """The .nvmrc file containing the version of Node.js to use.
_ATTRS = {
"name": attr.string(
doc = "Base name for generated repositories",
default = DEFAULT_NODE_REPOSITORY,
),
"node_version": attr.string(
doc = "Version of the Node.js interpreter",
default = DEFAULT_NODE_VERSION,
),
"node_version_from_nvmrc": attr.label(
allow_single_file = True,
doc = """The .nvmrc file containing the version of Node.js to use.
If set then the version found in the .nvmrc file is used instead of the one specified by node_version.""",
),
"include_headers": attr.bool(
doc = """Set headers field in NodeInfo provided by this toolchain.
),
"include_headers": attr.bool(
doc = """Set headers field in NodeInfo provided by this toolchain.
This setting creates a dependency on a c++ toolchain.
""",
),
}),
),
"node_urls": attr.string_list(
doc = """List of URLs to use to download Node.js.
Each entry is a template for downloading a node distribution.
The `{version}` parameter is substituted with the `node_version` attribute,
and `{filename}` with the matching entry from the `node_repositories` attribute.
""",
default = [DEFAULT_NODE_URL],
),
}

node = module_extension(
implementation = _toolchain_extension,
tag_classes = {
"toolchain": tag_class(attrs = _ATTRS),
},
)

0 comments on commit 4c48449

Please sign in to comment.