rules_java icon indicating copy to clipboard operation
rules_java copied to clipboard

Java toolchain is not respected

Open swarren12 opened this issue 8 months ago • 2 comments

It seems that --java_language_version and related flags are not respected by rules_java.

Expected behaviour: setting the following flags in .bazelrc should build using a Java 17 JDK:

build --java_language_version=17
build --tool_java_language_version=17
build --java_runtime_version=remotejdk_17
build --tool_java_runtime_version=remotejdk_17

Actual behaviour: the build happens with a Java 21 JDK.

Steps to reproduce: see https://github.com/swarren12/example-rules_java.

Specifically: include rules_java by way of bzlmod:

bazel_dep(name = "rules_java", version = "7.0.6", dev_dependency = True)

Create a java_library with sources using Java 17 preview features:

java_library(
    name = "example",
    srcs = glob(["src/main/java/**/*.java"]),
)

Set the properties above in .bazelrc. Attempt to build the code using bazel build //:example.

The build will fail due to:

src/main/java/com/example/Main.java:10: error: patterns in switch statements are not supported in -source 17

Adding the build --javacopt="--enable-preview" property to .bazelrc and re-building causes the following error:

ERROR: BUILD.bazel:4:13: Building libexample.jar (1 source file) failed: (Exit 1): java failed: error executing command (from target //:example) external/rules_java~7.0.6~toolchains~remotejdk21_linux/bin/java '--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED' '--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED' ... (remaining 19 arguments skipped)
error: invalid source release 17 with --enable-preview

We can see that the build is actually happening on remotejdk21_linux rather than remotejdk17_linux as would be expected.

Why this is important: trying to convert a Gradle project to Bazel and prove correctness. The Gradle project builds on JDK 17 using --enable-preview. If we attempt to set --enable-preview on here, the compiler complains that --enable-preview can only be used with "release 21". Without setting --enable-preview, preview features cannot be used and so the project cannot be migrated "like-for-like" without first being upgraded to Java 21.

Running the build with --toolchain_resolution_debug='@bazel_tools//tools/jdk:runtime_toolchain_type shows the following:

INFO: Build option --toolchain_resolution_debug has changed, discarding analysis cache.
...
INFO: ToolchainResolution:   Type @bazel_tools//tools/jdk:runtime_toolchain_type: target platform @local_config_platform//:host: execution @local_config_platform//:host: Selected toolchain @rules_java~7.0.6~toolchains~remotejdk17_linux//:jdk
...
INFO: ToolchainResolution:     Type @bazel_tools//tools/jdk:runtime_toolchain_type: target platform @local_config_platform//:host: execution platform @local_config_platform//:host: Skipping toolchain @remotejdk17_linux//:jdk; execution platform already has selected toolchain
...
INFO: ToolchainResolution: Target platform @local_config_platform//:host: Selected execution platform @local_config_platform//:host, type @bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @rules_java~7.0.6~toolchains~remotejdk17_linux//:jdk
...
INFO: ToolchainResolution:   Type @bazel_tools//tools/jdk:runtime_toolchain_type: target platform @local_config_platform//:host: execution @local_config_platform//:host: Selected toolchain @rules_java~7.0.6~toolchains~remotejdk17_linux//:jdk
...
INFO: ToolchainResolution:     Type @bazel_tools//tools/jdk:runtime_toolchain_type: target platform @local_config_platform//:host: execution platform @local_config_platform//:host: Skipping toolchain @remotejdk17_linux//:jdk; execution platform already has selected toolchain
...
INFO: ToolchainResolution: Target platform @local_config_platform//:host: Selected execution platform @local_config_platform//:host, type @bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @rules_java~7.0.6~toolchains~remotejdk17_linux//:jdk
...
INFO: ToolchainResolution:   Type @bazel_tools//tools/jdk:runtime_toolchain_type: target platform @local_config_platform//:host: execution @local_config_platform//:host: Selected toolchain @rules_java~7.0.6~toolchains~remotejdk21_linux//:jdk
...
INFO: ToolchainResolution: Target platform @local_config_platform//:host: Selected execution platform @local_config_platform//:host, type @bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @rules_java~7.0.6~toolchains~remotejdk21_linux//:jdk
INFO: Analyzed target //:example (0 packages loaded, 1113 targets configured).
INFO: Found 1 target...

It seems from this that remotejdk17_linux is selected, but then remotejdk21_linux is picked in preference to it. I cannot see any obvious way of excluding remotejdk21_linux from the selection process?

Seems like a similar issue to https://github.com/bazelbuild/rules_java/issues/95. The solution here was to explicitly define JDKs, but that doesn't feel like the right solution?

swarren12 avatar Oct 24 '23 17:10 swarren12

To summarise some points from fmeum's reply on the Bazel issue and my follow-up comment:

It just so happens that Bazel 6 uses a JDK 17 and Bazel 7 uses a JDK 21 for the Java toolchains registered by default. These toolchains use a transition on --java_runtime_version to resolve a matching JDK, which is why you see this additional resolution in the debugger.

It seems this also applies to the versions of rules_java: 6.3.2 uses Java 17 by default, whilst 7.0.4 uses Java 21.

We can use this to resolve the immediate issue by downgrading to 6.3.2 until the migration is complete. However, it is still unclear to me if (or how) we would force version 7.0.6 to use the Java 17 toolchain, rather than the Java 21 toolchain.

swarren12 avatar Oct 25 '23 17:10 swarren12

Thought I would leave my comment here for the record: https://github.com/bazelbuild/bazel/issues/19934#issuecomment-1781835538

I believe the real fix is to add

[
    default_java_toolchain(
        name = "toolchain_java%d" % release,
        configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
        source_version = "%s" % release,
        target_version = "%s" % release,
+     java_runtime = "@bazel_tools//tools/jdk:toolchain_jdk%d" %release,
    )
    for release in RELEASES
]

And supply the toolchain_jdk for versions 8,9,10,11 (or at least a corresponding @bazel_tools//tools/jdk:remote_jdk{8,9,10}).

This will not work out of the box without it because otherwise you will get

java_runtime attribute of java_toolchain rule @rules_java//toolchains:toolchain_java11: '@bazel_tools//tools/jdk:toolchain_java11' does not have mandatory providers: 'JavaRuntimeInfo'

EdbertChan avatar Oct 26 '23 20:10 EdbertChan