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]), ]