Skip to content

Commit e44132f

Browse files
authored
Avoid capturing module descriptor in JdkImageWorkaround for AGP >= 8 (#659)
#646 describes the issue when applying the plugin using not aligned JDKs in the toolchain and the build, build fails on the JdkImageWorkaround with the following message: ``` Execution failed for task ':app:compileDebugJavaWithJavac'. > Could not resolve all files for configuration ':app:androidJdkImage'. > Failed to transform core-for-system-modules.jar to match attributes {artifactType=_internal_android_jdk_image_extracted, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}. > Execution failed for ExtractJdkImageTransform: /Users/kio/.gradle/caches/transforms-3/066939602350b840bee1b3625dbd3a64/transformed/output. > Unsupported major.minor version 64.0 ``` After syncing with the BT team, we are proposing this PR where the main change is to not use the module descriptor after extracting the JDK generated by AGP. ## Background The main motivation of the `JdkImageWorkaround` is to normalize the inconsequential differences between JDKs. A more detailed explanation can be found [here](https://issuetracker.google.com/u/1/issues/234820480). Our workaround fixed completely the issue in different JDK/AGP versions despite the claim that this issue was fixed in AGP 7.4. Last year we experimented with different JDK vendors and we noticed the issue in some specific versions of JDK 11 and JDK 17. We repeated the experiment last week targeting AGP >= 8 and JDKs [17-21] observing the issue is [present](https://ge.solutions-team.gradle.com/c/jgotrldjuvgks/wvig5icz75ybi/task-inputs?expanded=WyJhcmJ1cTRtb2x6eDR5LW9wdGlvbnMuY29tcGlsZXJhcmd1bWVudHByb3ZpZGVycy4kMC5qcnRmc2phciJd) for JDK 21. It makes sense to keep the workaround for recent Java/AGP versions. ## Toolchain issue In all the different experiments we used aligned JDK versions between the build and the toolchain configuration. However, when we use a greater JDK version in the `JavaCompile` task , through toolchain conf, than the JDK build, we get the exception when trying to apply the logic of the workaround which creates a new module-descriptor file dropping the unnecessary versions: ``` File moduleInfoFile = new File(targetDir, 'java.base/module-info.class') ModuleDescriptor descriptor = captureModuleDescriptorWithoutVersion(moduleInfoFile) File descriptorData = new File(targetDir, "module-descriptor.txt") ``` The exception makes sense because the "extracted jdk" is created with the toolchain JDK version and we try to read it with a lower JDK version. We are able to reproduce the issue in AGP 7.x versions. ## AGP fix on the JDK differences Although the original issue(aligned jdks) is present in AGP 8.2.2/JDK 21, we analyzed the AGP changes in 7.4 fixing partially this issue ([details](https://issuetracker.google.com/u/1/issues/234820480#comment5)). Following the AGP classes `JdkImageTransform` and `JdkImageTransformDelegate`, we observed they are [already](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/internal/dependency/JdkImageTransformDelegate.kt;l=264?q=JdkImageTransformDelegate) creating a "module-info.java" file describing the contents of the "module.class" dropping the versions as we are doing in the workaround. We don't need to apply the same logic in the plugin. ## Using ModuleDescriptor based on the AGP version Knowing we don't need to generate a new "module-info.java" in the transform, it fixes the issue because the original exception is caused by capturing the module descriptor of the class file to generate the new info file. Still, we need to create the info file in AGP versions before the fix was introduced: ``` if (Versions.CURRENT_ANDROID_VERSION.major < 8) { File moduleInfoFile = new File(targetDir, 'java.base/module-info.class') ModuleDescriptor descriptor = captureModuleDescriptorWithoutVersion(moduleInfoFile) File descriptorData = new File(targetDir, "module-descriptor.txt") ... } ``` ## Toolchain state after fix | | No workaround | Current workaround | This PR | |-----------------------------------------|------------------------|--------------------|---------| | AGP 8.x with toolchains - JDK 21 | Success | Error | Success | | AGP 8.x without toolchains - JDK 21 | Build cache difference | Success | Success | ## Tests ### JDK Build 17 - JDK Toolchain 20 Before: * Build Failed https://ge.solutions-team.gradle.com/s/75jkfgb652u3g After: * Build https://ge.solutions-team.gradle.com/s/wnkkcskifuwks/timeline?details=arbuq4molzx4y&expanded=WyI0Il0 * JDK 17.0.2 Zulu vs JDK 17 Temurin https://ge.solutions-team.gradle.com/c/wnkkcskifuwks/zndww6rospvws/task-inputs ## Additional notes The issue is still present when using the workaround with AGP 7.x and toolchains not aligned. This PR adds new test using toolchain 19 for AGP > 8
2 parents 5f4fc19 + 869ca61 commit e44132f

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

src/main/groovy/org/gradle/android/workarounds/JdkImageWorkaround.groovy

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.gradle.android.workarounds
33
import com.google.common.annotations.VisibleForTesting
44
import com.google.common.collect.Lists
55
import org.gradle.android.AndroidIssue
6+
import org.gradle.android.Versions
67
import org.gradle.api.Project
78
import org.gradle.api.artifacts.transform.CacheableTransform
89
import org.gradle.api.artifacts.transform.InputArtifact
@@ -188,14 +189,16 @@ class JdkImageWorkaround implements Workaround {
188189
)
189190
}
190191

