From 87e4d3194c5742a17d4f8029c9ee8f47fe7fa598 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Wed, 11 Mar 2026 18:13:30 -0700 Subject: [PATCH 01/13] fix-desugar-jdk8-libs-support --- rules/android_binary/r8.bzl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rules/android_binary/r8.bzl b/rules/android_binary/r8.bzl index 9178f387..c34a51b5 100644 --- a/rules/android_binary/r8.bzl +++ b/rules/android_binary/r8.bzl @@ -17,6 +17,7 @@ load("//providers:providers.bzl", "AndroidDexInfo", "AndroidPreDexJarInfo") load("//rules:acls.bzl", "acls") load("//rules:android_neverlink_aspect.bzl", "StarlarkAndroidNeverlinkInfo") load("//rules:common.bzl", "common") +load("//rules:dex.bzl", "dex") load("//rules:java.bzl", "java") load("//rules:min_sdk_version.bzl", "min_sdk_version") load( @@ -80,6 +81,7 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_ android_jar = get_android_sdk(ctx).android_jar proguard_specs = proguard.get_proguard_specs(ctx, packaged_resources_ctx.resource_proguard_config) + desugared_lib_config = ctx.file._desugared_lib_config # Get min SDK version from attribute, manifest_values, or depot floor effective_min_sdk = min_sdk_version.DEPOT_FLOOR @@ -107,12 +109,17 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_ args.add(deploy_jar) # jar to optimize + desugar + dex args.add("--pg-map-output", proguard_mappings_output_file) + r8_inputs = [android_jar, deploy_jar] + proguard_specs + if ctx.fragments.android.desugar_java8_libs and desugared_lib_config: + args.add("--desugared-lib", desugared_lib_config) + r8_inputs.append(desugared_lib_config) + java.run( ctx = ctx, host_javabase = common.get_host_javabase(ctx), executable = get_android_toolchain(ctx).r8.files_to_run, arguments = [args], - inputs = depset([android_jar, deploy_jar] + proguard_specs, transitive = [neverlink_jars]), + inputs = depset(r8_inputs, transitive = [neverlink_jars]), outputs = [dexes_zip, proguard_mappings_output_file], mnemonic = "AndroidR8", jvm_flags = ["-Xmx8G"], From 85ffece87e116fc80dc8d53413ba2e922f551929 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Thu, 12 Mar 2026 10:33:32 -0700 Subject: [PATCH 02/13] Update r8.bzl --- rules/android_binary/r8.bzl | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/android_binary/r8.bzl b/rules/android_binary/r8.bzl index c34a51b5..53e5b1d3 100644 --- a/rules/android_binary/r8.bzl +++ b/rules/android_binary/r8.bzl @@ -17,7 +17,6 @@ load("//providers:providers.bzl", "AndroidDexInfo", "AndroidPreDexJarInfo") load("//rules:acls.bzl", "acls") load("//rules:android_neverlink_aspect.bzl", "StarlarkAndroidNeverlinkInfo") load("//rules:common.bzl", "common") -load("//rules:dex.bzl", "dex") load("//rules:java.bzl", "java") load("//rules:min_sdk_version.bzl", "min_sdk_version") load( From 8640a29b468399ab8c901be3b45fa5b5305d6dd9 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Wed, 18 Mar 2026 09:13:47 -0700 Subject: [PATCH 03/13] Add tests --- .../rules/android_binary/r8_integration/BUILD | 10 +++ .../java/com/desugaring/AndroidManifest.xml | 19 +++++ .../r8_integration/java/com/desugaring/BUILD | 16 ++++ .../com/desugaring/DesugaringActivity.java | 17 ++++ .../java/com/desugaring/DurationUser.java | 18 +++++ .../java/com/desugaring/proguard.cfg | 2 + .../desugaring/res/layout/activity_main.xml | 5 ++ .../com/desugaring/res/values/strings.xml | 4 + .../r8_integration/r8_desugaring_test.py | 80 +++++++++++++++++++ 9 files changed, 171 insertions(+) create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/BUILD create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml create mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml create mode 100644 test/rules/android_binary/r8_integration/r8_desugaring_test.py diff --git a/test/rules/android_binary/r8_integration/BUILD b/test/rules/android_binary/r8_integration/BUILD index d3f4325f..dc2ca194 100644 --- a/test/rules/android_binary/r8_integration/BUILD +++ b/test/rules/android_binary/r8_integration/BUILD @@ -24,3 +24,13 @@ build_test( name = "android_binary_with_neverlink_deps_build_test", targets = ["//test/rules/android_binary/r8_integration/java/com/neverlink:android_binary_with_neverlink_deps"], ) + +py_test( + name = "r8_desugaring_test", + srcs = ["r8_desugaring_test.py"], + args = ["$(location @androidsdk//:dexdump)"], + data = [ + "//test/rules/android_binary/r8_integration/java/com/desugaring:desugaring_app_r8", + "@androidsdk//:dexdump", + ], +) diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml new file mode 100644 index 00000000..67e5eae7 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD b/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD new file mode 100644 index 00000000..33409397 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD @@ -0,0 +1,16 @@ +load("//rules:rules.bzl", "android_binary", "android_library") + +android_library( + name = "duration_lib", + srcs = ["DurationUser.java"], +) + +android_binary( + name = "desugaring_app_r8", + srcs = ["DesugaringActivity.java"], + manifest = "AndroidManifest.xml", + proguard_specs = ["proguard.cfg"], + resource_files = glob(["res/**"]), + visibility = ["//test/rules/android_binary/r8_integration:__pkg__"], + deps = [":duration_lib"], +) diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java b/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java new file mode 100644 index 00000000..d9c42a68 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java @@ -0,0 +1,17 @@ +package com.desugaring; + +import android.app.Activity; +import android.os.Bundle; +import java.time.Duration; + +/** Activity that exercises Duration.toSeconds() to test core library desugaring. */ +public class DesugaringActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Force the compiler to retain the call to DurationUser.getSeconds + long seconds = DurationUser.getSeconds(Duration.ofMinutes(5)); + setTitle("Seconds: " + seconds); + } +} diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java b/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java new file mode 100644 index 00000000..2c63bec7 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java @@ -0,0 +1,18 @@ +package com.desugaring; + +import java.time.Duration; + +/** + * A class that uses Duration.toSeconds(), which was added in API 31. + * This simulates a third-party library (like Google Nav SDK) that calls + * methods not available on all supported API levels. + * + * Without core library desugaring, this causes NoSuchMethodError on + * API 26-30 devices. + */ +public class DurationUser { + public static long getSeconds(Duration duration) { + // Duration.toSeconds() requires API 31+ + return duration.toSeconds(); + } +} diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg b/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg new file mode 100644 index 00000000..7ef29b7a --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg @@ -0,0 +1,2 @@ +-dontobfuscate +-keep class com.desugaring.DurationUser { *; } diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml new file mode 100644 index 00000000..bce65f74 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml @@ -0,0 +1,5 @@ + + + diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml new file mode 100644 index 00000000..a5efdad9 --- /dev/null +++ b/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Desugaring Test + diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_test.py b/test/rules/android_binary/r8_integration/r8_desugaring_test.py new file mode 100644 index 00000000..a990b2c6 --- /dev/null +++ b/test/rules/android_binary/r8_integration/r8_desugaring_test.py @@ -0,0 +1,80 @@ +# Copyright 2024 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. + +"""Tests that R8 properly applies core library desugaring. + +Verifies that methods added after API 26 (like Duration.toSeconds() from +API 31) are retargeted to their backported implementations when R8 processes +an android_binary with core library desugaring enabled. +""" + +import os +import subprocess +import sys +import unittest +import zipfile + + +class R8DesugaringTest(unittest.TestCase): + """Tests R8 core library desugaring integration.""" + + def _get_dexdump_output(self, apk_name): + tmp = os.environ["TEST_TMPDIR"] + apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" + apk_path = os.path.join(apk_directory, apk_name) + apk_tmp = os.path.join(tmp, apk_name) + + all_output = [] + with zipfile.ZipFile(apk_path) as zf: + for name in zf.namelist(): + if name.endswith(".dex"): + zf.extract(name, apk_tmp) + dex_path = os.path.join(apk_tmp, name) + proc = subprocess.run( + [dexdump, "-d", dex_path], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, + ) + all_output.append(proc.stdout.decode(errors="replace")) + + return "\n".join(all_output) + + def test_duration_to_seconds_is_desugared(self): + """Duration.toSeconds() (API 31) must not appear as a raw call in the DEX.""" + output = self._get_dexdump_output("desugaring_app_r8.apk") + + self.assertNotIn( + "Ljava/time/Duration;.toSeconds:()J", + output, + "Duration.toSeconds() was NOT desugared. This method requires API 31 " + "and will cause NoSuchMethodError on API 28-30 devices. " + "R8 must be passed --desugared-lib to retarget this call.", + ) + + def test_desugared_duration_class_present(self): + """The desugared library runtime must be included in the APK.""" + output = self._get_dexdump_output("desugaring_app_r8.apk") + + # The DurationUser class should still be in the DEX (kept by proguard rules) + self.assertIn( + "Class descriptor : 'Lcom/desugaring/DurationUser;'", + output, + "DurationUser class not found in DEX output", + ) + + +if __name__ == "__main__": + dexdump = sys.argv.pop() + unittest.main(argv=None) From 08df61bc68adfe73a99cf1c6ea61316d35a94510 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Wed, 18 Mar 2026 09:21:12 -0700 Subject: [PATCH 04/13] Update .bazelrc with --desugar_java8_libs --- .bazelrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.bazelrc b/.bazelrc index faa25c25..8a3a2950 100644 --- a/.bazelrc +++ b/.bazelrc @@ -28,3 +28,4 @@ build:windows --define=protobuf_allow_msvc=true common --enable_platform_specific_config +common --desugar_java8_libs From bfea0aa76d1ad48e00485e0fd5a4c093d8998433 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Wed, 18 Mar 2026 09:29:19 -0700 Subject: [PATCH 05/13] Test cleanup --- .../r8_integration/r8_desugaring_test.py | 81 ++++++++----------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_test.py b/test/rules/android_binary/r8_integration/r8_desugaring_test.py index a990b2c6..979fe8c3 100644 --- a/test/rules/android_binary/r8_integration/r8_desugaring_test.py +++ b/test/rules/android_binary/r8_integration/r8_desugaring_test.py @@ -12,13 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Tests that R8 properly applies core library desugaring. - -Verifies that methods added after API 26 (like Duration.toSeconds() from -API 31) are retargeted to their backported implementations when R8 processes -an android_binary with core library desugaring enabled. -""" - import os import subprocess import sys @@ -27,54 +20,46 @@ class R8DesugaringTest(unittest.TestCase): - """Tests R8 core library desugaring integration.""" + """Tests R8 core library desugaring integration.""" - def _get_dexdump_output(self, apk_name): - tmp = os.environ["TEST_TMPDIR"] - apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" - apk_path = os.path.join(apk_directory, apk_name) - apk_tmp = os.path.join(tmp, apk_name) + def _get_dexdump_output(self, apk_name): + tmp = os.environ["TEST_TMPDIR"] + apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" + apk_path = os.path.join(apk_directory, apk_name) + apk_tmp = os.path.join(tmp, apk_name) - all_output = [] - with zipfile.ZipFile(apk_path) as zf: - for name in zf.namelist(): - if name.endswith(".dex"): - zf.extract(name, apk_tmp) - dex_path = os.path.join(apk_tmp, name) - proc = subprocess.run( - [dexdump, "-d", dex_path], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=True, - ) - all_output.append(proc.stdout.decode(errors="replace")) + with zipfile.ZipFile(apk_path) as zf: + zf.extract("classes.dex", apk_tmp) - return "\n".join(all_output) + dexdump_proc = subprocess.run( + [dexdump, "-d", os.path.join(apk_tmp, "classes.dex")], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, + ) + return str(dexdump_proc.stdout) - def test_duration_to_seconds_is_desugared(self): - """Duration.toSeconds() (API 31) must not appear as a raw call in the DEX.""" - output = self._get_dexdump_output("desugaring_app_r8.apk") + def test_duration_to_seconds_is_desugared(self): + output = self._get_dexdump_output("desugaring_app_r8.apk") - self.assertNotIn( - "Ljava/time/Duration;.toSeconds:()J", - output, - "Duration.toSeconds() was NOT desugared. This method requires API 31 " - "and will cause NoSuchMethodError on API 28-30 devices. " - "R8 must be passed --desugared-lib to retarget this call.", - ) + # Duration.toSeconds() (API 31) must not appear as a raw call in the DEX. + # If present, R8 was not passed --desugared-lib and the call will cause + # NoSuchMethodError on API 28-30 devices. + self.assertNotIn( + "Ljava/time/Duration;.toSeconds:()J", + output, + ) - def test_desugared_duration_class_present(self): - """The desugared library runtime must be included in the APK.""" - output = self._get_dexdump_output("desugaring_app_r8.apk") + def test_desugared_duration_class_present(self): + output = self._get_dexdump_output("desugaring_app_r8.apk") - # The DurationUser class should still be in the DEX (kept by proguard rules) - self.assertIn( - "Class descriptor : 'Lcom/desugaring/DurationUser;'", - output, - "DurationUser class not found in DEX output", - ) + # The DurationUser class should still be in the DEX (kept by proguard rules) + self.assertIn( + "Class descriptor : 'Lcom/desugaring/DurationUser;'", + output, + ) if __name__ == "__main__": - dexdump = sys.argv.pop() - unittest.main(argv=None) + dexdump = sys.argv.pop() + unittest.main(argv=None) From 8288b12500030cf774d2d9ad000b52ad74c06e45 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Thu, 19 Mar 2026 06:58:09 -0700 Subject: [PATCH 06/13] More fixes --- test/rules/android_binary/r8_integration/r8_desugaring_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_test.py b/test/rules/android_binary/r8_integration/r8_desugaring_test.py index 979fe8c3..49a06261 100644 --- a/test/rules/android_binary/r8_integration/r8_desugaring_test.py +++ b/test/rules/android_binary/r8_integration/r8_desugaring_test.py @@ -37,7 +37,7 @@ def _get_dexdump_output(self, apk_name): stderr=subprocess.PIPE, check=True, ) - return str(dexdump_proc.stdout) + return dexdump_proc.stdout.decode() def test_duration_to_seconds_is_desugared(self): output = self._get_dexdump_output("desugaring_app_r8.apk") From 22cb503be16fd9f3e3e0eed64fee9ac9536a8f56 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 11:00:44 -0700 Subject: [PATCH 07/13] Add more tests to cover edge cases --- .../r8_integration/r8_desugaring_test.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_test.py b/test/rules/android_binary/r8_integration/r8_desugaring_test.py index 49a06261..87a33617 100644 --- a/test/rules/android_binary/r8_integration/r8_desugaring_test.py +++ b/test/rules/android_binary/r8_integration/r8_desugaring_test.py @@ -39,6 +39,27 @@ def _get_dexdump_output(self, apk_name): ) return dexdump_proc.stdout.decode() + def _get_all_dex_output(self, apk_name): + """Get dexdump output for ALL dex files in the APK (without disassembly).""" + tmp = os.environ["TEST_TMPDIR"] + apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" + apk_path = os.path.join(apk_directory, apk_name) + apk_tmp = os.path.join(tmp, apk_name + "_all") + + all_output = "" + with zipfile.ZipFile(apk_path) as zf: + dex_files = sorted(name for name in zf.namelist() if name.endswith(".dex")) + for dex_file in dex_files: + zf.extract(dex_file, apk_tmp) + dexdump_proc = subprocess.run( + [dexdump, os.path.join(apk_tmp, dex_file)], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, + ) + all_output += dexdump_proc.stdout.decode(errors="replace") + return all_output + def test_duration_to_seconds_is_desugared(self): output = self._get_dexdump_output("desugaring_app_r8.apk") @@ -59,6 +80,24 @@ def test_desugared_duration_class_present(self): output, ) + def test_desugared_library_classes_present(self): + """Desugared library backport classes (j$.*) must be in the APK. + + When R8 rewrites java.* API calls to j$.* backport calls via + --desugared-lib, the j$.* implementation classes must be compiled into DEX + and included in the APK. Without them, the app crashes at runtime with: + NoClassDefFoundError: Failed resolution of: Lj$/...; + (e.g. j$/net/URLEncoder, j$/time/Duration, etc.) + """ + output = self._get_all_dex_output("desugaring_app_r8.apk") + + # There must be at least one j$.* class definition in the combined DEX + # output. These are the desugared library backport implementations. + self.assertRegex( + output, + r"Class descriptor\s+:\s+'Lj\$/", + ) + if __name__ == "__main__": dexdump = sys.argv.pop() From a3d242d5a87a57f2b7bb2bb683dcbb1a2593a431 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 11:01:06 -0700 Subject: [PATCH 08/13] And push the actual solution --- rules/android_binary/r8.bzl | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/rules/android_binary/r8.bzl b/rules/android_binary/r8.bzl index 53e5b1d3..9c8c0dbe 100644 --- a/rules/android_binary/r8.bzl +++ b/rules/android_binary/r8.bzl @@ -17,6 +17,7 @@ load("//providers:providers.bzl", "AndroidDexInfo", "AndroidPreDexJarInfo") load("//rules:acls.bzl", "acls") load("//rules:android_neverlink_aspect.bzl", "StarlarkAndroidNeverlinkInfo") load("//rules:common.bzl", "common") +load("//rules:dex.bzl", _dex = "dex") load("//rules:java.bzl", "java") load("//rules:min_sdk_version.bzl", "min_sdk_version") load( @@ -125,9 +126,28 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_ progress_message = "R8 Optimizing, Desugaring, and Dexing %{label}", ) + # When R8 runs with --desugared-lib, it rewrites java.* API calls to j$.* + # backport references, but does NOT include the j$.* implementation classes + # in its output. Append the prebuilt desugared library DEX so the j$.* + # classes are available at runtime. + if ctx.fragments.android.desugar_java8_libs and desugared_lib_config: + final_classes_dex_zip = ctx.actions.declare_file(ctx.label.name + "_final_dexes.zip") + java8_legacy_dex = utils.only( + get_android_toolchain(ctx).java8_legacy_dex.files.to_list(), + ) + _dex.append_desugar_dexes( + ctx, + output = final_classes_dex_zip, + input = dexes_zip, + dexes = [java8_legacy_dex], + dex_zips_merger = get_android_toolchain(ctx).dex_zips_merger.files_to_run, + ) + else: + final_classes_dex_zip = dexes_zip + android_dex_info = AndroidDexInfo( deploy_jar = deploy_jar, - final_classes_dex_zip = dexes_zip, + final_classes_dex_zip = final_classes_dex_zip, # R8 preserves the Java resources (i.e. non-Java-class files) in its output zip, so no need # to provide a Java resources zip. java_resource_jar = None, @@ -136,7 +156,7 @@ def process_r8(ctx, validation_ctx, jvm_ctx, packaged_resources_ctx, build_info_ return ProviderInfo( name = "r8_ctx", value = struct( - final_classes_dex_zip = dexes_zip, + final_classes_dex_zip = final_classes_dex_zip, dex_info = android_dex_info, providers = [ android_dex_info, From 6ba19ed8139030f2e3e24bdf015844026599e405 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 12:20:02 -0700 Subject: [PATCH 09/13] Build out some better integration tests --- .bazelrc | 2 - BUILD | 2 + rules/BUILD | 1 + .../rules/android_binary/r8_integration/BUILD | 38 ++- .../r8_integration/r8_desugaring_helper.sh | 236 ++++++++++++++++++ .../r8_desugaring_integration_test.sh | 86 +++++++ .../r8_integration/r8_desugaring_test.py | 104 -------- 7 files changed, 357 insertions(+), 112 deletions(-) create mode 100755 test/rules/android_binary/r8_integration/r8_desugaring_helper.sh create mode 100755 test/rules/android_binary/r8_integration/r8_desugaring_integration_test.sh delete mode 100644 test/rules/android_binary/r8_integration/r8_desugaring_test.py diff --git a/.bazelrc b/.bazelrc index 8a3a2950..d0ec2874 100644 --- a/.bazelrc +++ b/.bazelrc @@ -27,5 +27,3 @@ common:windows --host_per_file_copt=external/.*@/w build:windows --define=protobuf_allow_msvc=true common --enable_platform_specific_config - -common --desugar_java8_libs diff --git a/BUILD b/BUILD index 5cd188c2..b17116fd 100644 --- a/BUILD +++ b/BUILD @@ -6,6 +6,8 @@ package( default_visibility = ["//visibility:public"], ) +exports_files(["MODULE.bazel"]) + license( name = "license", package_name = "bazelbuild/rules_android", diff --git a/rules/BUILD b/rules/BUILD index b0d524b1..e924b6b7 100644 --- a/rules/BUILD +++ b/rules/BUILD @@ -109,6 +109,7 @@ bzl_library( visibility = [ "//mobile_install:__pkg__", "//stardoc:__pkg__", + "//test/rules/android_binary/r8_integration:__pkg__", "//test/rules/android_sdk_repository:__pkg__", ], deps = [ diff --git a/test/rules/android_binary/r8_integration/BUILD b/test/rules/android_binary/r8_integration/BUILD index dc2ca194..5cc853f3 100644 --- a/test/rules/android_binary/r8_integration/BUILD +++ b/test/rules/android_binary/r8_integration/BUILD @@ -1,5 +1,11 @@ +load("@bazel_binaries//:defs.bzl", "bazel_binaries") load("@bazel_skylib//rules:build_test.bzl", "build_test") +load( + "@rules_bazel_integration_test//bazel_integration_test:defs.bzl", + "script_test", +) load("@rules_python//python:py_test.bzl", "py_test") +load("@rules_shell//shell:sh_library.bzl", "sh_library") load(":test.bzl", "r8_neverlink_deps_test") py_test( @@ -25,12 +31,32 @@ build_test( targets = ["//test/rules/android_binary/r8_integration/java/com/neverlink:android_binary_with_neverlink_deps"], ) -py_test( - name = "r8_desugaring_test", - srcs = ["r8_desugaring_test.py"], - args = ["$(location @androidsdk//:dexdump)"], +sh_library( + name = "r8_desugaring_helper", + testonly = True, + srcs = ["r8_desugaring_helper.sh"], data = [ - "//test/rules/android_binary/r8_integration/java/com/desugaring:desugaring_app_r8", - "@androidsdk//:dexdump", + "//:MODULE.bazel", + "//rules:bzl", + ], + visibility = ["//visibility:private"], + deps = [ + "//test/bashunit", + "@rules_shell//shell/runfiles", + ], +) + +script_test( + name = "r8_desugaring_integration_test", + size = "enormous", + srcs = ["r8_desugaring_integration_test.sh"], + bazel_binaries = bazel_binaries, + bazel_version = bazel_binaries.versions.current, + tags = ["manual"], + timeout = "eternal", + deps = [ + ":r8_desugaring_helper", + "//test/bashunit", + "@rules_shell//shell/runfiles", ], ) diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_helper.sh b/test/rules/android_binary/r8_integration/r8_desugaring_helper.sh new file mode 100755 index 00000000..3ef49123 --- /dev/null +++ b/test/rules/android_binary/r8_integration/r8_desugaring_helper.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# +# Copyright 2024 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. + +# Helper functions for R8 desugaring integration tests. + +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +source "$(rlocation rules_android/test/bashunit/unittest.bash)" || \ + (echo >&2 "Failed to locate bashunit.sh" && exit 1) + +_WORKSPACE_INITIALIZED=false + +# Resolve the real filesystem path of the rules_android source tree. +function get_rules_android_path() { + local module_bazel="$(rlocation rules_android/MODULE.bazel)" + if [[ -z "${module_bazel}" || ! -f "${module_bazel}" ]]; then + fail "Failed to locate rules_android MODULE.bazel" + fi + local real_path + real_path="$(python3 -c "import os; print(os.path.realpath('${module_bazel}'))")" + dirname "${real_path}" +} + +# set_up is called before each test by bashunit. We only initialize the +# workspace once since the inner Bazel build is expensive. Subsequent tests +# reuse the same workspace and inner Bazel server. +function set_up() { + if [[ "${_WORKSPACE_INITIALIZED}" == "true" ]]; then + return + fi + _WORKSPACE_INITIALIZED=true + + # Clean out the workspace. + rm -rf * + + set_up_workspace + create_desugaring_app +} + +function set_up_workspace() { + local rules_dir="$(get_rules_android_path)" + + # Find the Android SDK path from environment. + local sdk_path="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}" + if [[ -z "${sdk_path}" ]]; then + fail "ANDROID_HOME or ANDROID_SDK_ROOT must be set" + fi + + cat > MODULE.bazel < .bazelrc < app/BUILD <<'EOF' +load("@rules_android//rules:rules.bzl", "android_binary") + +android_binary( + name = "desugaring_app", + srcs = [ + "DesugaringActivity.java", + "DurationUser.java", + ], + manifest = "AndroidManifest.xml", + proguard_specs = ["proguard.cfg"], + resource_files = glob(["res/**"]), +) +EOF + + cat > app/AndroidManifest.xml <<'EOF' + + + + + + + + + + + +EOF + + cat > app/DesugaringActivity.java <<'EOF' +package com.desugaring.test; + +import android.app.Activity; +import android.os.Bundle; +import java.time.Duration; + +public class DesugaringActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + long seconds = DurationUser.getSeconds(Duration.ofMinutes(5)); + setTitle("Seconds: " + seconds); + } +} +EOF + + cat > app/DurationUser.java <<'EOF' +package com.desugaring.test; + +import java.time.Duration; + +public class DurationUser { + public static long getSeconds(Duration duration) { + return duration.toSeconds(); + } +} +EOF + + cat > app/proguard.cfg <<'EOF' +-dontobfuscate +-keep class com.desugaring.test.DurationUser { *; } +-keep class com.desugaring.test.DesugaringActivity { *; } +EOF + + cat > app/res/layout/activity_main.xml <<'EOF' + + +EOF + + cat > app/res/values/strings.xml <<'EOF' + + + DesugarTest + +EOF +} + +# Build the desugaring app with extra Bazel flags. +# Usage: build_desugaring_app [--desugar_java8_libs | --nodesugar_java8_libs] +function build_desugaring_app() { + "${BIT_BAZEL_BINARY}" build "$@" -- //app:desugaring_app >& $TEST_log || \ + fail "Failed to build desugaring app" +} + +# Returns 0 if any dex in the APK contains a string matching the given pattern. +# Usage: apk_dex_contains +function apk_dex_contains() { + local pattern="$1" + local apk_path="bazel-bin/app/desugaring_app.apk" + if [[ ! -f "${apk_path}" ]]; then + echo "APK not found at ${apk_path}" >&2 + return 1 + fi + + local tmpdir=$(mktemp -d) + unzip -o "${apk_path}" '*.dex' -d "${tmpdir}" > /dev/null 2>&1 + + local found=false + for dex in "${tmpdir}"/classes*.dex; do + if [[ -f "${dex}" ]] && strings "${dex}" | grep -q "${pattern}"; then + found=true + break + fi + done + + rm -rf "${tmpdir}" + [[ "${found}" == "true" ]] +} diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_integration_test.sh b/test/rules/android_binary/r8_integration/r8_desugaring_integration_test.sh new file mode 100755 index 00000000..0fe90e72 --- /dev/null +++ b/test/rules/android_binary/r8_integration/r8_desugaring_integration_test.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# +# Copyright 2024 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. + +# Integration tests for R8 desugaring with and without --desugar_java8_libs. +# +# These tests verify that: +# 1. When --desugar_java8_libs is enabled, R8 rewrites java.* calls to j$.* +# backports AND the j$.* implementation classes are included in the APK. +# 2. When --nodesugar_java8_libs is set, no j$.* classes appear in the APK +# and java.* calls remain as-is. +# 3. DurationUser is retained in both cases (proguard keep rule). + +# --- begin runfiles.bash initialization v2 --- +# Copy-pasted from the Bazel Bash runfiles library v2. +set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash +source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \ + source "$0.runfiles/$f" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \ + { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e +# --- end runfiles.bash initialization v2 --- + +source "$(rlocation rules_android/test/bashunit/unittest.bash)" || \ + (echo >&2 "Failed to locate bashunit.sh" && exit 1) + +source "$(rlocation rules_android/test/rules/android_binary/r8_integration/r8_desugaring_helper.sh)" || \ + (echo >&2 "Failed to locate r8_desugaring_helper.sh" && exit 1) + +# Test: with --desugar_java8_libs enabled, R8 desugars java.time APIs and the +# backport implementation classes (j$.*) are included in the APK. +function test_desugaring_enabled() { + build_desugaring_app --desugar_java8_libs + + # DurationUser must be retained by proguard keep rules. + apk_dex_contains 'Lcom/desugaring/test/DurationUser;' || \ + fail "DurationUser class not found in DEX" + + # Duration.toSeconds() (API 31) must be desugared: the raw java.time + # invocation is rewritten to a j$.time backport, so the original + # method reference should not appear in the constant pool. + if apk_dex_contains 'java/time/Duration;.*toSeconds'; then + fail "Expected Duration.toSeconds() to be desugared but raw java/time reference found" + fi + + # The j$.* backport implementation classes must be present. Without them + # the app crashes at runtime with NoClassDefFoundError (e.g. j$/net/URLEncoder). + apk_dex_contains 'Lj\$/' || \ + fail "Expected j\$.* desugared library classes in the APK" +} + +# Test: with --nodesugar_java8_libs, R8 does not rewrite java.* references +# and no desugared library DEX is appended. +function test_desugaring_disabled() { + build_desugaring_app --nodesugar_java8_libs + + # DurationUser must still be retained regardless of the desugaring flag. + apk_dex_contains 'Lcom/desugaring/test/DurationUser;' || \ + fail "DurationUser class not found in DEX" + + # Without desugaring, java.time.Duration.toSeconds() remains as a direct + # call in the DEX. (It will crash on API < 31 devices, but the reference + # should be present.) + apk_dex_contains 'java/time/Duration' || \ + fail "Expected raw java/time/Duration reference when desugaring is disabled" + + # No j$.* classes should be present. + if apk_dex_contains 'Lj\$/'; then + fail "Unexpected j\$.* desugared library classes when desugaring is disabled" + fi +} + +run_suite "R8 desugaring integration tests" diff --git a/test/rules/android_binary/r8_integration/r8_desugaring_test.py b/test/rules/android_binary/r8_integration/r8_desugaring_test.py deleted file mode 100644 index 87a33617..00000000 --- a/test/rules/android_binary/r8_integration/r8_desugaring_test.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright 2024 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. - -import os -import subprocess -import sys -import unittest -import zipfile - - -class R8DesugaringTest(unittest.TestCase): - """Tests R8 core library desugaring integration.""" - - def _get_dexdump_output(self, apk_name): - tmp = os.environ["TEST_TMPDIR"] - apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" - apk_path = os.path.join(apk_directory, apk_name) - apk_tmp = os.path.join(tmp, apk_name) - - with zipfile.ZipFile(apk_path) as zf: - zf.extract("classes.dex", apk_tmp) - - dexdump_proc = subprocess.run( - [dexdump, "-d", os.path.join(apk_tmp, "classes.dex")], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=True, - ) - return dexdump_proc.stdout.decode() - - def _get_all_dex_output(self, apk_name): - """Get dexdump output for ALL dex files in the APK (without disassembly).""" - tmp = os.environ["TEST_TMPDIR"] - apk_directory = "test/rules/android_binary/r8_integration/java/com/desugaring" - apk_path = os.path.join(apk_directory, apk_name) - apk_tmp = os.path.join(tmp, apk_name + "_all") - - all_output = "" - with zipfile.ZipFile(apk_path) as zf: - dex_files = sorted(name for name in zf.namelist() if name.endswith(".dex")) - for dex_file in dex_files: - zf.extract(dex_file, apk_tmp) - dexdump_proc = subprocess.run( - [dexdump, os.path.join(apk_tmp, dex_file)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=True, - ) - all_output += dexdump_proc.stdout.decode(errors="replace") - return all_output - - def test_duration_to_seconds_is_desugared(self): - output = self._get_dexdump_output("desugaring_app_r8.apk") - - # Duration.toSeconds() (API 31) must not appear as a raw call in the DEX. - # If present, R8 was not passed --desugared-lib and the call will cause - # NoSuchMethodError on API 28-30 devices. - self.assertNotIn( - "Ljava/time/Duration;.toSeconds:()J", - output, - ) - - def test_desugared_duration_class_present(self): - output = self._get_dexdump_output("desugaring_app_r8.apk") - - # The DurationUser class should still be in the DEX (kept by proguard rules) - self.assertIn( - "Class descriptor : 'Lcom/desugaring/DurationUser;'", - output, - ) - - def test_desugared_library_classes_present(self): - """Desugared library backport classes (j$.*) must be in the APK. - - When R8 rewrites java.* API calls to j$.* backport calls via - --desugared-lib, the j$.* implementation classes must be compiled into DEX - and included in the APK. Without them, the app crashes at runtime with: - NoClassDefFoundError: Failed resolution of: Lj$/...; - (e.g. j$/net/URLEncoder, j$/time/Duration, etc.) - """ - output = self._get_all_dex_output("desugaring_app_r8.apk") - - # There must be at least one j$.* class definition in the combined DEX - # output. These are the desugared library backport implementations. - self.assertRegex( - output, - r"Class descriptor\s+:\s+'Lj\$/", - ) - - -if __name__ == "__main__": - dexdump = sys.argv.pop() - unittest.main(argv=None) From ce04ea54ac8cfad818f62c2b80859000012dc808 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 12:48:18 -0700 Subject: [PATCH 10/13] Remove test/rules/android_binary/r8_integration --- .../java/com/desugaring/AndroidManifest.xml | 19 ------------------- .../r8_integration/java/com/desugaring/BUILD | 16 ---------------- .../com/desugaring/DesugaringActivity.java | 17 ----------------- .../java/com/desugaring/DurationUser.java | 18 ------------------ .../java/com/desugaring/proguard.cfg | 2 -- .../desugaring/res/layout/activity_main.xml | 5 ----- .../com/desugaring/res/values/strings.xml | 4 ---- 7 files changed, 81 deletions(-) delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/BUILD delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml delete mode 100644 test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml deleted file mode 100644 index 67e5eae7..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/AndroidManifest.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD b/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD deleted file mode 100644 index 33409397..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/BUILD +++ /dev/null @@ -1,16 +0,0 @@ -load("//rules:rules.bzl", "android_binary", "android_library") - -android_library( - name = "duration_lib", - srcs = ["DurationUser.java"], -) - -android_binary( - name = "desugaring_app_r8", - srcs = ["DesugaringActivity.java"], - manifest = "AndroidManifest.xml", - proguard_specs = ["proguard.cfg"], - resource_files = glob(["res/**"]), - visibility = ["//test/rules/android_binary/r8_integration:__pkg__"], - deps = [":duration_lib"], -) diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java b/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java deleted file mode 100644 index d9c42a68..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/DesugaringActivity.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.desugaring; - -import android.app.Activity; -import android.os.Bundle; -import java.time.Duration; - -/** Activity that exercises Duration.toSeconds() to test core library desugaring. */ -public class DesugaringActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // Force the compiler to retain the call to DurationUser.getSeconds - long seconds = DurationUser.getSeconds(Duration.ofMinutes(5)); - setTitle("Seconds: " + seconds); - } -} diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java b/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java deleted file mode 100644 index 2c63bec7..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/DurationUser.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.desugaring; - -import java.time.Duration; - -/** - * A class that uses Duration.toSeconds(), which was added in API 31. - * This simulates a third-party library (like Google Nav SDK) that calls - * methods not available on all supported API levels. - * - * Without core library desugaring, this causes NoSuchMethodError on - * API 26-30 devices. - */ -public class DurationUser { - public static long getSeconds(Duration duration) { - // Duration.toSeconds() requires API 31+ - return duration.toSeconds(); - } -} diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg b/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg deleted file mode 100644 index 7ef29b7a..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/proguard.cfg +++ /dev/null @@ -1,2 +0,0 @@ --dontobfuscate --keep class com.desugaring.DurationUser { *; } diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml deleted file mode 100644 index bce65f74..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/res/layout/activity_main.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml b/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml deleted file mode 100644 index a5efdad9..00000000 --- a/test/rules/android_binary/r8_integration/java/com/desugaring/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Desugaring Test - From c12dfa35f12d5af195f871c75627fcf6d879e537 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 12:48:32 -0700 Subject: [PATCH 11/13] Maybe more --- .../javatests/com/google/devtools/build/android/dexer/BUILD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD index 16e5a699..79d2acfb 100644 --- a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD +++ b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD @@ -58,9 +58,9 @@ java_test( ":simple_jar", ], jvm_flags = [ - "-Djsimplejar=google3/$(location :jsimple_jar)", - "-Dmultidexjar=google3/$(location :multidex_jar)", - "-Dsimplejar=google3/$(location :simple_jar)", + "-Djsimplejar=_main/$(location :jsimple_jar)", + "-Dmultidexjar=_main/$(location :multidex_jar)", + "-Dsimplejar=_main/$(location :simple_jar)", ], runtime_deps = [ ":tests", From c53da039421d5fca81dd4d622e00242eea04b275 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 13:03:53 -0700 Subject: [PATCH 12/13] Maybe more --- .../javatests/com/google/devtools/build/android/dexer/BUILD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD index 79d2acfb..644bf548 100644 --- a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD +++ b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD @@ -58,9 +58,9 @@ java_test( ":simple_jar", ], jvm_flags = [ - "-Djsimplejar=_main/$(location :jsimple_jar)", - "-Dmultidexjar=_main/$(location :multidex_jar)", - "-Dsimplejar=_main/$(location :simple_jar)", + "-Djsimplejar=$(rlocationpath :jsimple_jar)", + "-Dmultidexjar=$(rlocationpath :multidex_jar)", + "-Dsimplejar=$(rlocationpath :simple_jar)", ], runtime_deps = [ ":tests", From 6270b93245f11b448bb3c291e9958edbb9fe3e64 Mon Sep 17 00:00:00 2001 From: Ben Lee Date: Fri, 20 Mar 2026 15:10:25 -0700 Subject: [PATCH 13/13] Revert --- .../javatests/com/google/devtools/build/android/dexer/BUILD | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD index 644bf548..16e5a699 100644 --- a/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD +++ b/src/tools/javatests/com/google/devtools/build/android/dexer/BUILD @@ -58,9 +58,9 @@ java_test( ":simple_jar", ], jvm_flags = [ - "-Djsimplejar=$(rlocationpath :jsimple_jar)", - "-Dmultidexjar=$(rlocationpath :multidex_jar)", - "-Dsimplejar=$(rlocationpath :simple_jar)", + "-Djsimplejar=google3/$(location :jsimple_jar)", + "-Dmultidexjar=google3/$(location :multidex_jar)", + "-Dsimplejar=google3/$(location :simple_jar)", ], runtime_deps = [ ":tests",