Add Java toolchain tests

- Pass java_language_version to bazel_java_test.sh
  This way also the toolchain compiling for Java 14 and 15 are tested and not only if the code compiled with toolchain for Java 8 executes on JDKs 14 and 15.
- Add verifications Java toolchain tests.
  Different configurations of toolchains were just tested to build, without checking the configurations give the desired results.
- Add local_java_repository tests and fix problem with implementation.
- Remove unused toolchain_utils.bzl

Closes #12788.

PiperOrigin-RevId: 350752125
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index 3d90a81..605c4ac 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -244,6 +244,8 @@
             # java_tools zips to test
             "src/java_tools.zip",
             "src/java_tools_prebuilt.zip",
+            # --java_language_version value
+            java_version,
             # --java_runtime_version value
             java_version,
         ],
@@ -269,6 +271,8 @@
             # java_tools zips to test
             "$(LOCAL_JAVA_TOOLS_ZIP_URL)",
             "$(LOCAL_JAVA_TOOLS_PREBUILT_ZIP_URL)",
+            # --java_language_version value
+            java_version,
             # --java_runtime_version value
             java_version,
         ],
diff --git a/src/test/shell/bazel/bazel_java_test.sh b/src/test/shell/bazel/bazel_java_test.sh
index 4601591..8ffeca4 100755
--- a/src/test/shell/bazel/bazel_java_test.sh
+++ b/src/test/shell/bazel/bazel_java_test.sh
@@ -82,6 +82,13 @@
 JAVA_TOOLS_PREBUILT_ZIP_FILE_URL=${JAVA_TOOLS_PREBUILT_ZIP_FILE_URL:-}
 
 if [[ $# -gt 0 ]]; then
+  JAVA_LANGUAGE_VERSION="$1"; shift
+  add_to_bazelrc "build --java_language_version=${JAVA_LANGUAGE_VERSION}"
+  add_to_bazelrc "build --tool_java_language_version=${JAVA_LANGUAGE_VERSION}"
+fi
+
+
+if [[ $# -gt 0 ]]; then
   JAVA_RUNTIME_VERSION="$1"; shift
   add_to_bazelrc "build --java_runtime_version=${JAVA_RUNTIME_VERSION}"
   add_to_bazelrc "build --tool_java_runtime_version=${JAVA_RUNTIME_VERSION}"
diff --git a/src/test/shell/bazel/bazel_java_test_defaults.sh b/src/test/shell/bazel/bazel_java_test_defaults.sh
index 980b121..9f88537 100755
--- a/src/test/shell/bazel/bazel_java_test_defaults.sh
+++ b/src/test/shell/bazel/bazel_java_test_defaults.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# Tests the java rules with the default values provided by Bazel.
+# Tests Java toolchains, configured using flags or the default_java_toolchain macro.
 #
 
 set -euo pipefail
@@ -133,6 +133,7 @@
   expect_log "major version: 59"
 }
 
+# When coverage is requested with no Jacoco configured, an error shall be reported.
 function test_tools_jdk_toolchain_nojacocorunner() {
   mkdir -p java/main
   cat >java/main/BUILD <<EOF
@@ -167,7 +168,7 @@
   expect_log "jacocorunner not set in java_toolchain:"
 }
 
-
+# Specific toolchain attributes can be overridden.
 function test_default_java_toolchain_manualConfiguration() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain")
@@ -177,9 +178,15 @@
   jvm_opts = [],
 )
 EOF
+
   bazel build //:vanilla || fail "default_java_toolchain target failed to build"
+  bazel cquery --output=build //:vanilla >& $TEST_log || fail "failed to query //:vanilla"
+
+  expect_log 'jvm_opts = \[\]'
+  expect_log 'javabuilder = \["//:VanillaJavaBuilder"\]'
 }
 
+# Specific toolchain attributes - jvm_opts containing location function - can be overridden.
 function test_default_java_toolchain_manualConfigurationWithLocation() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "JDK9_JVM_OPTS")
