Skip to content

Commit e0db9cf

Browse files
Bencodescopybara-github
authored andcommitted
Merge #471 by Bencodes: Fix R8 --desugar_java8_libs support
Potential fix for #455 and a test that helps repro this issue over here #470 Closes #471 COPYBARA_INTEGRATE_REVIEW=#471 from Bencodes:fix-desugar-jdk8-libs-support c7271b8 PiperOrigin-RevId: 897901132 Change-Id: I19bf1cff38f35cee1e8c2df797c1a00beb43cf23
1 parent 21b08f7 commit e0db9cf

File tree

6 files changed

+432
-6
lines changed

6 files changed

+432
-6
lines changed

BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package(
66
default_visibility = ["//visibility:public"],
77
)
88

9+
exports_files(["MODULE.bazel"])
10+
911
license(
1012
name = "license",
1113
package_name = "bazelbuild/rules_android",

rules/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ bzl_library(
109109
visibility = [
110110
"//mobile_install:__pkg__",
111111
"//stardoc:__pkg__",
112+
"//test/rules/android_binary/r8_integration:__pkg__",
112113
"//test/rules/android_sdk_repository:__pkg__",
113114
],
114115
deps = [

rules/android_binary/r8.bzl

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ load("//providers:providers.bzl", "AndroidDexInfo", "AndroidPreDexJarInfo")
1717
load("//rules:acls.bzl", "acls")
1818
load("//rules:android_neverlink_aspect.bzl", "StarlarkAndroidNeverlinkInfo")
1919
load("//rules:common.bzl", "common")
20+
load("//rules:dex.bzl", _dex = "dex")
2021
load("//rules:java.bzl", "java")
2122
load("//rules:min_sdk_version.bzl", "min_sdk_version")
2223
load(
@@ -81,6 +82,7 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_
8182

8283
android_jar = get_android_sdk(ctx).android_jar
8384
proguard_specs = proguard.get_proguard_specs(ctx, packaged_resources_ctx.resource_proguard_config)
85+
desugared_lib_config = ctx.file._desugared_lib_config
8486

8587
# Optionally extract proguard specs embedded in the deploy JAR (META-INF/proguard/
8688
# and META-INF/com.android.tools/) so they are passed to R8.
@@ -126,30 +128,56 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_
126128
args.add(deploy_jar) # jar to optimize + desugar + dex
127129
args.add("--pg-map-output", proguard_mappings_output_file)
128130

131+
r8_inputs = [android_jar, deploy_jar] + proguard_specs
132+
if ctx.fragments.android.desugar_java8_libs and desugared_lib_config:
133+
args.add("--desugared-lib", desugared_lib_config)
134+
r8_inputs.append(desugared_lib_config)
135+
129136
java.run(
130137
ctx = ctx,
131138
host_javabase = common.get_host_javabase(ctx),
132139
executable = get_android_toolchain(ctx).r8.files_to_run,
133140
arguments = [args],
134-
inputs = depset([android_jar, deploy_jar] + proguard_specs, transitive = [neverlink_jars]),
141+
inputs = depset(r8_inputs, transitive = [neverlink_jars]),
135142
outputs = [dexes_zip, proguard_mappings_output_file],
136143
mnemonic = "AndroidR8",
137144
jvm_flags = ["-Xmx8G"],
138145
progress_message = "R8 Optimizing, Desugaring, and Dexing %{label}",
139146
)
140147

148+
# When R8 runs with --desugared-lib, it rewrites java.* API calls to j$.*
149+
# backport references, but does NOT include the j$.* implementation classes
150+
# in its output. Append the prebuilt desugared library DEX so the j$.*
151+
# classes are available at runtime.
152+
if ctx.fragments.android.desugar_java8_libs and desugared_lib_config:
153+
final_classes_dex_zip = ctx.actions.declare_file(ctx.label.name + "_final_dexes.zip")
154+
java8_legacy_dex = utils.only(
155+
get_android_toolchain(ctx).java8_legacy_dex.files.to_list(),
156+
)
157+
_dex.append_desugar_dexes(
158+
ctx,
159+
output = final_classes_dex_zip,
160+
input = dexes_zip,
161+
dexes = [java8_legacy_dex],
162+
dex_zips_merger = get_android_toolchain(ctx).dex_zips_merger.files_to_run,
163+
)
164+
else:
165+
final_classes_dex_zip = dexes_zip
166+
141167
android_dex_info = AndroidDexInfo(
142168
deploy_jar = deploy_jar,
143-
final_classes_dex_zip = dexes_zip,
144-
# R8 preserves the Java resources (i.e. non-Java-class files) in its output zip, so no need
145-
# to provide a Java resources zip.
146-
java_resource_jar = None,
169+
final_classes_dex_zip = final_classes_dex_zip,
170+
# The deploy jar contains Java resources (non-class files like metadata) that must be
171+
# extracted and included in the final APK. While R8 preserves them in its direct output,
172+
# the DexReducer (used by append_desugar_dexes) strips non-.dex entries, so we must
173+
# always provide the deploy jar for separate resource extraction.
174+
java_resource_jar = deploy_jar,
147175
)
148176

149177
return ProviderInfo(
150178
name = "r8_ctx",
151179
value = struct(
152-
final_classes_dex_zip = dexes_zip,
180+
final_classes_dex_zip = final_classes_dex_zip,
153181
dex_info = android_dex_info,
154182
providers = [
155183
android_dex_info,

test/rules/android_binary/r8_integration/BUILD

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
load("@bazel_binaries//:defs.bzl", "bazel_binaries")
12
load("@bazel_skylib//rules:build_test.bzl", "build_test")
3+
load(
4+
"@rules_bazel_integration_test//bazel_integration_test:defs.bzl",
5+
"script_test",
6+
)
27
load("@rules_python//python:py_test.bzl", "py_test")
8+
load("@rules_shell//shell:sh_library.bzl", "sh_library")
39
load(":test.bzl", "r8_neverlink_deps_test")
410

511
py_test(
@@ -24,3 +30,34 @@ build_test(
2430
name = "android_binary_with_neverlink_deps_build_test",
2531
targets = ["//test/rules/android_binary/r8_integration/java/com/neverlink:android_binary_with_neverlink_deps"],
2632
)
33+
34+
sh_library(
35+
name = "r8_integration_helper",
36+
testonly = True,
37+
srcs = ["r8_integration_helper.sh"],
38+
data = [
39+
"//:MODULE.bazel",
40+
"//rules:bzl",
41+
],
42+
visibility = ["//visibility:private"],
43+
deps = [
44+
"//test/bashunit",
45+
"@rules_shell//shell/runfiles",
46+
],
47+
)
48+
49+
script_test(
50+
name = "r8_desugaring_integration_test",
51+
size = "enormous",
52+
timeout = "eternal",
53+
srcs = ["r8_desugaring_integration_test.sh"],
54+
additional_env_inherit = ["ANDROID_HOME"],
55+
bazel_binaries = bazel_binaries,
56+
bazel_version = bazel_binaries.versions.current,
57+
tags = ["manual"],
58+
deps = [
59+
":r8_integration_helper",
60+
"//test/bashunit",
61+
"@rules_shell//shell/runfiles",
62+
],
63+
)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/bin/bash
2+
#
3+
# Copyright 2024 The Bazel Authors. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Integration tests for R8 desugaring and Java resource preservation.
18+
19+
# --- begin runfiles.bash initialization v2 ---
20+
# Copy-pasted from the Bazel Bash runfiles library v2.
21+
set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
22+
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
23+
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
24+
source "$0.runfiles/$f" 2>/dev/null || \
25+
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
26+
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
27+
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
28+
# --- end runfiles.bash initialization v2 ---
29+
30+
source "$(rlocation rules_android/test/rules/android_binary/r8_integration/r8_integration_helper.sh)" || \
31+
(echo >&2 "Failed to locate r8_integration_helper.sh" && exit 1)
32+
33+
# Test: with --desugar_java8_libs enabled, R8 desugars java.time APIs and the
34+
# backport implementation classes (j$.*) are included in the APK.
35+
function test_desugaring_enabled() {
36+
build_app --desugar_java8_libs
37+
38+
apk_dex_contains 'Lcom/test/app/DurationUser;' || \
39+
fail "DurationUser class not found in DEX"
40+
41+
if apk_dex_contains 'java/time/Duration;.*toSeconds'; then
42+
fail "Expected Duration.toSeconds() to be desugared but raw java/time reference found"
43+
fi
44+
45+
apk_dex_contains 'Lj\$/' || \
46+
fail "Expected j\$.* desugared library classes in the APK"
47+
}
48+
49+
# Test: with --nodesugar_java8_libs, R8 does not rewrite java.* references
50+
# and no desugared library DEX is appended.
51+
function test_desugaring_disabled() {
52+
build_app --nodesugar_java8_libs
53+
54+
apk_dex_contains 'Lcom/test/app/DurationUser;' || \
55+
fail "DurationUser class not found in DEX"
56+
57+
apk_dex_contains 'java/time/Duration' || \
58+
fail "Expected raw java/time/Duration reference when desugaring is disabled"
59+
60+
if apk_dex_contains 'Lj\$/'; then
61+
fail "Unexpected j\$.* desugared library classes when desugaring is disabled"
62+
fi
63+
}
64+
65+
# Test: with --desugar_java8_libs enabled, Java resources from dependency JARs
66+
# must be present in the final APK.
67+
function test_java_resources_preserved_with_desugaring() {
68+
build_app --desugar_java8_libs
69+
70+
apk_contains_file 'com/test/data/metadata.txt' || \
71+
fail "Java resource 'com/test/data/metadata.txt' not found in APK. " \
72+
"DexReducer likely stripped non-.dex entries and java_resource_jar was not set."
73+
}
74+
75+
# Test: without desugaring, Java resources should also be preserved.
76+
function test_java_resources_preserved_without_desugaring() {
77+
build_app --nodesugar_java8_libs
78+
79+
apk_contains_file 'com/test/data/metadata.txt' || \
80+
fail "Java resource 'com/test/data/metadata.txt' not found in APK even without desugaring."
81+
}
82+
83+
run_suite "R8 desugaring integration tests"

0 commit comments

Comments
 (0)