|  | # 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. | 
|  |  | 
|  | """Rules for importing and registering a local JDK.""" | 
|  |  | 
|  | load(":default_java_toolchain.bzl", "JVM8_TOOLCHAIN_CONFIGURATION", "default_java_toolchain") | 
|  |  | 
|  | def _detect_java_version(repository_ctx, java_bin): | 
|  | properties_out = repository_ctx.execute([java_bin, "-XshowSettings:properties"]).stderr | 
|  | # This returns an indented list of properties separated with newlines: | 
|  | # "  java.vendor.url.bug = ... \n" | 
|  | # "  java.version = 11.0.8\n" | 
|  | # "  java.version.date = 2020-11-05\" | 
|  |  | 
|  | 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 None | 
|  |  | 
|  | version_value = version_property[0][len("java.version = "):] | 
|  | parts = version_value.split(".") | 
|  | major = parts[0] | 
|  | if len(parts) == 1: | 
|  | return major | 
|  | elif major == "1":  # handles versions below 1.8 | 
|  | minor = parts[1] | 
|  | return minor | 
|  | return major | 
|  |  | 
|  | def local_java_runtime(name, java_home, version, runtime_name = None, visibility = ["//visibility:public"]): | 
|  | """Defines a java_runtime target together with Java runtime and compile toolchain definitions. | 
|  |  | 
|  | Java runtime toolchain is constrained by flag --java_runtime_version having | 
|  | value set to either name or version argument. | 
|  |  | 
|  | Java compile toolchains are created for --java_language_version flags values | 
|  | between 8 and version (inclusive). Java compile toolchains use the same | 
|  | (local) JDK for compilation. This requires a different configuration for JDK8 | 
|  | than the newer versions. | 
|  |  | 
|  | Args: | 
|  | name: name of the target. | 
|  | java_home: Path to the JDK. | 
|  | version: Version of the JDK. | 
|  | runtime_name: name of java_runtime target if it already exists. | 
|  | visibility: Visibility that will be applied to the java runtime target | 
|  | """ | 
|  | if runtime_name == None: | 
|  | runtime_name = name | 
|  | native.java_runtime( | 
|  | name = runtime_name, | 
|  | java_home = java_home, | 
|  | visibility = visibility, | 
|  | ) | 
|  |  | 
|  | native.config_setting( | 
|  | name = name + "_name_setting", | 
|  | values = {"java_runtime_version": name}, | 
|  | visibility = ["//visibility:private"], | 
|  | ) | 
|  | native.config_setting( | 
|  | name = name + "_version_setting", | 
|  | 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 + "_settings_alias", | 
|  | actual = select({ | 
|  | name + "_name_setting": name + "_name_setting", | 
|  | name + "_version_setting": name + "_version_setting", | 
|  | "//conditions:default": name + "_name_version_setting", | 
|  | }), | 
|  | visibility = ["//visibility:private"], | 
|  | ) | 
|  | native.toolchain( | 
|  | name = "runtime_toolchain_definition", | 
|  | target_settings = [":%s_settings_alias" % name], | 
|  | toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type", | 
|  | toolchain = runtime_name, | 
|  | ) | 
|  |  | 
|  | if version == "8": | 
|  | default_java_toolchain( | 
|  | name = name + "_toolchain_java8", | 
|  | configuration = JVM8_TOOLCHAIN_CONFIGURATION, | 
|  | source_version = version, | 
|  | target_version = version, | 
|  | java_runtime = runtime_name, | 
|  | ) | 
|  | 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), | 
|  | source_version = str(version), | 
|  | target_version = str(version), | 
|  | 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. | 
|  |  | 
|  | Args: | 
|  | repository_ctx: repository context | 
|  | """ | 
|  | 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.find("windows") != -1 else "" | 
|  | java_bin = java_home_path.get_child("bin").get_child("java" + extension) | 
|  |  | 
|  | if not java_bin.exists: | 
|  | # Java binary does not exist | 
|  | repository_ctx.file( | 
|  | "BUILD.bazel", | 
|  | _NOJDK_BUILD_TPL.format( | 
|  | local_jdk = repository_ctx.name, | 
|  | java_binary = "bin/java" + extension, | 
|  | java_home = java_home, | 
|  | ), | 
|  | False, | 
|  | ) | 
|  | return | 
|  |  | 
|  | # Detect version | 
|  | version = repository_ctx.attr.version if repository_ctx.attr.version != "" else _detect_java_version(repository_ctx, java_bin) | 
|  |  | 
|  | # Prepare BUILD file using "local_java_runtime" macro | 
|  | build_file = "" | 
|  | if repository_ctx.attr.build_file != None: | 
|  | build_file = repository_ctx.read(repository_ctx.path(repository_ctx.attr.build_file)) | 
|  |  | 
|  | runtime_name = '"jdk"' if repository_ctx.attr.build_file else None | 
|  | local_java_runtime_macro = """ | 
|  | local_java_runtime( | 
|  | name = "%s", | 
|  | runtime_name = %s, | 
|  | java_home = "%s", | 
|  | version = "%s", | 
|  | ) | 
|  | """ % (repository_ctx.name, runtime_name, java_home, version) | 
|  |  | 
|  | repository_ctx.file( | 
|  | "BUILD.bazel", | 
|  | 'load("@bazel_tools//tools/jdk:local_java_repository.bzl", "local_java_runtime")\n' + | 
|  | build_file + | 
|  | local_java_runtime_macro, | 
|  | ) | 
|  |  | 
|  | # Symlink all files | 
|  | for file in repository_ctx.path(java_home).readdir(): | 
|  | repository_ctx.symlink(file, file.basename) | 
|  |  | 
|  | # Build file template, when JDK does not exist | 
|  | _NOJDK_BUILD_TPL = '''load("@bazel_tools//tools/jdk:fail_rule.bzl", "fail_rule") | 
|  | fail_rule( | 
|  | name = "jdk", | 
|  | header = "Auto-Configuration Error:", | 
|  | message = ("Cannot find Java binary {java_binary} in {java_home}; either correct your JAVA_HOME, " + | 
|  | "PATH or specify Java from remote repository (e.g. " + | 
|  | "--java_runtime_version=remotejdk_11") | 
|  | ) | 
|  | config_setting( | 
|  | name = "localjdk_setting", | 
|  | values = {{"java_runtime_version": "{local_jdk}"}}, | 
|  | visibility = ["//visibility:private"], | 
|  | ) | 
|  | toolchain( | 
|  | name = "runtime_toolchain_definition", | 
|  | target_settings = [":localjdk_setting"], | 
|  | toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type", | 
|  | toolchain = ":jdk", | 
|  | ) | 
|  | ''' | 
|  |  | 
|  | _local_java_repository_rule = repository_rule( | 
|  | implementation = _local_java_repository_impl, | 
|  | local = True, | 
|  | configure = True, | 
|  | attrs = { | 
|  | "java_home": attr.string(), | 
|  | "version": attr.string(), | 
|  | "build_file": attr.label(), | 
|  | }, | 
|  | ) | 
|  |  | 
|  | def local_java_repository(name, java_home, version = "", build_file = None): | 
|  | """Registers a runtime toolchain for local JDK and creates an unregistered compile toolchain. | 
|  |  | 
|  | Toolchain resolution is constrained with --java_runtime_version flag | 
|  | having value of the "name" or "version" parameter. | 
|  |  | 
|  | Java compile toolchains are created for --java_language_version flags values | 
|  | between 8 and version (inclusive). Java compile toolchains use the same | 
|  | (local) JDK for compilation. | 
|  |  | 
|  | If there is no JDK "virtual" targets are created, which fail only when actually needed. | 
|  |  | 
|  | Args: | 
|  | name: A unique name for this rule. | 
|  | java_home: Location of the JDK imported. | 
|  | build_file: optionally BUILD file template | 
|  | version: optionally java version | 
|  | """ | 
|  | _local_java_repository_rule(name = name, java_home = java_home, version = version, build_file = build_file) | 
|  | native.register_toolchains("@" + name + "//:runtime_toolchain_definition") |