@@ -200,9 +207,15 @@
     ],
 )
 EOF
+
   bazel build //:toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery --output=build //:toolchain >& $TEST_log || fail "failed to query //:toolchain"
+
+  expect_log 'jvm_opts = \["-XX:+UseParallelOldGC", "-XX:-CompactStrings", "--patch-module=java.compiler=$(location @remote_java_tools//:java_compiler_jar)", "--patch-module=jdk.compiler=$(location @remote_java_tools//:jdk_compiler_jar)",'
+  expect_log 'tools = \["@remote_java_tools//:java_compiler_jar", "@remote_java_tools//:jdk_compiler_jar"\]'
 }
 
+# JVM8_TOOLCHAIN_CONFIGURATION shall override Java 8 internal compiler classes.
 function test_default_java_toolchain_jvm8Toolchain() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "JVM8_TOOLCHAIN_CONFIGURATION")
@@ -212,9 +225,18 @@
   java_runtime = "@local_jdk//:jdk",
 )
 EOF
+
   bazel query //:jvm8_toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery 'deps(//:jvm8_toolchain)' >& $TEST_log || fail "failed to query //:jvm8_toolchain"
+
+  expect_log ":JavaBuilder"
+  expect_log ":javac_jar"
+  expect_not_log ":java_compiler_jar"
+  expect_not_log ":jdk_compiler_jar"
+  expect_not_log ":VanillaJavaBuilder"
 }
 
+# DEFAULT_TOOLCHAIN_CONFIGURATION shall use JavaBuilder and override Java 9+ internal compiler classes.
 function test_default_java_toolchain_javabuilderToolchain() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "DEFAULT_TOOLCHAIN_CONFIGURATION")
@@ -223,9 +245,18 @@
   configuration = DEFAULT_TOOLCHAIN_CONFIGURATION,
 )
 EOF
+
   bazel build //:javabuilder_toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery 'deps(//:javabuilder_toolchain)' >& $TEST_log || fail "failed to query //:javabuilder_toolchain"
+
+  expect_log ":JavaBuilder"
+  expect_log ":java_compiler_jar"
+  expect_log ":jdk_compiler_jar"
+  expect_not_log ":VanillaJavaBuilder"
+  expect_not_log ":javac_jar"
 }
 
+# VANILLA_TOOLCHAIN_CONFIGURATION shall use VanillaJavaBuilder and not override any JDK internal compiler classes.
 function test_default_java_toolchain_vanillaToolchain() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "VANILLA_TOOLCHAIN_CONFIGURATION")
@@ -235,9 +266,18 @@
   java_runtime = "@local_jdk//:jdk",
 )
 EOF
+
   bazel build //:vanilla_toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery 'deps(//:vanilla_toolchain)' >& $TEST_log || fail "failed to query //:vanilla_toolchain"
+
+  expect_log ":VanillaJavaBuilder"
+  expect_not_log ":JavaBuilder"
+  expect_not_log ":java_compiler_jar"
+  expect_not_log ":jdk_compiler_jar"
+  expect_not_log ":javac_jar"
 }
 
+# PREBUILT_TOOLCHAIN_CONFIGURATION shall use prebuilt ijar and singlejar binaries.
 function test_default_java_toolchain_prebuiltToolchain() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "PREBUILT_TOOLCHAIN_CONFIGURATION")
@@ -246,9 +286,17 @@
   configuration = PREBUILT_TOOLCHAIN_CONFIGURATION,
 )
 EOF
+
   bazel build //:prebuilt_toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery 'deps(//:prebuilt_toolchain)' >& $TEST_log || fail "failed to query //:prebuilt_toolchain"
+
+  expect_log "ijar/ijar\(.exe\)\? "
+  expect_log "singlejar/singlejar_local"
+  expect_not_log "ijar/ijar.cc"
+  expect_not_log "singlejar/singlejar_main.cc"
 }
 
