Implement local_java_repository for local version of Java

Implement local_java_repository for local version of Java.

Repository is a way to verify Java is really present and emit a nice message otherwise.

Resolves #6993

Closes #12078.

PiperOrigin-RevId: 331980488
diff --git a/tools/jdk/BUILD b/tools/jdk/BUILD
index 116b8eb..0b8e37e 100644
--- a/tools/jdk/BUILD
+++ b/tools/jdk/BUILD
@@ -433,7 +433,10 @@
         "BUILD-jdk",  # Tools are build from the workspace for tests.
         "DumpPlatformClassPath.java",
         "default_java_toolchain.bzl",
+        "fail_rule.bzl",
         "java_toolchain_alias.bzl",
+        "jdk.BUILD",
+        "local_java_repository.bzl",
         "nosystemjdk/README",
         "proguard_whitelister.py",
         "proguard_whitelister_test.py",
diff --git a/tools/jdk/fail_rule.bzl b/tools/jdk/fail_rule.bzl
new file mode 100644
index 0000000..a8a3da2
--- /dev/null
+++ b/tools/jdk/fail_rule.bzl
@@ -0,0 +1,35 @@
+# Copyright 2020 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.
+
+"""A rule than fails during analysis."""
+
+def _fail_rule_impl(ctx):
+    if ctx.attr.header:
+        fail("%s %s" % (ctx.attr.header, ctx.attr.message))
+    else:
+        fail(ctx.attr.message)
+
+fail_rule = rule(
+    doc = "A rule that fails during analysis.",
+    implementation = _fail_rule_impl,
+    attrs = {
+        "header": attr.string(
+            doc = "Header of the message.",
+        ),
+        "message": attr.string(
+            mandatory = True,
+            doc = "Message to display.",
+        ),
+    },
+)
diff --git a/tools/jdk/jdk.BUILD b/tools/jdk/jdk.BUILD
new file mode 100644
index 0000000..0cd0b36
--- /dev/null
+++ b/tools/jdk/jdk.BUILD
@@ -0,0 +1,227 @@
+package(default_visibility = ["//visibility:public"])
+
+load("@rules_java//java:defs.bzl", "java_runtime", "java_import")
+
+exports_files(["BUILD.bazel"])
+
+DEPRECATION_MESSAGE = ("Don't depend on targets in the JDK workspace;" +
+                       " use @bazel_tools//tools/jdk:current_java_runtime instead" +
+                       " (see https://github.com/bazelbuild/bazel/issues/5594)")
+
+filegroup(
+    name = "jni_header",
+    srcs = ["include/jni.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-darwin",
+    srcs = ["include/darwin/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-linux",
+    srcs = ["include/linux/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-freebsd",
+    srcs = ["include/freebsd/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-openbsd",
+    srcs = ["include/openbsd/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jni_md_header-windows",
+    srcs = ["include/win32/jni_md.h"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "java",
+    srcs = select({
+        ":windows": ["bin/java.exe"],
+        "//conditions:default": ["bin/java"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jar",
+    srcs = select({
+        ":windows": ["bin/jar.exe"],
+        "//conditions:default": ["bin/jar"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "javac",
+    srcs = select({
+        ":windows": ["bin/javac.exe"],
+        "//conditions:default": ["bin/javac"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "javadoc",
+    srcs = select({
+        ":windows": ["bin/javadoc.exe"],
+        "//conditions:default": ["bin/javadoc"],
+    }),
+    data = [":jdk"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "xjc",
+    srcs = ["bin/xjc"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "wsimport",
+    srcs = ["bin/wsimport"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+BOOTCLASS_JARS = [
+    "rt.jar",
+    "resources.jar",
+    "jsse.jar",
+    "jce.jar",
+    "charsets.jar",
+]
+
+# TODO(cushon): this isn't compatible with JDK 9
+filegroup(
+    name = "bootclasspath",
+    srcs = ["jre/lib/%s" % jar for jar in BOOTCLASS_JARS],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+# TODO(cushon): migrate to extclasspath and delete
+filegroup(
+    name = "extdir",
+    srcs = glob(
+        ["jre/lib/ext/*.jar"],
+        allow_empty = True,
+    ),
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "extclasspath",
+    srcs = glob(
+        ["jre/lib/ext/*.jar"],
+        allow_empty = True,
+    ),
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jre-bin",
+    srcs = select({
+        # In some configurations, Java browser plugin is considered harmful and
+        # common antivirus software blocks access to npjp2.dll interfering with Bazel,
+        # so do not include it in JRE on Windows.
+        ":windows": glob(
+            ["jre/bin/**"],
+            allow_empty = True,
+            exclude = ["jre/bin/plugin2/**"],
+        ),
+        "//conditions:default": glob(
+            ["jre/bin/**"],
+            allow_empty = True,
+        ),
+    }),
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jre-lib",
+    srcs = glob(
+        ["jre/lib/**"],
+        allow_empty = True,
+    ),
+)
+
+filegroup(
+    name = "jre",
+    srcs = [":jre-default"],
+)
+
+filegroup(
+    name = "jre-default",
+    srcs = [
+        ":jre-bin",
+        ":jre-lib",
+    ],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+filegroup(
+    name = "jdk-bin",
+    srcs = glob(
+        ["bin/**"],
+        # The JDK on Windows sometimes contains a directory called
+        # "%systemroot%", which is not a valid label.
+        exclude = ["**/*%*/**"],
+    ),
+)
+
+filegroup(
+    name = "jdk-include",
+    srcs = glob(["include/**"]),
+)
+
+filegroup(
+    name = "jdk-lib",
+    srcs = glob(
+        ["lib/**"],
+        exclude = [
+            "lib/missioncontrol/**",
+            "lib/visualvm/**",
+        ],
+    ),
+)
+
+java_runtime(
+    name = "jdk",
+    srcs = [
+        ":jdk-bin",
+        ":jdk-include",
+        ":jdk-lib",
+        ":jre-default",
+    ],
+)
+
+filegroup(
+    name = "langtools",
+    srcs = ["lib/tools.jar"],
+    deprecation = DEPRECATION_MESSAGE,
+)
+
+java_import(
+    name = "langtools-neverlink",
+    deprecation = DEPRECATION_MESSAGE,
+    jars = ["lib/tools.jar"],
+    neverlink = 1,
+)
+
+config_setting(
+    name = "windows",
+    values = {"cpu": "x64_windows"},
+    visibility = ["//visibility:private"],
+)
diff --git a/tools/jdk/local_java_repository.bzl b/tools/jdk/local_java_repository.bzl
new file mode 100644
index 0000000..0a68136
--- /dev/null
+++ b/tools/jdk/local_java_repository.bzl
@@ -0,0 +1,74 @@
+# Copyright 2020 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.
+
+"""Java repository implementation.
+
+Creates a local repository using jdk.BUILD file.
+
+When Java executable is not present it creates a BUILD file with target "jdk"
+displaying an error message.
+"""
+
+def _local_java_repository_impl(repository_ctx):
+    java_home = repository_ctx.attr.java_home
+    java_home_path = repository_ctx.path(java_home)
+    if not java_home_path.exists:
+        fail('The path indicated by the "java_home" attribute "%s" (absolute: "%s") ' +
+             "does not exist." % (java_home, str(java_home_path)))
+
+    repository_ctx.file(
+        "WORKSPACE",
+        "# DO NOT EDIT: automatically generated WORKSPACE file for local_java_repository\n" +
+        "workspace(name = \"{name}\")\n".format(name = repository_ctx.name),
+    )
+
+    extension = ".exe" if repository_ctx.os.name.lower().find("windows") != -1 else ""
+    if java_home_path.get_child("bin").get_child("java" + extension).exists:
+        repository_ctx.file(
+            "BUILD.bazel",
+            repository_ctx.read(repository_ctx.path(repository_ctx.attr._build_file)),
+            False,
+        )
+
+        # Symlink all files
+        for file in repository_ctx.path(java_home).readdir():
+            repository_ctx.symlink(file, file.basename)
+
+        return
+
+    # Java binary does not exist
+    # TODO(ilist): replace error message after toolchain implementation
+    repository_ctx.file(
+        "BUILD.bazel",
+        '''load("@bazel_tools//tools/jdk:fail_rule.bzl", "fail_rule")
+
+fail_rule(
+    name = "jdk",
+    header = "Auto-Configuration Error:",
+    message = ("Cannot find Java binary %s in %s; either correct your JAVA_HOME, " +
+           "PATH or specify embedded Java (e.g. " +
+           "--javabase=@bazel_tools//tools/jdk:remote_jdk11)")
+)''' % ("bin/java" + extension, java_home),
+        False,
+    )
+
+local_java_repository = repository_rule(
+    implementation = _local_java_repository_impl,
+    local = True,
+    configure = True,
+    attrs = {
+        "java_home": attr.string(),
+        "_build_file": attr.label(default = "@bazel_tools//tools/jdk:jdk.BUILD"),
+    },
+)