blob: af76f498442f8b7ddc2428f5300a33dc5e3dafb0 [file] [log] [blame]
# 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("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
load("//java/common:java_semantics.bzl", "PLATFORMS_ROOT")
load("//third_party/bazel_skylib/lib:paths.bzl", "paths")
load(":java_helper.bzl", "helper")
visibility(["//java/..."])
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 paths.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 paths.get_relative(java_home, _get_bin_java(ctx))
else:
repo_runfiles_path = "" if _is_main_repo(label) else paths.get_relative("..", label.workspace_name)
return paths.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 = paths.get_relative(java_home, java_home_attr)
java_binary_exec_path = paths.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 = """
<p>
Specifies the configuration for a Java runtime.
</p>
<h4 id="java_runtime_example">Example:</h4>
<pre class="code">
<code class="lang-starlark">
java_runtime(
name = "jdk-9-ea+153",
srcs = glob(["jdk9-ea+153/**"]),
java_home = "jdk9-ea+153",
)
</code>
</pre>
""",
attrs = {
"default_cds": attr.label(
allow_single_file = True,
executable = True,
cfg = "target",
doc = """
Default CDS archive for hermetic <code>java_runtime</code>. When hermetic
is enabled for a <code>java_binary</code> target and if the target does not
provide its own CDS archive by specifying the
<a href="${link java_binary.classlist}"><code>classlist</code></a> attribute,
the <code>java_runtime</code> 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 <a href="${link make-variables}">"Make" variable</a> substitution.
If this path is absolute, the rule denotes a non-hermetic Java runtime with a well-known
path. In that case, the <code>srcs</code> and <code>java</code> attributes must be empty.
""",
),
"lib_ct_sym": attr.label(
allow_single_file = True,
doc = """
The lib/ct.sym file needed for compilation with <code>--release</code>. If not specified and
there is exactly one file in <code>srcs</code> whose path ends with
<code>/lib/ct.sym</code>, 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
<code>Runtime.version().feature()</code>.
""",
),
# buildifier: disable=attr-licenses
"output_licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
"_windows_constraints": attr.label_list(
default = [paths.join(PLATFORMS_ROOT, "os:windows")],
),
},
fragments = ["java"],
provides = [
JavaRuntimeInfo,
platform_common.TemplateVariableInfo,
],
)