+# NONPREBUILT_TOOLCHAIN_CONFIGURATION shall compile ijar and singlejar from sources.
 function test_default_java_toolchain_nonprebuiltToolchain() {
   cat > BUILD <<EOF
 load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "default_java_toolchain", "NONPREBUILT_TOOLCHAIN_CONFIGURATION")
@@ -257,8 +305,14 @@
   configuration = NONPREBUILT_TOOLCHAIN_CONFIGURATION,
 )
 EOF
+
   bazel build //:nonprebuilt_toolchain || fail "default_java_toolchain target failed to build"
+  bazel cquery 'deps(//:nonprebuilt_toolchain)' >& $TEST_log || fail "failed to query //:nonprebuilt_toolchain"
+
+  expect_log "ijar/ijar.cc"
+  expect_log "singlejar/singlejar_main.cc"
+  expect_not_log "ijar/ijar\(.exe\)\? "
+  expect_not_log "singlejar/singlejar_local"
 }
 
-
-run_suite "Java integration tests with default Bazel values"
+run_suite "Java toolchains tests, configured using flags or the default_java_toolchain macro."
diff --git a/src/test/shell/bazel/bazel_with_jdk_test.sh b/src/test/shell/bazel/bazel_with_jdk_test.sh
index b9ba99d..11843fb 100755
--- a/src/test/shell/bazel/bazel_with_jdk_test.sh
+++ b/src/test/shell/bazel/bazel_with_jdk_test.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# Tests that the version of Bazel with a bundled JDK works.
+# Tests detection of local JDK and that Bazel executes with a bundled JDK.
 #
 
 set -euo pipefail
@@ -90,6 +90,19 @@
     setup_bazelrc
   fi
 
+  mkdir -p java/main
+  cat >java/main/BUILD <<EOF
+java_library(
+    name = 'JavaExample',
+    srcs = ['JavaExample.java'],
+)
+EOF
+
+  cat >java/main/JavaExample.java <<EOF
+public class JavaExample {
+}
+EOF
+
   # ... but ensure JAVA_HOME is set, so we can find a default local jdk
   export JAVA_HOME="${javabase}"
 }
@@ -123,7 +136,6 @@
 # JVM selection: Do not automatically use remote JDK for execution JVM if local
 # JDK is not found. Print an error message guiding the user how to use remote JDK.
 # Rationale: Keeping build systems stable upon Bazel releases.
-
 function test_bazel_reports_missing_local_jdk() {
   # Make a JAVA_HOME with javac and without java
   # This fails discovery on systems that rely on JAVA_HOME, rely on PATH and
@@ -135,21 +147,97 @@
   export JAVA_HOME="$PWD"
   export PATH="$PWD/bin:$PATH"
 
-  mkdir -p java/main
-  cat >java/main/BUILD <<EOF
-java_library(
-    name = 'JavaExample',
-    srcs = ['JavaExample.java'],
-)
-EOF
-
-  cat >java/main/JavaExample.java <<EOF
-public class JavaExample {
-}
-EOF
-
-  bazel build java/main:JavaExample &>"${TEST_log}" && fail "build should have failed" || true
+  bazel build java/main:JavaExample &>"${TEST_log}" \
+      && fail "build with missing local JDK should have failed" || true
   expect_log "Auto-Configuration Error: Cannot find Java binary"
 }
 
