Skip to content

Commit

Permalink
Add incompatible_language_version_bootclasspath flag
Browse files Browse the repository at this point in the history
With `--@rules_java//java:incompatible_language_version_bootclasspath`,
the bootclasspath used for Java compilation is now determined based on
the numeric version specified in `--java_language_version` and the type
specified in `--java_runtime_version` rather than just using the target
runtime.

For example, with `--java_language_version=8` and
`--java_runtime_version=remotejdk_21`, the bootclasspath would extracted
from `remotejdk_8`.

For unversioned runtime versions such as `local_jdk`, the behavior
doesn't change.

If a matching runtime is not available, analysis fails with a customized
error message explaining the various options to the user. Instead, to
make the incompatible change less breaking, the failure could be
downgraded to a warning and a fallback to the previous behavior.

Work towards bazelbuild/bazel#21769
  • Loading branch information
fmeum committed Apr 2, 2024
1 parent 36415e3 commit df69312
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
9 changes: 9 additions & 0 deletions java/BUILD
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")

package(default_visibility = ["//visibility:public"])

licenses(["notice"])

# If enabled, the bootclasspath for Java compilation will be extracted from a Java runtime matching
# the version specified with `--java_language_version` rather than the runtime specified with
# `--java_runtime_version`.
bool_flag(
name = "incompatible_language_version_bootclasspath",
build_setting_default = False,
)

