blob: 7a0cd143410c883297a64faef9ac685c4f18f02a [file] [log] [blame] [edit]
""" Java rules, that allow to specify target JDK version
Temporary solution until java rules suppor pinning java versions nativly (presumably by transitions)
Minimalistic implementation, to keep support complexity low.
Please comment on b/260924999 if your use case in not supported.
"""
load(":merge_archives.bzl", "run_singlejar")
load(":functions.bzl", "create_option_file")
load("@bazel_tools//tools/jdk:toolchain_utils.bzl", "find_java_toolchain")
# Client list. When adding new dependence, please create a tracking bug describing:
# - why target need a custom java version target
# - condition when it could be switched to default
# - test target that verify bytecode version/sdk compatibility
clients = [
"//tools/data-binding:", # b/260925941
]
def java_pinned_library(name, java_language_version, srcs, resources = [], resource_strip_prefix = None, deps = []):
"""Compiles sources into a .jar file with specified bytecode version
Arguments apart from `java_language_version` have same meaning as in java_library rule
Args:
java_language_version: Bytecode version to compile to. Possible values: "8", "11", "17"
name: A unique name for this target.
srcs: The list of source files that are processed to create the target.
resources: A list of data files to include in a Java jar.
resource_strip_prefix: The path prefix to strip from Java resources.
deps: The list of libraries to link into this library
"""
_java_library(
name = name,
java_language_version = java_language_version,
srcs = srcs,
resources = resources,
resource_strip_prefix = resource_strip_prefix,
deps = deps,
)
def _resources(ctx):
"""sets up an action to build a resource jar for the target being compiled.
Returns: The file resource jar file.
"""
resources = ctx.files.resources
resources_jar = ctx.actions.declare_file(ctx.label.name + "-resources.jar")
prefix = ctx.attr.resource_strip_prefix
rel_paths = []
for res in resources:
short = res.short_path
if not short.startswith(prefix):
fail("Resource file %s is not under the specified prefix to strip" % short)
short = short[len(prefix):]
if short.startswith("/"):
short = short[1:]
rel_paths.append((short, res))
zipper_files = "".join([k + "=" + v.path + "\n" for k, v in rel_paths])
zipper_list = create_option_file(ctx, "%s_resources_zipper_args" % ctx.label.name, zipper_files)
ctx.actions.run(
inputs = resources + [zipper_list],
outputs = [resources_jar],
executable = ctx.executable._zipper,
arguments = ["c", resources_jar.path, "@" + zipper_list.path],
progress_message = "Creating resources %s (%d files)" % (resources_jar.short_path, len(resources)),
mnemonic = "zipper",
)
return resources_jar
def _java_library_impl(ctx):
# TODO replace with load visibility check after Bazel upgraded to 6.0
target_allowed = False
for client in clients:
if str(ctx.label).startswith(client):
target_allowed = True
if not target_allowed:
fail("%s is not allowed. See //tools/base/bazel/java.bzl for details." % ctx.label)
output_jar = ctx.outputs.jar
srcs = ctx.files.srcs
resources = ctx.files.resources
jars = []
deps = [dep[JavaInfo] for dep in ctx.attr.deps]
# use zipper to strip resource prefix
if resources and ctx.attr.resource_strip_prefix:
jars.append(_resources(ctx))
resources = []
java_toolchain = find_java_toolchain(ctx, ctx.attr._java_toolchain)
java_jar = ctx.actions.declare_file(ctx.label.name + ".java.jar")
java_common.compile(
ctx,
source_files = srcs,
resources = resources,
output = java_jar,
deps = deps,
javac_opts = java_common.default_javac_opts(java_toolchain = java_toolchain),
java_toolchain = java_toolchain,
)
jars.append(java_jar)
run_singlejar(
ctx = ctx,
jars = jars,
out = ctx.outputs.jar,
)
return [JavaInfo(
output_jar = output_jar,
compile_jar = output_jar,
deps = deps,
)]
def _java_version_transition_impl(settings, attr):
""" Get java_language_version rule attribute value, and set --java_language_version option"""
return {"//command_line_option:java_language_version": attr.java_language_version}
_java_version_transition = transition(
implementation = _java_version_transition_impl,
inputs = [], # Transition don't depends on anything from current configuration
outputs = ["//command_line_option:java_language_version"], # overrides one command-line option
)
_java_library = rule(
implementation = _java_library_impl,
attrs = {
"java_language_version": attr.string(
mandatory = True,
doc = """Java bytecode version to compile to.
Target indirectly transitions to the differen (than parent target) java toolchain.
bazel --java_language_version option is updated from rule attribute value,
after on toolchain resolution phase, a java toolchain would be selected as if target is building
with `bazel build --java_language_version=X //...` command
""",
),
#Bazel allowlist to be able to use transitions
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
"deps": attr.label_list(providers = [[JavaInfo]]),
"srcs": attr.label_list(allow_files = [".java"]),
"resources": attr.label_list(allow_files = True),
"resource_strip_prefix": attr.string(),
"_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:current_java_toolchain")),
"_zipper": attr.label(
default = Label("@bazel_tools//tools/zip:zipper"),
cfg = "exec",
executable = True,
),
"_singlejar": attr.label(
default = Label("@bazel_tools//tools/jdk:singlejar"),
cfg = "exec",
executable = True,
),
},
cfg = _java_version_transition,
fragments = ["java"],
outputs = {
"jar": "%{name}.jar",
},
)