-run_suite "bazel test suite"
+# Bazel shall detect JDK version and configure it with "local_jdk_{version}" and "{version}" setting.
+function test_bazel_detects_local_jdk_version8() {
+  # Fake Java version 8
+  mkdir -p jdk/bin
+  touch jdk/bin/javac
+  chmod +x jdk/bin/javac
+  cat >jdk/bin/java <<EOF
+#!/bin/bash
+
+echo " Property settings:" >&2
+echo "  java.version = 1.8.0 " >&2
+EOF
+  chmod +x jdk/bin/java
+  export JAVA_HOME="$PWD/jdk"
+  export PATH="$PWD/jdk/bin:$PATH"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=8 '//java/main:JavaExample' \
+      &>"${TEST_log}" || fail "Autodetecting a fake JDK version 8 and selecting it failed"
+  expect_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=local_jdk_8 '//java/main:JavaExample' \
+      &>"${TEST_log}" || fail "Autodetecting a fake JDK version 8 and selecting it failed"
+  expect_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=11 '//java/main:JavaExample' \
+      &>"${TEST_log}" || fail "Selecting prepackaged JDK version 11 failed"
+  expect_not_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+}
+
+# Bazel shall detect JDK version and configure it with "local_jdk_{version}" and "{version}" setting.
+function test_bazel_detects_local_jdk_version11() {
+  # Fake Java version 11
+  mkdir -p jdk/bin
+  touch jdk/bin/javac
+  chmod +x jdk/bin/javac
+  cat >jdk/bin/java <<EOF
+#!/bin/bash
+
+echo " Property settings:" >&2
+echo "  java.version = 11.0.1 " >&2
+EOF
+  chmod +x jdk/bin/java
+  export JAVA_HOME="$PWD/jdk"
+  export PATH="$PWD/jdk/bin:$PATH"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=11 '//java/main:JavaExample' \
+      &>"${TEST_log}" || fail "Autodetecting a fake JDK version 11 and selecting it failed"
+  expect_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=local_jdk_11 '//java/main:JavaExample' \
+      &>"${TEST_log}" || fail "Autodetecting a fake JDK version 8 and selecting it failed"
+  expect_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+
+  bazel cquery --toolchain_resolution_debug --java_runtime_version=15 '//java/main:JavaExample' \
+      &>"${TEST_log}"  || fail "Selecting prepackaged JDK version 15 failed"
+  expect_not_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+}
+
+# Failure to detect JDK version shall be handled gracefully.
+function test_bazel_gracefully_handles_unknown_java() {
+  # Fake Java version 11
+  mkdir -p jdk/bin
+  touch jdk/bin/javac
+  chmod +x jdk/bin/javac
+  cat >jdk/bin/java <<EOF
+#!/bin/bash
+
+echo " Property settings:" >&2
+echo "  java.version = xxx.superfuture.version " >&2
+EOF
+  chmod +x jdk/bin/java
+  export JAVA_HOME="$PWD/jdk"
+  export PATH="$PWD/jdk/bin:$PATH"
+
+  bazel cquery --toolchain_resolution_debug '//java/main:JavaExample' &>"${TEST_log}" \
+      || fail "Failed to resolve Java toolchain when version cannot be detected"
+  expect_log "@bazel_tools//tools/jdk:runtime_toolchain_type -> toolchain @local_jdk//:jdk"
+}
+
+# Bazel shall provide Java compilation toolchains that use local JDK.
+function test_bazel_compiles_with_localjdk() {
+  bazel aquery '//java/main:JavaExample' --extra_toolchains=@local_jdk//:all &>"${TEST_log}" \
+      || fail "Failed to use extra toolchains provided by @local_jdk repository."
+
+  expect_log "exec external/local_jdk/bin/java"
+  expect_not_log "exec external/remotejdk11_linux/bin/java"
+}
+
+run_suite "Tests detection of local JDK and that Bazel executes with a bundled JDK."
diff --git a/tools/jdk/BUILD b/tools/jdk/BUILD
index de37f89..4e4098d 100644
--- a/tools/jdk/BUILD
+++ b/tools/jdk/BUILD
@@ -24,7 +24,6 @@
         "proguard_whitelister_test.py",
         "proguard_whitelister_test_input.pgcfg",
         "remote_java_repository.bzl",
-        "toolchain_utils.bzl",
     ],
 )
 
