-
Notifications
You must be signed in to change notification settings - Fork 522
Description
What is the current behavior?
Currently @rules_nodejs//nodejs:toolchain_type is covering 2 separate use cases.
- Direct usage within a rule.
- Runtime for executable outputs.
When execution and target platforms are the same (as is usually the case) scenarios 1 and 2 can be satisfied by the same toolchain implementation.
However when the execution and target platforms are different, problems arise. For example;
# From /___/external/rules_nodejs~~node~nodejs_toolchains/BUILD.bazel
toolchain(
name = "linux_amd64_toolchain",
exec_compatible_with = ["@platforms//os:linux", "@platforms//cpu:x86_64"],
toolchain = "@nodejs_linux_amd64//:toolchain",
toolchain_type = "@rules_nodejs//nodejs:toolchain_type",
)
toolchain(
name = "linux_amd64_toolchain_target",
target_compatible_with = ["@platforms//os:linux", "@platforms//cpu:x86_64"],
toolchain = "@nodejs_linux_amd64//:toolchain",
toolchain_type = "@rules_nodejs//nodejs:toolchain_type",
)
toolchain(
name = "darwin_arm64_toolchain",
exec_compatible_with = ["@platforms//os:macos", "@platforms//cpu:arm64"],
toolchain = "@nodejs_darwin_arm64//:toolchain",
toolchain_type = "@rules_nodejs//nodejs:toolchain_type",
)
toolchain(
name = "darwin_arm64_toolchain_target",
target_compatible_with = ["@platforms//os:macos", "@platforms//cpu:arm64"],
toolchain = "@nodejs_darwin_arm64//:toolchain",
toolchain_type = "@rules_nodejs//nodejs:toolchain_type",
)Suppose I am on macOS and building for Linux.
:linux_amd64_toolchainwill be skipped;- π΄
exec_compatible_with - π’
target_compatible_with(no constaints)
- π΄
:linux_amd64_toolchain_targetcan be selected;- π’
exec_compatible_with(no constaints) - π’
target_compatible_with
- π’
:darwin_arm64_toolchaincan be selected;- π’
exec_compatible_with - π’
target_compatible_with(no constaints)
- π’
darwin_arm64_toolchain_targetwill be skipped;- π’
exec_compatible_with(no constaints) - π΄
target_compatible_with
- π’
That both :linux_amd64_toolchain_target and :darwin_arm64_toolchain represents a contradiction.
Describe the feature
Starting off with an example. Rules Java has 3 toolchain types, 2 of which are of interest.
@bazel_tools//tools/jdk:toolchain_type
The compilation toolchain.
This is always run within a rule (thinkcfg = "exec") and is used to produce platform-neutral output.
Implementations useexec_compatible_withonly.@bazel_tools//tools/jdk:runtime_toolchain_type
The runtime toolchain.
This is used for outputs (e.g. fromjava_binary), which indirectly be used in other rules (as an implementation detail that it not directly executed).
Implementations usetarget_compatible_withonly.
So in Java a rule like java_binary would accept both toolchains. @bazel_tools//tools/jdk:toolchain_type for compilation and @bazel_tools//tools/jdk:runtime_toolchain_type so the output has a Java runtime to use.
This split makes it possible to cross-compile (e.g. build for Linux on macOS).
JS is not a compiled language however the Rules JS toolchains are setup to support such a scenario (a subset at least). The problem, as hinted in "What is the current behavior?", is that such cross-compilation scenarios do not work. Attempts to do so lead to one of 2 outcomes.
:*_toolchainis resolved.
- π’ Actions using
nodefrom the toolchain runs correctly. - π΄
nodein the output cannot run on the target platform.
:*_toolchain_targetis resolved.
- π΄ Actions using
nodefrom the toolchain run cannot run. - π‘
nodein the output runs correctly on the target platform, provided dependent actions do not depend on the resolvednode.
Implementing a split like what Rules Java has would address this issue, although it still leaves once use case uncovered.
NodeJS has a handful workflows that require the execution and target platforms to be identical.
- Snapshot generation (
--build-snapshotand--build-snapshot-config)
Produces a snapshot blob that can be later loaded with--snapshot-blob. Requires NodeJS version, architecture, platform, V8 flags and CPU features match what was used to generate the snapshot. - Single executable applications (
--experimental-sea-config)
This is a multi-step workflow, of which--experimental-sea-configincurs the same requirements as "snapshot generation" whenuseSnapshotoruseCodeCacheare enabled.
So, here is my proposal.
- Introduce a new toolchain type
@rules_nodejs//nodejs:runtime_toolchain_typewith the same usage semantics as@bazel_tools//tools/jdk:runtime_toolchain_type. Add implementations for this new type tonodejs_toolchainsgenerated external repository. - [[BREAKING CHANGE]] Remove
:*_toolchain_targettoolchain implementations, bringing usage semantics in line with@bazel_tools//tools/jdk:toolchain_type(ignoring that JS doesn't need to be compiled). - Introduce a new toolchain type to cover cases where execution and target platforms need to be the same. Add implementations for this new type to
nodejs_toolchainsgenerated external repository.
It may make sense to tweak the plan outlined according to what the bulk of current @rules_nodejs//nodejs:toolchain_type usage is for (I suspect right now executable outputs are the primary use case).
How existing toolchain types are supposed to be used isn't always obvious at a glance, same goes for those in Rules Java and especially the generic toolchain_type name. May be worthwhile workshopping the names.