# Copyright 2023 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. """ Definition of java_runtime rule and JavaRuntimeInfo provider. """ load("@bazel_skylib//lib:paths.bzl", "paths") load("@rules_cc//cc/common:cc_info.bzl", "CcInfo") load("//java/common:java_semantics.bzl", "semantics") load("//java/common/rules/impl:java_helper.bzl", "helper") # copybara: default visibility ToolchainInfo = platform_common.ToolchainInfo def _init_java_runtime_info(**_kwargs): fail("instantiating JavaRuntimeInfo is a private API") JavaRuntimeInfo, _new_javaruntimeinfo = provider( doc = "Information about the Java runtime used by the java rules.", fields = { "default_cds": "Returns the JDK default CDS archive.", "files": "Returns the files in the Java runtime.", "hermetic_files": "Returns the files in the Java runtime needed for hermetic deployments.", "hermetic_static_libs": "Returns the JDK static libraries.", "java_executable_exec_path": "Returns the execpath of the Java executable.", "java_executable_runfiles_path": """Returns the path of the Java executable in runfiles trees. This should only be used when one needs to access the JVM during the execution of a binary or a test built by Bazel. In particular, when one needs to invoke the JVM during an action, java_executable_exec_path should be used instead.""", "java_home": "Returns the execpath of the root of the Java installation.", "java_home_runfiles_path": """Returns the path of the Java installation in runfiles trees. This should only be used when one needs to access the JDK during the execution of a binary or a test built by Bazel. In particular, when one needs the JDK during an action, java_home should be used instead.""", "lib_ct_sym": "Returns the lib/ct.sym file.", "lib_modules": "Returns the lib/modules file.", "version": "The Java feature version of the runtime. This is 0 if the version is unknown.", }, init = _init_java_runtime_info, ) def _is_main_repo(label): return label.workspace_name == "" def _default_java_home(label): if _is_main_repo(label): return label.package else: return helper.get_relative(label.workspace_root, label.package) def _get_bin_java(ctx): is_windows = helper.is_target_platform_windows(ctx) return "bin/java.exe" if is_windows else "bin/java" def _get_runfiles_java_executable(ctx, java_home, label): if paths.is_absolute(java_home) or _is_main_repo(label): return helper.get_relative(java_home, _get_bin_java(ctx)) else: repo_runfiles_path = "" if _is_main_repo(label) else helper.get_relative("..", label.workspace_name) return helper.get_relative(repo_runfiles_path, _get_bin_java(ctx)) def _is_java_binary(path): return path.endswith("bin/java") or path.endswith("bin/java.exe") def _get_lib_ct_sym(srcs, explicit_lib_ct_sym): if explicit_lib_ct_sym: return explicit_lib_ct_sym candidates = [src for src in srcs if src.path.endswith("/lib/ct.sym")] if len(candidates) == 1: return candidates[0] else: return None def _java_runtime_rule_impl(ctx): all_files = [] # [depset[File]] all_files.append(depset(ctx.files.srcs)) java_home = _default_java_home(ctx.label) if ctx.attr.java_home: java_home_attr = ctx.expand_make_variables("java_home", ctx.attr.java_home, {}) if ctx.files.srcs and paths.is_absolute(java_home_attr): fail("'java_home' with an absolute path requires 'srcs' to be empty.") java_home = helper.get_relative(java_home, java_home_attr) java_binary_exec_path = helper.get_relative(java_home, _get_bin_java(ctx)) java_binary_runfiles_path = _get_runfiles_java_executable(ctx, java_home, ctx.label) java = ctx.file.java if java: if paths.is_absolute(java_home): fail("'java_home' with an absolute path requires 'java' to be empty.") java_binary_exec_path = java.path java_binary_runfiles_path = java.short_path if not _is_java_binary(java_binary_exec_path): fail("the path to 'java' must end in 'bin/java'.") java_home = paths.dirname(paths.dirname(java_binary_exec_path)) all_files.append(depset([java])) java_home_runfiles_path = paths.dirname(paths.dirname(java_binary_runfiles_path)) hermetic_inputs = depset(ctx.files.hermetic_srcs) all_files.append(hermetic_inputs) lib_ct_sym = _get_lib_ct_sym(ctx.files.srcs, ctx.file.lib_ct_sym) lib_modules = ctx.file.lib_modules hermetic_static_libs = [dep[CcInfo] for dep in ctx.attr.hermetic_static_libs] # If a runtime does not set default_cds in hermetic mode, it is not fatal. # We can skip the default CDS in the check below. default_cds = ctx.file.default_cds if (hermetic_inputs or lib_modules or hermetic_static_libs) and ( not hermetic_inputs or not lib_modules or not hermetic_static_libs ): fail("hermetic specified, all of java_runtime.lib_modules, java_runtime.hermetic_srcs and java_runtime.hermetic_static_libs must be specified") files = depset(transitive = all_files) java_runtime_info = _new_javaruntimeinfo( default_cds = default_cds, files = files, hermetic_files = hermetic_inputs, hermetic_static_libs = hermetic_static_libs, java_executable_exec_path = java_binary_exec_path, java_executable_runfiles_path = java_binary_runfiles_path, java_home = java_home, java_home_runfiles_path = java_home_runfiles_path, lib_ct_sym = lib_ct_sym, lib_modules = lib_modules, version = ctx.attr.version, ) return [ DefaultInfo( files = files, runfiles = ctx.runfiles(transitive_files = files), ), java_runtime_info, platform_common.TemplateVariableInfo({ "JAVA": java_binary_exec_path, "JAVABASE": java_home, }), ToolchainInfo(java_runtime = java_runtime_info), ] java_runtime = rule( implementation = _java_runtime_rule_impl, doc = """

