blob: 012109f920df9c146ba6701d0896a7bda2dd206e [file]
# Copyright 2023 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.
"""Aspect that transitively build .dex archives and desugar jars."""
load("//providers:providers.bzl", "AndroidIdeInfo", "StarlarkAndroidDexInfo")
load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
load("@rules_java//java/common:java_info.bzl", "JavaInfo")
load(":acls.bzl", "acls")
load(":attrs.bzl", _attrs = "attrs")
load(":desugar.bzl", _desugar = "desugar")
load(":dex.bzl", _dex = "dex")
load(":dex_toolchains.bzl", "dex_toolchains")
load(":min_sdk_version.bzl", _min_sdk_version = "min_sdk_version")
load(":utils.bzl", "ANDROID_SDK_TOOLCHAIN_TYPE", _get_android_sdk = "get_android_sdk", _utils = "utils")
visibility(PROJECT_VISIBILITY)
_tristate = _attrs.tristate
# Keep sorted
_ATTR_ASPECTS = [
"_aidl_lib", # for the aidl runtime on android_library
"_aspect_proto_toolchain_for_javalite", # To get from proto_library through proto_lang_toolchain rule to proto runtime library.
"_build_stamp_deps", # for build stamp runtime class deps
"_build_stamp_mergee_manifest_lib", # for empty build stamp Service class implementation
"_desugared_lib_config", # For java8 desugaring config file
"_toolchain", # For _java_lite_grpc_library
"deps",
"exports",
"runtime",
"runtime_deps",
]
# Also used by the android_binary rule
def get_aspect_deps(ctx):
"""Get all the deps of the dex_desugar_aspect that requires traversal.
Args:
ctx: The context.
Returns:
deps_list: List of all deps of the dex_desugar_aspect that requires traversal.
"""
deps_list = []
for attr in _ATTR_ASPECTS:
# android_binary's deps attr has a split transition, so when
# this is called from android_binary, deps should be accessed
# via ctx.split_attr instead of ctx.attr to ensure that the same
# branch of the split is used throughout the build. An aspect's
# ctx doesn't have split_attr, so access that via ctx.attr when
# this is called from the aspect impl.
if attr == "deps" and hasattr(ctx, "split_attr"):
deps = _utils.dedupe_split_attr(ctx.split_attr.deps)
elif attr == "_toolchain" and getattr(ctx, "kind", "") == "kt_jvm_toolchain":
pass # TODO(b/370300302): Prevent double-deps on Kotlin toolchain. Delete when fixed.
else:
deps = getattr(ctx.attr, attr, [])
if str(type(deps)) == "list":
deps_list += deps
elif str(type(deps)) == "Target":
deps_list.append(deps)
deps_list.extend([
ctx.toolchains[toolchain_type]
for toolchain_type in dex_toolchains
if (toolchain_type in ctx.toolchains)
])
return deps_list
def _get_desugar_classpath(java_info):
if acls.in_desugaring_runtime_jar_classpath_rollout():
return java_info._transitive_full_compile_time_jars
return java_info.transitive_compile_time_jars
def _aspect_impl(target, ctx):
"""Adapts the rule and target data.
Args:
target: The target.
ctx: The context.
Returns:
A list of providers.
"""
incremental_dexing = getattr(ctx.rule.attr, "incremental_dexing", _tristate.auto)
min_sdk_version = _min_sdk_version.get(ctx)
if incremental_dexing == _tristate.no or \
(not ctx.fragments.android.use_incremental_dexing and
incremental_dexing == _tristate.auto):
return []
extra_toolchain_jars = _get_platform_based_toolchain_jars(ctx)
if hasattr(ctx.rule.attr, "neverlink") and ctx.rule.attr.neverlink:
return []
dex_archives_dict = {}
runtime_jars = _get_produced_runtime_jars(target, ctx, extra_toolchain_jars)
bootclasspath = _get_boot_classpath(target, ctx)
desugar_classpath = _get_desugar_classpath(target[JavaInfo]) if JavaInfo in target else depset([])
if runtime_jars:
basename_clash = _check_basename_clash(runtime_jars)
aspect_dexopts = _get_aspect_dexopts(ctx)
for jar in runtime_jars:
if ctx.fragments.android.desugar_java8:
unique_desugar_filename = (jar.path if basename_clash else jar.basename) + "_desugared.jar"
desugared_jar = _dex.get_dx_artifact(ctx, unique_desugar_filename, min_sdk_version)
_desugar.desugar(
ctx,
input = jar,
output = desugared_jar,
bootclasspath = bootclasspath,
classpath = desugar_classpath,
min_sdk_version = min_sdk_version,
desugar_exec = ctx.executable._desugar_java8,
desugared_lib_config = ctx.file._desugared_lib_config,
)
else:
desugared_jar = None
for incremental_dexopts_list in aspect_dexopts:
incremental_dexopts = "".join(incremental_dexopts_list)
unique_dx_filename = (jar.short_path if basename_clash else jar.basename) + \
incremental_dexopts + ".dex.zip"
dex = _dex.get_dx_artifact(ctx, unique_dx_filename, min_sdk_version)
_dex.dex(
ctx,
input = desugared_jar if desugared_jar else jar,
output = dex,
incremental_dexopts = incremental_dexopts_list,
min_sdk_version = min_sdk_version,
dex_exec = ctx.executable._dexbuilder,
)
dex_archive = struct(
jar = jar,
desugared_jar = desugared_jar,
dex = dex,
)
if incremental_dexopts not in dex_archives_dict:
dex_archives_dict[incremental_dexopts] = []
dex_archives_dict[incremental_dexopts].append(dex_archive)
infos = _utils.collect_providers(StarlarkAndroidDexInfo, get_aspect_deps(ctx.rule))
merged_info = _dex.merge_infos(infos)
for dexopts in dex_archives_dict:
if dexopts in merged_info.dex_archives_dict:
merged_info.dex_archives_dict[dexopts] = depset(dex_archives_dict[dexopts], transitive = [merged_info.dex_archives_dict[dexopts]])
else:
merged_info.dex_archives_dict[dexopts] = depset(dex_archives_dict[dexopts])
return [
StarlarkAndroidDexInfo(
dex_archives_dict = merged_info.dex_archives_dict,
),
]
def _get_produced_runtime_jars(target, ctx, extra_toolchain_jars):
if ctx.rule.kind == "proto_library":
if getattr(ctx.rule.attr, "srcs", []):
if JavaInfo in target:
return [java_output.class_jar for java_output in target[JavaInfo].java_outputs]
return []
else:
jars = []
if JavaInfo in target:
jars.extend(target[JavaInfo].runtime_output_jars)
# TODO(b/124540821): Disable R.jar desugaring (with a flag).
if AndroidIdeInfo in target and target[AndroidIdeInfo].resource_jar:
jars.append(target[AndroidIdeInfo].resource_jar.class_jar)
jars.extend(extra_toolchain_jars)
return jars
def _get_platform_based_toolchain_jars(ctx):
if hasattr(ctx.rule.attr, "_aidl_lib"):
return ctx.rule.attr._aidl_lib[JavaInfo].runtime_output_jars
return []
def _get_aspect_dexopts(ctx):
return _power_set(_dex.normalize_dexopts(ctx.fragments.android.get_dexopts_supported_in_incremental_dexing))
def _get_boot_classpath(target, ctx):
if JavaInfo in target:
compilation_info = target[JavaInfo].compilation_info
if compilation_info and compilation_info.boot_classpath:
return compilation_info.boot_classpath
android_jar = _get_android_sdk(ctx).android_jar
if android_jar:
return [android_jar]
# This shouldn't ever be reached, but if it is, we should be clear about the error.
fail("No compilation info or android jar!")
def _check_basename_clash(artifacts):
seen = {}
for artifact in artifacts:
basename = artifact.basename
if basename not in seen:
seen[basename] = True
else:
return True
return False
def _power_set(items):
"""Calculates the power set of the given items.
"""
def _exp(base, n):
""" Calculates base ** n."""
res = 1
for _ in range(n):
res *= base
return res
power_set = []
size = len(items)
for i in range(_exp(2, size)):
element = [items[j] for j in range(size) if (i // _exp(2, j) % 2) != 0]
power_set.append(element)
return power_set
def _opt_kwargs():
"""Optional args to pass to the aspect definition if Bazel supports them."""
result = {}
if dex_toolchains:
result["toolchains_aspects"] = dex_toolchains
return result
dex_desugar_aspect = aspect(
implementation = _aspect_impl,
attr_aspects = _ATTR_ASPECTS,
attrs = _attrs.add(
{
"_desugar_java8": attr.label(
default = Label("//tools/android:desugar_java8"),
allow_files = True,
cfg = "exec",
executable = True,
),
"_desugared_lib_config": attr.label(
allow_single_file = True,
default = Label("//tools/android:full_desugar_jdk_libs_config_json"),
),
"_dexbuilder": attr.label(
default = Label("//tools/android:dexbuilder"),
allow_files = True,
cfg = "exec",
executable = True,
),
},
_attrs.ANDROID_SDK,
_min_sdk_version.attrs,
),
fragments = ["android"],
toolchains = [
ANDROID_SDK_TOOLCHAIN_TYPE,
],
required_aspect_providers = [[JavaInfo]],
**_opt_kwargs()
)