blob: 62f8d96d7dca02b72806058adc9078b25b5011de [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")
load(":common/java/java_semantics.bzl", "semantics")
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_resources(ctx, extra_resources = []):
"""Collects resources files from different attributes defined by Java rules.
The default location to specify resources is resources attribute.
.properties files may be also specified in srcs attribute.
Depending on semantics proto_library targets in resources attribute may be
expanded to .proto files or to a descriptor set.
Args:
ctx: (RuleContext) Uses ctx.attr.resources, ctx.files.resources, ctx.files.srcs.
extra_resources: (list[File]) Additional resource files.
Returns:
(list[File]) List of resource files.
"""
resources = []
if semantics.COLLECT_SRCS_FROM_PROTO_LIBRARY:
for resource in ctx.attr.resources:
if ProtoInfo in resource:
resources.extend(resource[ProtoInfo].transitive_sources.to_list())
else:
resources.extend(resource[DefaultInfo].files.to_list())
else:
resources.extend(ctx.files.resources)
resources.extend(extra_resources)
return resources
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,
)