From d3ee68818e3d2f07ebb52012506eabe5681b26c3 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Sun, 7 May 2023 11:36:47 +0200 Subject: [PATCH] Verify bootclasspath version is at most compilation runtime version The bootclasspath is loaded by the Java toolchain's runtime used for compilation and thus needs to be compatible with it, otherwise this triggers errors such as: ``` error: cannot access module-info bad class file: /modules/jdk.net/module-info.class class file has wrong version 64.0, should be 61.0 Please remove or make sure it appears in the correct subdirectory of the classpath. error: cannot access module-info bad class file: /modules/jdk.accessibility/module-info.class class file has wrong version 64.0, should be 61.0 Please remove or make sure it appears in the correct subdirectory of the classpath. error: cannot access module-info bad class file: /modules/jdk.internal.vm.compiler.management/module-info.class class file has wrong version 64.0, should be 61.0 Please remove or make sure it appears in the correct subdirectory of the classpath. ``` --- .../devtools/build/lib/rules/java/BUILD | 1 + .../lib/rules/java/BootClassPathInfo.java | 24 ++++++++ .../build/lib/rules/java/JavaToolchain.java | 12 ++++ src/test/shell/bazel/bazel_with_jdk_test.sh | 61 +++++++++++++++++++ tools/jdk/default_java_toolchain.bzl | 6 +- 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/BUILD b/src/main/java/com/google/devtools/build/lib/rules/java/BUILD index 2fe5004cb06c26..430d6f18609209 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/BUILD +++ b/src/main/java/com/google/devtools/build/lib/rules/java/BUILD @@ -41,6 +41,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster", "//src/main/java/com/google/devtools/build/lib/analysis:config/build_configuration", "//src/main/java/com/google/devtools/build/lib/analysis:config/compilation_mode", + "//src/main/java/com/google/devtools/build/lib/analysis:config/core_options", "//src/main/java/com/google/devtools/build/lib/analysis:config/execution_transition_factory", "//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/no_transition", "//src/main/java/com/google/devtools/build/lib/analysis:configured_target", diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java b/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java index ad5f49b1a63e1b..741bd063c5ff8a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/BootClassPathInfo.java @@ -35,6 +35,7 @@ import net.starlark.java.eval.NoneType; import net.starlark.java.eval.Sequence; import net.starlark.java.eval.Starlark; +import net.starlark.java.eval.StarlarkInt; import net.starlark.java.eval.StarlarkThread; import net.starlark.java.eval.StarlarkValue; import net.starlark.java.syntax.Location; @@ -74,6 +75,13 @@ private Provider() { "The inputs to javac's --system flag, either a directory or a listing of files," + " which must contain at least 'release', 'lib/modules', and" + " 'lib/jrt-fs.jar'"), + @Param( + name = "version", + positional = false, + named = true, + defaultValue = "0", + doc = "The major version of the runtime providing this bootclasspath." + ), }, selfCall = true, useStarlarkThread = true) @@ -81,15 +89,21 @@ public BootClassPathInfo bootClassPathInfo( Sequence bootClassPathList, Sequence auxiliaryList, Object systemOrNone, + StarlarkInt version, StarlarkThread thread) throws EvalException { NestedSet systemInputs = getSystemInputs(systemOrNone); Optional systemPath = getSystemPath(systemInputs); + int versionChecked = version.toInt("version"); + if (versionChecked < 0) { + throw Starlark.errorf("version must be non-negative, got %d", versionChecked); + } return new BootClassPathInfo( getBootClassPath(bootClassPathList), getAuxiliary(auxiliaryList), systemInputs, systemPath, + versionChecked, thread.getCallerLocation()); } @@ -150,18 +164,21 @@ private static Optional getSystemPath(NestedSet systemIn private final NestedSet auxiliary; private final NestedSet systemInputs; private final Optional systemPath; + private final int version; private BootClassPathInfo( NestedSet bootclasspath, NestedSet auxiliary, NestedSet systemInputs, Optional systemPath, + int version, Location creationLocation) { super(creationLocation); this.bootclasspath = bootclasspath; this.auxiliary = auxiliary; this.systemInputs = systemInputs; this.systemPath = systemPath; + this.version = version; } @Override @@ -175,6 +192,7 @@ public static BootClassPathInfo create(NestedSet bootclasspath) { NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), Optional.empty(), + 0, null); } @@ -184,6 +202,7 @@ public static BootClassPathInfo empty() { NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER), Optional.empty(), + 0, null); } @@ -210,6 +229,11 @@ public NestedSet systemInputs() { return systemInputs; } + /** The major version of the runtime providing this bootclasspath. */ + public int version() { + return version; + } + public boolean isEmpty() { return bootclasspath.isEmpty(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java index 5e6b31a72de646..7e22a9ef19e96e 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java +++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java @@ -35,6 +35,7 @@ import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.analysis.config.CoreOptions; import com.google.devtools.build.lib.analysis.configuredtargets.PackageGroupConfiguredTarget; import com.google.devtools.build.lib.analysis.platform.ToolchainInfo; import com.google.devtools.build.lib.collect.nestedset.NestedSet; @@ -160,6 +161,17 @@ public ConfiguredTarget create(RuleContext ruleContext) JavaRuntimeInfo javaRuntime = JavaRuntimeInfo.from(ruleContext, "java_runtime"); + if (javaRuntime.version() != 0 && javaRuntime.version() < bootclasspath.version()) { + ruleContext.attributeError("java_runtime", String.format( + "The version of the Java compilation toolchain's java_runtime (%d) must be at least as" + + " high as the version of the Java runtime that provides the bootclasspath (%d)." + + " Either switch to a Java toolchain with a higher version of the Java runtime or" + + " lower the version of --%sjava_runtime_version", + javaRuntime.version(), bootclasspath.version(), + ruleContext.getConfiguration().getOptions().get(CoreOptions.class).isExec + ? "tool_" : "")); + } + JavaToolchainProvider provider = JavaToolchainProvider.create( ruleContext.getLabel(), diff --git a/src/test/shell/bazel/bazel_with_jdk_test.sh b/src/test/shell/bazel/bazel_with_jdk_test.sh index e6629c36e716a8..d98626574d79e3 100755 --- a/src/test/shell/bazel/bazel_with_jdk_test.sh +++ b/src/test/shell/bazel/bazel_with_jdk_test.sh @@ -289,4 +289,65 @@ function test_bazel_compiles_with_localjdk() { expect_not_log "exec external/remotejdk11_linux/bin/java" } +function test_java_runtime_greater_than_java_toolchain_runtime_version() { + mkdir -p pkg + cat >pkg/BUILD <<'EOF' +java_binary( + name = "Main", + srcs = ["Main.java"], + main_class = "com.example.Main", +) +EOF + + cat >pkg/Main.java <<'EOF' +package com.example; +public class Main { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} +EOF + + bazel build //pkg:Main \ + --java_language_version=8 \ + --java_runtime_version=remotejdk_20 \ + &>"${TEST_log}" && fail "Expected build to fail" + + expect_log "The version of the Java compilation toolchain's java_runtime (17) must be at least as high as the version of the Java runtime that provides the bootclasspath (20). Either switch to a Java toolchain with a higher version of the Java runtime or lower the version of --java_runtime_version" +} + +function test_tool_java_runtime_greater_than_java_toolchain_runtime_version() { + mkdir -p pkg + cat >pkg/BUILD <<'EOF' +java_binary( + name = "Main", + srcs = ["Main.java"], + main_class = "com.example.Main", +) + +genrule( + name = "gen", + outs = ["gen.txt"], + tools = [":Main"], + cmd = "$(location :Main) > $@", +) +EOF + + cat >pkg/Main.java <<'EOF' +package com.example; +public class Main { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} +EOF + + bazel build //pkg:gen \ + --tool_java_language_version=8 \ + --tool_java_runtime_version=remotejdk_20 \ + &>"${TEST_log}" && fail "Expected build to fail" + + expect_log "The version of the Java compilation toolchain's java_runtime (17) must be at least as high as the version of the Java runtime that provides the bootclasspath (20). Either switch to a Java toolchain with a higher version of the Java runtime or lower the version of --tool_java_runtime_version" +} + run_suite "Tests detection of local JDK and that Bazel executes with a bundled JDK." diff --git a/tools/jdk/default_java_toolchain.bzl b/tools/jdk/default_java_toolchain.bzl index a83e14e7251c30..90b459e33f4dfd 100644 --- a/tools/jdk/default_java_toolchain.bzl +++ b/tools/jdk/default_java_toolchain.bzl @@ -241,9 +241,12 @@ def _bootclasspath_impl(ctx): system = [f for f in ctx.files.target_javabase if f.basename in system_files] if len(system) != len(system_files): system = None + version = 0 if ctx.attr.target_javabase: + runtime_info = ctx.attr.target_javabase[java_common.JavaRuntimeInfo] inputs.extend(ctx.files.target_javabase) - args.add(ctx.attr.target_javabase[java_common.JavaRuntimeInfo].java_home) + args.add(runtime_info.java_home) + version = runtime_info.version ctx.actions.run( executable = str(host_javabase.java_executable_exec_path), @@ -257,6 +260,7 @@ def _bootclasspath_impl(ctx): java_common.BootClassPathInfo( bootclasspath = [bootclasspath], system = system, + version = version, ), OutputGroupInfo(jar = [bootclasspath]), ]