Specifies the configuration for a Java runtime.

Example:



java_runtime(
    name = "jdk-9-ea+153",
    srcs = glob(["jdk9-ea+153/**"]),
    java_home = "jdk9-ea+153",
)


""", attrs = { "default_cds": attr.label( allow_single_file = True, executable = True, cfg = "target", doc = """ Default CDS archive for hermetic java_runtime. When hermetic is enabled for a java_binary target the java_runtime default CDS is packaged in the hermetic deploy JAR. """, ), "hermetic_srcs": attr.label_list( allow_files = True, doc = """ Files in the runtime needed for hermetic deployments. """, ), "hermetic_static_libs": attr.label_list( providers = [CcInfo], doc = """ The libraries that are statically linked with the launcher for hermetic deployments """, ), "java": attr.label( allow_single_file = True, executable = True, cfg = "target", doc = """ The path to the java executable. """, ), "java_home": attr.string( doc = """ The path to the root of the runtime. Subject to "Make" variable substitution. If this path is absolute, the rule denotes a non-hermetic Java runtime with a well-known path. In that case, the srcs and java attributes must be empty. """, ), "lib_ct_sym": attr.label( allow_single_file = True, doc = """ The lib/ct.sym file needed for compilation with --release. If not specified and there is exactly one file in srcs whose path ends with /lib/ct.sym, that file is used. """, ), "lib_modules": attr.label( allow_single_file = True, executable = True, cfg = "target", doc = """ The lib/modules file needed for hermetic deployments. """, ), "srcs": attr.label_list( allow_files = True, doc = """ All files in the runtime. """, ), "version": attr.int( doc = """ The feature version of the Java runtime. I.e., the integer returned by Runtime.version().feature(). """, ), # buildifier: disable=attr-licenses "output_licenses": attr.license() if hasattr(attr, "license") else attr.string_list(), "_windows_constraints": attr.label_list( default = [paths.join(semantics.PLATFORMS_ROOT, "os:windows")], ), }, fragments = ["java"], provides = [ JavaRuntimeInfo, platform_common.TemplateVariableInfo, ], )