feat(builtin): expose a concrete toolchain for rules that don't under… (#2960)
diff --git a/e2e/core/BUILD.bazel b/e2e/core/BUILD.bazel index 7703257..6b991ce 100644 --- a/e2e/core/BUILD.bazel +++ b/e2e/core/BUILD.bazel
@@ -9,6 +9,12 @@ content = ["require('fs').writeFileSync(process.argv[2], 'stuff')"], ) +write_file( + name = "write_expected", + out = "expected", + content = ["stuff"], +) + # This technique can be used to directly grab a node binary as a label, however it has the # downside that analysis phase on this select() statement will cause an eager fetch of all # the platforms and therefore download a bunch of node binaries. @@ -34,37 +40,23 @@ # tools = [":node_bin"], # ) -# In theory, you can use the node toolchain together with a genrule(). -# However the genrule implementation doesn't perform toolchain resolution. -# See this comment from Jay Conrod about a similar question for rules_go -# https://github.com/bazelbuild/rules_go/issues/2255#issuecomment-545478712 -# That means you must resolve the toolchain yourself, with a select() -# and that falls down the same deoptimization described above: -# it will eager-fetch node for all platforms. -# So instead we recommend always writing a custom rule to access the node binary. -# alias( -# name = "node_toolchain", -# actual = select({ -# "@bazel_tools//src/conditions:darwin_arm64": "@node16_darwin_arm64//:node_toolchain", -# "@bazel_tools//src/conditions:darwin_x86_64": "@node16_darwin_amd64//:node_toolchain", -# "@bazel_tools//src/conditions:linux_aarch64": "@node16_linux_arm64//:node_toolchain", -# "@bazel_tools//src/conditions:linux_s390x": "@node16_linux_s390x//:node_toolchain", -# "@bazel_tools//src/conditions:linux_x86_64": "@node16_linux_amd64//:node_toolchain", -# "@bazel_tools//src/conditions:linux_ppc64le": "@node16_linux_ppc64le//:node_toolchain", -# "@bazel_tools//src/conditions:windows": "@node16_windows_amd64//:node_toolchain", -# "//conditions:default": "@node16_linux_amd64//:node_toolchain", -# }), -# ) -# genrule( -# name = "use_node_toolchain", -# srcs = ["some.js"], -# outs = ["thing2"], -# cmd = "$(NODE_PATH) $(execpath some.js) $@", -# toolchains = [":node_toolchain"], -# # It will also fail to include the files from the node_toolchain, so you're -# # forced to repeat the label of the node binary as an explicit input. -# tools = ["@node16_host//:node_bin"], -# ) +# You can use the node toolchain together with a genrule(). +# This gives you complete control over starting the interpreter, but you also have to +# manually handle module resolution. +genrule( + name = "use_node_toolchain", + srcs = ["some.js"], + outs = ["genrule_out"], + cmd = "$(NODE_PATH) $(execpath some.js) $@", + toolchains = ["@node16_toolchains//:resolved_toolchain"], + tools = ["@node16_toolchains//:resolved_toolchain"], +) + +diff_test( + name = "test_genrule", + file1 = "expected", + file2 = "genrule_out", +) # Here, my_nodejs is a fake for something like nodejs_binary or # some other custom rule that runs node. @@ -75,15 +67,8 @@ ) # Assert that the node program wrote the file we expect -write_file( - name = "write_expected", - out = "expected", - content = ["stuff"], -) - diff_test( - name = "test", + name = "test_custom_rule", file1 = "expected", file2 = "thing", ) -# end Assert
diff --git a/nodejs/private/toolchains_repo.bzl b/nodejs/private/toolchains_repo.bzl index 3e731f5..9dcdc60 100644 --- a/nodejs/private/toolchains_repo.bzl +++ b/nodejs/private/toolchains_repo.bzl
@@ -62,12 +62,42 @@ ), } -def _impl(repository_ctx): +def _toolchains_repo_impl(repository_ctx): + # Expose a concrete toolchain which is the result of Bazel resolving the toolchain + # for the execution or target platform. + # Workaround for https://github.com/bazelbuild/bazel/issues/14009 + starlark_content = """# Generated by toolchains_repo.bzl + +# Forward all the providers +def _resolved_toolchain_impl(ctx): + toolchain_info = ctx.toolchains["@rules_nodejs//nodejs:toolchain_type"] + return [ + toolchain_info, + toolchain_info.default, + toolchain_info.nodeinfo, + toolchain_info.template_variables, + ] + +# Copied from java_toolchain_alias +# https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl +resolved_toolchain = rule( + implementation = _resolved_toolchain_impl, + toolchains = ["@rules_nodejs//nodejs:toolchain_type"], + incompatible_use_toolchain_transition = True, +) +""" + repository_ctx.file("defs.bzl", starlark_content) + build_content = """# Generated by toolchains_repo.bzl # # These can be registered in the workspace file or passed to --extra_toolchains flag. # By default all these toolchains are registered by the nodejs_register_toolchains macro # so you don't normally need to interact with these targets. + +load(":defs.bzl", "resolved_toolchain") + +resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"]) + """ for [platform, meta] in PLATFORMS.items(): @@ -90,7 +120,7 @@ repository_ctx.file("BUILD.bazel", build_content) toolchains_repo = repository_rule( - _impl, + _toolchains_repo_impl, doc = """Creates a repository with toolchain definitions for all known platforms which can be registered or selected.""", attrs = {
diff --git a/nodejs/toolchain.bzl b/nodejs/toolchain.bzl index b48f432..f8beb6d 100644 --- a/nodejs/toolchain.bzl +++ b/nodejs/toolchain.bzl
@@ -45,23 +45,31 @@ tool_files = ctx.attr.target_tool.files.to_list() target_tool_path = _to_manifest_path(ctx, tool_files[0]) + # Make the $(NODE_PATH) variable available in places like genrules. + # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables + template_variables = platform_common.TemplateVariableInfo({ + "NODE_PATH": target_tool_path, + }) + default = DefaultInfo( + files = depset(tool_files), + runfiles = ctx.runfiles(files = tool_files), + ) + nodeinfo = NodeInfo( + target_tool_path = target_tool_path, + tool_files = tool_files, + ) + + # Export all the providers inside our ToolchainInfo + # so the resolved_toolchain rule can grab and re-export them. + toolchain_info = platform_common.ToolchainInfo( + nodeinfo = nodeinfo, + template_variables = template_variables, + default = default, + ) return [ - DefaultInfo( - runfiles = ctx.runfiles(files = tool_files), - ), - platform_common.ToolchainInfo( - nodeinfo = NodeInfo( - target_tool_path = target_tool_path, - tool_files = tool_files, - ), - ), - # Make the $(NODE_PATH) variable available in places like genrules. - # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables - # Note that genrule seems to have a bug: this only works if the node_toolchain target - # itself is used by the toolchains attribute of genrule, not the toolchain_type. - platform_common.TemplateVariableInfo({ - "NODE_PATH": target_tool_path, - }), + default, + toolchain_info, + template_variables, ] node_toolchain = rule(