blob: 5a2ad06d6f068cc8edcc63c0c295c3b1d9b90b27 [file] [log] [blame]
# Copyright 2021 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_import rule.
"""
load(":common/java/basic_java_library.bzl", "construct_defaultinfo")
load(":common/java/java_semantics.bzl", "semantics")
load(":common/java/proguard_validation.bzl", "validate_proguard_specs")
load(":common/java/import_deps_check.bzl", "import_deps_check")
load(":common/cc/cc_info.bzl", "CcInfo")
load(":common/java/java_info.bzl", "JavaInfo")
load(":common/java/java_common.bzl", "java_common")
load(":common/java/java_common_internal_for_builtins.bzl", _run_ijar_private_for_builtins = "run_ijar")
PackageSpecificationInfo = _builtins.toplevel.PackageSpecificationInfo
def _filter_provider(provider, *attrs):
return [dep[provider] for attr in attrs for dep in attr if provider in dep]
def _collect_jars(ctx, jars):
jars_dict = {}
for info in jars:
if JavaInfo in info:
fail("'jars' attribute cannot contain labels of Java targets")
for jar in info.files.to_list():
jar_path = jar.dirname + jar.basename
if jars_dict.get(jar_path) != None:
fail("in jars attribute of java_import rule //" + ctx.label.package + ":" + ctx.attr.name + ": " + jar.basename + " is a duplicate")
jars_dict[jar_path] = jar
return [jar_tuple[1] for jar_tuple in jars_dict.items()] if len(jars_dict.items()) > 0 else []
def _process_with_ijars_if_needed(jars, ctx):
file_dict = {}
use_ijars = ctx.fragments.java.use_ijars()
for jar in jars:
interface_jar = jar
if use_ijars:
ijar_basename = jar.short_path.removeprefix("../").removesuffix("." + jar.extension) + "-ijar.jar"
interface_jar_directory = "_ijar/" + ctx.label.name + "/" + ijar_basename
interface_jar = ctx.actions.declare_file(interface_jar_directory)
_run_ijar_private_for_builtins(
ctx.actions,
target_label = ctx.label,
jar = jar,
output = interface_jar,
java_toolchain = semantics.find_java_toolchain(ctx),
)
file_dict[jar] = interface_jar
return file_dict
def _check_export_error(ctx, exports):
not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_exports") and not getattr(ctx.attr, "_allowlist_java_import_exports")[PackageSpecificationInfo].contains(ctx.label)
disallow_java_import_exports = ctx.fragments.java.disallow_java_import_exports()
if len(exports) != 0 and (disallow_java_import_exports or not_in_allowlist):
fail("java_import.exports is no longer supported; use java_import.deps instead")
def _check_empty_jars_error(ctx, jars):
# TODO(kotlaja): Remove temporary incompatible flag [disallow_java_import_empty_jars] once migration is done.
not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_empty_jars") and not getattr(ctx.attr, "_allowlist_java_import_empty_jars")[PackageSpecificationInfo].contains(ctx.label)
disallow_java_import_empty_jars = ctx.fragments.java.disallow_java_import_empty_jars()
if len(jars) == 0 and disallow_java_import_empty_jars and not_in_allowlist:
fail("empty java_import.jars is no longer supported " + ctx.label.package)
def _create_java_info_with_dummy_output_file(ctx, srcjar, all_deps, exports, runtime_deps_list, neverlink, cc_info_list):
dummy_jar = ctx.actions.declare_file(ctx.label.name + "_dummy.jar")
dummy_src_jar = srcjar
if dummy_src_jar == None:
dummy_src_jar = ctx.actions.declare_file(ctx.label.name + "_src_dummy.java")
ctx.actions.write(dummy_src_jar, "")
return java_common.compile(
ctx,
output = dummy_jar,
java_toolchain = semantics.find_java_toolchain(ctx),
source_files = [dummy_src_jar],
deps = all_deps,
runtime_deps = runtime_deps_list,
neverlink = neverlink,
exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually.
native_libraries = cc_info_list,
)
def bazel_java_import_rule(
ctx,
jars = [],
srcjar = None,
deps = [],
runtime_deps = [],
exports = [],
neverlink = False,
proguard_specs = []):
"""Implements java_import.
This rule allows the use of precompiled .jar files as libraries in other Java rules.
Args:
ctx: (RuleContext) Used to register the actions.
jars: (list[Artifact]) List of output jars.
srcjar: (Artifact) The jar containing the sources.
deps: (list[Target]) The list of dependent libraries.
runtime_deps: (list[Target]) Runtime dependencies to attach to the rule.
exports: (list[Target]) The list of exported libraries.
neverlink: (bool) Whether this rule should only be used for compilation and not at runtime.
constraints: (list[String]) Rule constraints.
proguard_specs: (list[File]) Files to be used as Proguard specification.
Returns:
(list[provider]) A list containing DefaultInfo, JavaInfo,
OutputGroupsInfo, ProguardSpecProvider providers.
"""
_check_empty_jars_error(ctx, jars)
_check_export_error(ctx, exports)
collected_jars = _collect_jars(ctx, jars)
all_deps = _filter_provider(JavaInfo, deps, exports)
jdeps_artifact = None
merged_java_info = java_common.merge(all_deps)
not_in_allowlist = hasattr(ctx.attr, "_allowlist_java_import_deps_checking") and not ctx.attr._allowlist_java_import_deps_checking[PackageSpecificationInfo].contains(ctx.label)
if len(collected_jars) > 0 and not_in_allowlist and "incomplete-deps" not in ctx.attr.tags:
jdeps_artifact = import_deps_check(
ctx,
collected_jars,
merged_java_info.compile_jars,
merged_java_info.transitive_compile_time_jars,
"java_import",
)
compilation_to_runtime_jar_map = _process_with_ijars_if_needed(collected_jars, ctx)
runtime_deps_list = [runtime_dep[JavaInfo] for runtime_dep in runtime_deps if JavaInfo in runtime_dep]
cc_info_list = [dep[CcInfo] for dep in deps if CcInfo in dep]
java_info = None
if len(collected_jars) > 0:
java_infos = []
for jar in collected_jars:
java_infos.append(JavaInfo(
output_jar = jar,
compile_jar = compilation_to_runtime_jar_map[jar],
deps = all_deps,
runtime_deps = runtime_deps_list,
neverlink = neverlink,
source_jar = srcjar,
exports = [export[JavaInfo] for export in exports if JavaInfo in export], # Watchout, maybe you need to add them there manually.
native_libraries = cc_info_list,
))
java_info = java_common.merge(java_infos)
else:
# TODO(kotlaja): Remove next line once all java_import targets with empty jars attribute are cleaned from depot (b/246559727).
java_info = _create_java_info_with_dummy_output_file(ctx, srcjar, all_deps, exports, runtime_deps_list, neverlink, cc_info_list)
target = {"JavaInfo": java_info}
target["ProguardSpecProvider"] = validate_proguard_specs(
ctx,
proguard_specs,
[deps, runtime_deps, exports],
)
# TODO(kotlaja): Revise if collected_runtimes can be added into construct_defaultinfo directly.
collected_runtimes = []
for runtime_dep in ctx.attr.runtime_deps:
collected_runtimes.extend(runtime_dep.files.to_list())
target["DefaultInfo"] = construct_defaultinfo(
ctx,
collected_jars,
collected_jars + collected_runtimes,
neverlink,
exports,
)
output_group_src_jars = depset() if srcjar == None else depset([srcjar])
target["OutputGroupInfo"] = OutputGroupInfo(
**{
"_source_jars": output_group_src_jars,
"_direct_source_jars": output_group_src_jars,
"_validation": depset() if jdeps_artifact == None else depset([jdeps_artifact]),
"_hidden_top_level_INTERNAL_": target["ProguardSpecProvider"].specs,
}
)
return target
def _proxy(ctx):
return bazel_java_import_rule(
ctx,
ctx.attr.jars,
ctx.file.srcjar,
ctx.attr.deps,
ctx.attr.runtime_deps,
ctx.attr.exports,
ctx.attr.neverlink,
ctx.files.proguard_specs,
).values()
_ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT = [
"java_library",
"java_import",
"cc_library",
"cc_binary",
]
JAVA_IMPORT_ATTRS = {
"data": attr.label_list(
allow_files = True,
flags = ["SKIP_CONSTRAINTS_OVERRIDE"],
),
"deps": attr.label_list(
providers = [JavaInfo],
allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
),
"exports": attr.label_list(
providers = [JavaInfo],
allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
),
"runtime_deps": attr.label_list(
allow_files = [".jar"],
allow_rules = _ALLOWED_RULES_IN_DEPS_FOR_JAVA_IMPORT,
providers = [[CcInfo], [JavaInfo]],
flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"],
),
# JavaImportBazeRule attr
"jars": attr.label_list(
allow_files = [".jar"],
mandatory = True,
),
"srcjar": attr.label(
allow_single_file = [".srcjar", ".jar"],
flags = ["DIRECT_COMPILE_TIME_INPUT"],
),
"neverlink": attr.bool(
default = False,
),
"constraints": attr.string_list(),
# ProguardLibraryRule attr
"proguard_specs": attr.label_list(
allow_files = True,
),
# Additional attrs
"licenses": attr.license() if hasattr(attr, "license") else attr.string_list(),
"_java_toolchain_type": attr.label(default = semantics.JAVA_TOOLCHAIN_TYPE),
}
java_import = rule(
_proxy,
attrs = JAVA_IMPORT_ATTRS,
provides = [JavaInfo],
fragments = ["java", "cpp"],
toolchains = [semantics.JAVA_TOOLCHAIN],
)