| # 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", "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, |
| version = int(version) if version.isdigit() else 0, |
| ) |
| |
| 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 = Label("@bazel_tools//tools/jdk:runtime_toolchain_type"), |
| toolchain = runtime_name, |
| ) |
| |
| if 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", "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 |
| if repository_ctx.attr.build_file_content and repository_ctx.attr.build_file: |
| fail("build_file and build_file_content are exclusive") |
| if repository_ctx.attr.build_file_content: |
| build_file = repository_ctx.attr.build_file_content |
| elif repository_ctx.attr.build_file: |
| build_file = repository_ctx.read(repository_ctx.path(repository_ctx.attr.build_file)) |
| else: |
| build_file = "" |
| build_file = build_file.replace("___RUNTIME_VERSION___", version if version.isdigit() else "0") |
| |
| runtime_name = '"jdk"' if 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(), |
| "build_file_content": attr.string(), |
| }, |
| ) |
| |
| def local_java_repository(name, java_home, version = "", build_file = None, build_file_content = 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 |
| build_file_content: optional BUILD file template as a string |
| version: optionally java version |
| """ |
| _local_java_repository_rule(name = name, java_home = java_home, version = version, build_file = build_file, build_file_content = build_file_content) |
| native.register_toolchains("@" + name + "//:runtime_toolchain_definition") |