filegroup(
name = "srcs",
srcs = glob(["**"]) + [
Expand Down
36 changes: 36 additions & 0 deletions toolchains/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
load(
"@bazel_skylib//rules:common_settings.bzl",
"string_setting",
)
load("@rules_cc//cc:defs.bzl", "cc_library")
load(
":default_java_toolchain.bzl",
Expand All @@ -6,6 +10,7 @@ load(
"bootclasspath",
"default_java_toolchain",
"java_runtime_files",
"language_version_bootstrap_runtime",
)
load(
":java_toolchain_alias.bzl",
Expand Down Expand Up @@ -250,10 +255,41 @@ alias(
}),
)

config_setting(
name = "incompatible_language_version_bootclasspath",
flag_values = {
"//java:incompatible_language_version_bootclasspath": "True",
},
visibility = ["//visibility:private"],
)

string_setting(
name = "java_language_version",
build_setting_default = "",
visibility = ["//visibility:private"],
)

string_setting(
name = "java_runtime_version",
build_setting_default = "",
visibility = ["//visibility:private"],
)

language_version_bootstrap_runtime(
name = "language_version_bootstrap_runtime",
java_language_version = ":java_language_version",
java_runtime_version = ":java_runtime_version",
visibility = ["//visibility:private"],
)

bootclasspath(
name = "platformclasspath",
src = "DumpPlatformClassPath.java",
java_runtime_alias = ":current_java_runtime",
language_version_bootstrap_runtime = select({
":incompatible_language_version_bootclasspath": ":language_version_bootstrap_runtime",
"//conditions:default": None,
}),
)

default_java_toolchain(
Expand Down
119 changes: 118 additions & 1 deletion toolchains/default_java_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""Rules for defining default_java_toolchain"""

load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("//java:defs.bzl", "java_toolchain")

# JVM options, without patching java.compiler and jdk.compiler modules.
Expand Down Expand Up @@ -207,6 +208,87 @@ def java_runtime_files(name, srcs):
tags = ["manual"],
)

# TODO: This provider and the its usage is only necessary since --java_{language,runtime}_version
# are not available directly to Starlark.
_JavaVersionsInfo = provider(
"Exposes the --java_{language,runtime}_version value as extracted from a transition to a dependant.",
fields = {
"java_language_version": "The value of --java_language_version",
"java_runtime_version": "The value of --java_runtime_version",
},
)

def _language_version_bootstrap_runtime(ctx):
providers = [
_JavaVersionsInfo(
java_language_version = ctx.attr.java_language_version[BuildSettingInfo].value,
java_runtime_version = ctx.attr.java_runtime_version[BuildSettingInfo].value,
),
]

bootstrap_runtime = ctx.toolchains["@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type"]
if bootstrap_runtime:
providers.append(bootstrap_runtime.java_runtime)

return providers

language_version_bootstrap_runtime = rule(
implementation = _language_version_bootstrap_runtime,
attrs = {
"java_language_version": attr.label(
providers = [BuildSettingInfo],
),
"java_runtime_version": attr.label(
providers = [BuildSettingInfo],
),
},
toolchains = [
config_common.toolchain_type("@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type", mandatory = False),
],
)

def _get_bootstrap_runtime_version(*, java_language_version, java_runtime_version):
"""Returns the runtime version to use for bootstrapping the given language version.
If the runtime version is not versioned, e.g. "local_jdk", it is used as is.
Otherwise, the language version replaces the numeric part of the runtime version, e.g.,
"remotejdk_17" becomes "remotejdk_8".
"""
prefix, separator, version = java_runtime_version.rpartition("_")
if version and version.isdigit():
new_version = java_language_version
else:
# The runtime version is not versioned, e.g. "local_jdk". Use it as is.
new_version = version

return prefix + separator + new_version

def _bootclasspath_transition_impl(settings, _):
java_language_version = settings["//command_line_option:java_language_version"]
java_runtime_version = settings["//command_line_option:java_runtime_version"]

return {
"//command_line_option:java_runtime_version": _get_bootstrap_runtime_version(
java_language_version = java_language_version,
java_runtime_version = java_runtime_version,
),
"//toolchains:java_language_version": java_language_version,
"//toolchains:java_runtime_version": java_runtime_version,
}

_bootclasspath_transition = transition(
implementation = _bootclasspath_transition_impl,
inputs = [
"//command_line_option:java_language_version",
"//command_line_option:java_runtime_version",
],
outputs = [
"//command_line_option:java_runtime_version",
"//toolchains:java_language_version",
"//toolchains:java_runtime_version",
],
)

_JAVA_BOOTSTRAP_RUNTIME_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type")

# Opt the Java bootstrap actions into path mapping:
Expand Down Expand Up @@ -250,7 +332,36 @@ def _bootclasspath_impl(ctx):
args.add("DumpPlatformClassPath")
args.add(bootclasspath)

any_javabase = ctx.toolchains[_JAVA_BOOTSTRAP_RUNTIME_TOOLCHAIN_TYPE].java_runtime
incompatible_language_version_bootclasspath = ctx.attr._incompatible_language_version_bootclasspath[BuildSettingInfo].value
if incompatible_language_version_bootclasspath:
# The attribute is subject to a split transition.
language_version_bootstrap_runtime = ctx.attr.language_version_bootstrap_runtime[0]
if java_common.JavaRuntimeInfo in language_version_bootstrap_runtime:
any_javabase = language_version_bootstrap_runtime[java_common.JavaRuntimeInfo]
else:
java_versions_info = language_version_bootstrap_runtime[_JavaVersionsInfo]
bootstrap_runtime_version = _get_bootstrap_runtime_version(
java_language_version = java_versions_info.java_language_version,
java_runtime_version = java_versions_info.java_runtime_version,
)
is_exec = "-exec" in ctx.bin_dir.path
tool_prefix = "tool_" if is_exec else ""
fail("""
No Java runtime found to extract the bootclasspath from for --{tool_prefix}java_language_version={language_version} and --{tool_prefix}java_runtime_version={runtime_version}.
You can:
* register a Java runtime with name "{bootstrap_runtime_version}" to provide the bootclasspath or
* set --java_language_version to the Java version of an available runtime.
Rerun with --toolchain_resolution_debug='@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type' to see more details about toolchain resolution.
""".format(
language_version = java_versions_info.java_language_version,
runtime_version = java_versions_info.java_runtime_version,
bootstrap_runtime_version = bootstrap_runtime_version,
tool_prefix = tool_prefix,
))
else:
any_javabase = ctx.toolchains[_JAVA_BOOTSTRAP_RUNTIME_TOOLCHAIN_TYPE].java_runtime
args.add(any_javabase.java_home)

system_files = ("release", "modules", "jrt-fs.jar")
Expand Down Expand Up @@ -283,11 +394,17 @@ _bootclasspath = rule(
cfg = "exec",
providers = [java_common.JavaRuntimeInfo],
),
"language_version_bootstrap_runtime": attr.label(
cfg = _bootclasspath_transition,
),
"output_jar": attr.output(mandatory = True),
"src": attr.label(
cfg = "exec",
allow_single_file = True,
),
"_incompatible_language_version_bootclasspath": attr.label(
default = "//java:incompatible_language_version_bootclasspath",
),
},
toolchains = [_JAVA_BOOTSTRAP_RUNTIME_TOOLCHAIN_TYPE],
)
Expand Down

0 comments on commit df69312

Please sign in to comment.