blob: 9d57b09170b0cba7a59352db6310d52dc873cf0e [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.
"""
Common code for reuse across java_* rules
"""
load(":common/rule_util.bzl", "create_composite_dep")
load(":common/java/android_lint.bzl", "android_lint_action")
load(":common/java/compile_action.bzl", "COMPILE_ACTION")
java_common = _builtins.toplevel.java_common
coverage_common = _builtins.toplevel.coverage_common
JavaInfo = _builtins.toplevel.JavaInfo
JavaPluginInfo = _builtins.toplevel.JavaPluginInfo
ProtoInfo = _builtins.toplevel.ProtoInfo
CcInfo = _builtins.toplevel.CcInfo
def _filter_srcs(srcs, ext):
return [f for f in srcs if f.extension == ext]
def _filter_provider(provider, *attrs):
return [dep[provider] for attr in attrs for dep in attr if provider in dep]
def _get_attr_safe(ctx, attr, default):
return getattr(ctx.attr, attr) if hasattr(ctx.attr, attr) else default
# TODO(b/11285003): disallow jar files in deps, require java_import instead
def _filter_javainfo_and_legacy_jars(attr):
dep_list = []
# Native code collected data into a NestedSet, using add for legacy jars and
# addTransitive for JavaInfo. This resulted in legacy jars being first in the list.
for dep in attr:
kind = java_common.target_kind(dep)
if not JavaInfo in dep or kind == "java_binary" or kind == "java_test":
for file in dep[DefaultInfo].files.to_list():
if file.extension == "jar":
# Native doesn't construct JavaInfo
java_info = JavaInfo(output_jar = file, compile_jar = file)
dep_list.append(java_info)
for dep in attr:
if JavaInfo in dep:
dep_list.append(dep[JavaInfo])
return dep_list
def basic_java_library(
ctx,
srcs,
deps = [],
runtime_deps = [],
plugins = [],
exports = [],
exported_plugins = [],
resources = [],
classpath_resources = [],
javacopts = [],
neverlink = False,
enable_compile_jar_action = True,
coverage_config = None):
"""
Creates actions that compile and lint Java sources, sets up coverage and returns JavaInfo, InstrumentedFilesInfo and output groups.
The call creates actions and providers needed and shared by `java_library`,
`java_plugin`,`java_binary`, and `java_test` rules and it is primarily
intended to be used in those rules.
To prepare arguments for this call from rule's attributes use the provided
helpers: `collect_deps`, `collect_plugins`, and `collect_resources`.
Before compilation coverage.runner is added to the dependencies and if
present plugins are extended with the value of `--plugin` flag.
Args:
ctx: (RuleContext) Used to register the actions.
srcs: (list[File]) The list of source files that are processed to create the target.
deps: (list[Target]) The list of other libraries to be linked in to the target.
runtime_deps: (list[Target]) Libraries to make available to the final binary or test at runtime only.
plugins: (list[Target]) Java compiler plugins to run at compile-time.
exports: (list[Target]) Exported libraries.
exported_plugins: (list[Target]) The list of `java_plugin`s (e.g. annotation
processors) to export to libraries that directly depend on this library.
resources: (list[File]) A list of data files to include in a Java jar.
classpath_resources: (list[File])
javacopts: (list[str])
neverlink: (bool) Whether this library should only be used for compilation and not at runtime.
enable_compile_jar_action: (bool) Enables header compilation or ijar creation.
coverage_config: (struct{runner:Target, support_files:list[File]|depset[File], env:dict[str,str]})
Coverage configuration. `runner` is added to dependencies during
compilation, `support_files` and `env` is returned in InstrumentedFilesInfo.
Returns:
({java_info: JavaInfo,
files_to_build: list[File],
has_sources_or_resources: bool,
instrumented_files_info: InstrumentedFilesInfo,
output_groups: dict[str,list[File]],
extra_providers: list[Provider]})
"""
source_files = _filter_srcs(srcs, "java")
source_jars = _filter_srcs(srcs, "srcjar")
java_info, compilation_info = COMPILE_ACTION.call(
ctx,
output_class_jar = ctx.outputs.classjar,
output_source_jar = ctx.outputs.sourcejar,
source_files = source_files,
source_jars = source_jars,
deps = _collect_deps(deps + ([coverage_config.runner] if coverage_config else [])),
runtime_deps = _collect_deps(runtime_deps),
plugins = _collect_plugins(plugins + [ctx.attr._java_plugins]),
exports = _collect_deps(exports),
exported_plugins = _collect_plugins(exported_plugins),
resources = resources + _filter_srcs(srcs, "properties"),
classpath_resources = classpath_resources,
native_libraries = _collect_native_libraries(deps, runtime_deps, exports),
javacopts = javacopts,
neverlink = neverlink,
strict_deps = ctx.fragments.java.strict_java_deps,
enable_compile_jar_action = enable_compile_jar_action,
)
output_groups = dict(
compilation_outputs = compilation_info.output_class_jars,
_source_jars = java_info.transitive_source_jars,
_direct_source_jars = java_info.source_jars,
)
# TODO(b/131760365): This is a hack, since the Starlark APIs don't have
# an explicit test for "host" or "tool" configuration.
if not (ctx.configuration == ctx.host_configuration or
ctx.bin_dir.path.find("-exec-") >= 0) and not neverlink:
lint_output = android_lint_action(
ctx,
source_files,
source_jars + [output.generated_source_jar for output in java_info.java_outputs if output.generated_source_jar != None],
compilation_info,
)
if lint_output:
output_groups["_validation"] = [lint_output]
instrumented_files_info = coverage_common.instrumented_files_info(
ctx,
source_attributes = ["srcs"],
dependency_attributes = ["deps", "data", "resources", "resource_jars", "exports", "runtime_deps", "jars"],
coverage_support_files = coverage_config.support_files if coverage_config else depset(),
coverage_environment = coverage_config.env if coverage_config else {},
)
return struct(
java_info = java_info,
files_to_build = compilation_info.output_class_jars,
has_sources_or_resources = source_files or source_jars or resources,
instrumented_files_info = instrumented_files_info,
output_groups = output_groups,
extra_providers = {},
)
def _collect_plugins(plugins):
"""Collects plugins from an attribute.
Use this call to collect plugins from `plugins` or `exported_plugins` attribute.
The call simply extracts JavaPluginInfo provider.
Args:
plugins: (list[Target]) Attribute to collect plugins from.
Returns:
(list[JavaPluginInfo]) The plugins.
"""
return _filter_provider(JavaPluginInfo, plugins)
def _collect_deps(deps):
"""Collects dependencies from an attribute.
Use this call to collect plugins from `deps`, `runtime_deps`, or `exports` attribute.
The call extracts JavaInfo and additionaly also "legacy jars". "legacy jars"
are wrapped into a JavaInfo.
Args:
deps: (list[Target]) Attribute to collect dependencies from.
Returns:
(list[JavaInfo]) The dependencies.
"""
return _filter_javainfo_and_legacy_jars(deps)
def _collect_native_libraries(*attrs):
"""Collects native libraries from a list of attributes.
Use this call to collect native libraries from `deps`, `runtime_deps`, or `exports` attributes.
The call simply extracts CcInfo provider.
Args:
*attrs: (*list[Target]) Attribute to collect native libraries from.
Returns:
(list[CcInfo]) The native library dependencies.
"""
return _filter_provider(CcInfo, *attrs)
def construct_defaultinfo(ctx, files, neverlink, has_sources_or_resources, *extra_attrs):
"""Constructs DefaultInfo for Java library like rule.
Args:
ctx: (RuleContext) Used to construct the runfiles.
files: (list[File]) List of the files built by the rule.
neverlink: (bool) When true empty runfiles are constructed.
has_sources_or_resources: (bool) TODO(b/213551463): Check if this can be removed.
*extra_attrs: (list[Target]) Extra attributes to merge runfiles from.
Returns:
(DefaultInfo) DefaultInfo provider.
"""
files_depset = depset(files)
if neverlink:
runfiles = None
else:
run_files = files_depset if has_sources_or_resources else None
runfiles = ctx.runfiles(transitive_files = run_files, collect_default = True)
runfiles = runfiles.merge_all([dep[DefaultInfo].default_runfiles for attr in extra_attrs for dep in attr])
default_info = DefaultInfo(
files = files_depset,
runfiles = runfiles,
)
return default_info
JAVA_COMMON_DEP = create_composite_dep(
basic_java_library,
COMPILE_ACTION,
)