191-
// Capture the module descriptor ignoring the version, which is not enforced anyways
192-
File moduleInfoFile = new File(targetDir, 'java.base/module-info.class')
193-
ModuleDescriptor descriptor = captureModuleDescriptorWithoutVersion(moduleInfoFile)
194-
File descriptorData = new File(targetDir, "module-descriptor.txt")
195-
descriptorData.text = serializeDescriptor(descriptor)
196-
197-
fileOperations.delete {
198-
delete(moduleInfoFile)
192+
// Starting with AGP 8 only the major Java version is stored in the output so we don't need any normalization
193+
if (Versions.CURRENT_ANDROID_VERSION.major < 8) {
194+
// Capture the module descriptor ignoring the version, which is not enforced anyways
195+
File moduleInfoFile = new File(targetDir, 'java.base/module-info.class')
196+
ModuleDescriptor descriptor = captureModuleDescriptorWithoutVersion(moduleInfoFile)
197+
File descriptorData = new File(targetDir, "module-descriptor.txt")
198+
descriptorData.text = serializeDescriptor(descriptor)
199+
fileOperations.delete {
200+
delete(moduleInfoFile)
201+
}
199202
}
200203
}
201204

src/test/groovy/org/gradle/android/JdkImageWorkaroundTest.groovy

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,57 @@ class JdkImageWorkaroundTest extends AbstractTest {
257257
buildResult.task(':library:compileDebugJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
258258
buildResult.task(':library:compileReleaseJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
259259
}
260+
261+
def "jdkImage is normalized when using different toolchain configuration"() {
262+
263+
Assume.assumeTrue("Android Gradle Plugin < 8", androidVersion >= VersionNumber.parse("8.0"))
264+
265+
266+
def gradleVersion = TestVersions.latestSupportedGradleVersionFor(androidVersion)
267+
SimpleAndroidApp.builder(temporaryFolder.root, cacheDir)
268+
.withAndroidVersion(androidVersion)
269+
.withKotlinDisabled()
270+
.withToolchainVersion("19")
271+
.withSourceCompatibility(JavaVersion.VERSION_1_9)
272+
.withDatabindingDisabled()
273+
.build()
274+
.writeProject()
275+
276+
when:
277+
BuildResult buildResult = withGradleVersion(gradleVersion.version)
278+
.withProjectDir(temporaryFolder.root)
279+
.withArguments(
280+
"clean", "testDebug", "testRelease", "assemble",
281+
"--build-cache"
282+
).build()
283+
284+
then:
285+
buildResult.task(':app:compileDebugJavaWithJavac').outcome == TaskOutcome.SUCCESS
286+
buildResult.task(':library:compileDebugJavaWithJavac').outcome == TaskOutcome.SUCCESS
287+
288+
buildResult.task(':app:compileDebugUnitTestJavaWithJavac').outcome == TaskOutcome.SUCCESS
289+
buildResult.task(':library:compileDebugUnitTestJavaWithJavac').outcome == TaskOutcome.SUCCESS
290+
291+
when:
292+
buildResult = withGradleVersion(gradleVersion.version)
293+
.withProjectDir(temporaryFolder.root)
294+
.withArguments(
295+
"clean", "testDebug", "testRelease", "assemble",
296+
"--build-cache"
297+
).build()
298+
299+
then:
300+
buildResult.task(':app:compileDebugJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
301+
buildResult.task(':app:compileReleaseJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
302+
buildResult.task(':library:compileDebugJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
303+
buildResult.task(':library:compileReleaseJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
304+
305+
buildResult.task(':app:compileDebugUnitTestJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
306+
buildResult.task(':app:compileReleaseUnitTestJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
307+
buildResult.task(':library:compileDebugUnitTestJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
308+
buildResult.task(':library:compileReleaseUnitTestJavaWithJavac').outcome == TaskOutcome.FROM_CACHE
309+
310+
where:
311+
androidVersion << TestVersions.latestAndroidVersions
312+
}
260313
}

0 commit comments

Comments
 (0)