diff --git a/tools/jdk/local_java_repository.bzl b/tools/jdk/local_java_repository.bzl
index 51aa1d3..90f4547 100644
--- a/tools/jdk/local_java_repository.bzl
+++ b/tools/jdk/local_java_repository.bzl
@@ -26,7 +26,7 @@
     strip_properties = [property.strip() for property in properties_out.splitlines()]
     version_property = [property for property in strip_properties if property.startswith("java.version = ")]
     if len(version_property) != 1:
-        return "unknown"
+        return None
 
     version_value = version_property[0][len("java.version = "):]
     (major, minor, rest) = version_value.split(".", 2)
@@ -69,17 +69,23 @@
         values = {"java_runtime_version": version},
         visibility = ["//visibility:private"],
     )
+    native.config_setting(
+        name = name + "_name_version_setting",
+        values = {"java_runtime_version": name + "_" + version},
+        visibility = ["//visibility:private"],
+    )
     native.alias(
-        name = name + "_version_or_name_setting",
+        name = name + "_settings_alias",
         actual = select({
+            name + "_name_setting": name + "_name_setting",
             name + "_version_setting": name + "_version_setting",
-            "//conditions:default": name + "_name_setting",
+            "//conditions:default": name + "_name_version_setting",
         }),
         visibility = ["//visibility:private"],
     )
     native.toolchain(
         name = "runtime_toolchain_definition",
-        target_settings = [":%s_version_or_name_setting" % name],
+        target_settings = [":%s_settings_alias" % name],
         toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type",
         toolchain = runtime_name,
     )
@@ -92,7 +98,7 @@
             target_version = version,
             java_runtime = runtime_name,
         )
-    else:
+    elif type(version) == type("") and version.isdigit() and int(version) > 8:
         for version in range(8, int(version) + 1):
             default_java_toolchain(
                 name = name + "_toolchain_java" + str(version),
@@ -101,6 +107,8 @@
                 java_runtime = runtime_name,
             )
 
+    # else version is not recognized and no compilation toolchains are predefined
+
 def _local_java_repository_impl(repository_ctx):
     """Repository rule local_java_repository implementation.
 
@@ -148,8 +156,8 @@
 local_java_runtime(
     name = "%s",
     runtime_name = %s,
-     java_home = "%s",
-     version = "%s",
+    java_home = "%s",
+    version = "%s",
 )
 """ % (repository_ctx.name, runtime_name, java_home, version)
 
diff --git a/tools/jdk/toolchain_utils.bzl b/tools/jdk/toolchain_utils.bzl
deleted file mode 100644
index fd7482a..0000000
--- a/tools/jdk/toolchain_utils.bzl
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2019 The Bazel Authors. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Finds the Java toolchain.
-
-Returns the toolchain if enabled, and falls back to a toolchain constructed from
-legacy toolchain selection.
-"""
-
-def find_java_toolchain(ctx, target):
-    """
-    Finds the Java toolchain.
-
-    If the Java toolchain is in use, returns it.  Otherwise, returns a Java
-    toolchain derived from legacy toolchain selection.
-
-    Args:
-      ctx: The rule context for which to find a toolchain.
-      target: A java_toolchain target (for legacy toolchain resolution).
-
-    Returns:
-      A JavaToolchainInfo.
-    """
-
-    _ignore = [ctx]
-
-    return target[java_common.JavaToolchainInfo]
-
-def find_java_runtime_toolchain(ctx, target):
-    """
-    Finds the Java runtime.
-
-    If the Java toolchain is in use, returns it.  Otherwise, returns a Java
-    runtime derived from legacy toolchain selection.
-
-    Args:
-      ctx: The rule context for which to find a toolchain.
-      target: A java_runtime target (for legacy toolchain resolution).
-
-    Returns:
-      A JavaRuntimeInfo.
-    """
-
-    _ignore = [ctx]
-
-    return target[java_common.JavaRuntimeInfo]