val runningGradle5 = gradle.gradleVersion.startsWith("5.") val guavaVersionJre = "(.*)".toRegex().find(file("../../pom.xml").readText())?.groups?.get(1)?.value ?: error("version not found in pom") val expectedReducedRuntimeClasspathAndroidVersion = setOf( "guava-${guavaVersionJre.replace("jre", "android")}.jar", "failureaccess-1.0.2.jar", "j2objc-annotations-3.0.0.jar", "jsr305-3.0.2.jar", "checker-qual-3.43.0.jar", "error_prone_annotations-2.28.0.jar", "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" ) val expectedReducedRuntimeClasspathJreVersion = setOf( "guava-$guavaVersionJre.jar", "failureaccess-1.0.2.jar", "j2objc-annotations-3.0.0.jar", "jsr305-3.0.2.jar", "checker-qual-3.43.0.jar", "error_prone_annotations-2.28.0.jar", "listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" ) val expectedCompileClasspathAndroidVersion = expectedReducedRuntimeClasspathAndroidVersion val expectedCompileClasspathJreVersion = expectedReducedRuntimeClasspathJreVersion val extraLegacyDependencies = setOf("google-collections-1.0.jar") buildscript { val agpVersion = if (gradle.gradleVersion.startsWith("5.")) "3.6.4" else "7.0.4" repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:$agpVersion") { exclude( group = "org.jetbrains.trove4j" ) // Might not be available on Maven Central and not needed for this test } } } subprojects { if (name.endsWith("Java")) { apply(plugin = "java-library") } else { apply(plugin = "com.android.application") the().compileSdkVersion(30) } var expectedClasspath = if (runningGradle5) { // without Gradle Module Metadata (only the POM is used) // - variant decision is made based on version suffix (android/jre) and not on the actual // environment // - runtime classpath equals the compile classpath // - dependency conflict with Google Collections is not detected if (name.startsWith("android")) { expectedCompileClasspathAndroidVersion + extraLegacyDependencies } else { expectedCompileClasspathJreVersion + extraLegacyDependencies } } else { // with Gradle Module Metadata // - variant is chosen based on the actual environment, independent of version suffix // - reduced runtime classpath is used (w/o annotation libraries) // - capability conflicts are detected with Google Collections if (name.contains("Android") && !name.contains("JreConstraint")) { when { name.contains("RuntimeClasspath") -> { expectedReducedRuntimeClasspathAndroidVersion } name.contains("CompileClasspath") -> { expectedCompileClasspathAndroidVersion } else -> { error("unexpected classpath type: $name") } } } else { when { name.contains("RuntimeClasspath") -> { expectedReducedRuntimeClasspathJreVersion } name.contains("CompileClasspath") -> { expectedCompileClasspathJreVersion } else -> { error("unexpected classpath type: $name") } } } } val guavaVersion = if (name.startsWith("android")) { guavaVersionJre.replace("jre", "android") } else { guavaVersionJre } val javaVersion = JavaVersion.VERSION_1_8 repositories { mavenCentral() mavenLocal() } val java = the() java.targetCompatibility = javaVersion java.sourceCompatibility = javaVersion if (!runningGradle5) { configurations.all { resolutionStrategy.capabilitiesResolution { withCapability("com.google.collections:google-collections") { candidates .find { val idField = it.javaClass.getDeclaredMethod( "getId" ) // reflective access to make this compile with Gradle 5 (idField.invoke(it) as ModuleComponentIdentifier).module == "guava" } ?.apply { select(this) } } } } if (name.contains("AndroidConstraint")) { dependencies { constraints { "api"("com.google.guava:guava") { attributes { // if the Gradle version is 7+, you can use // TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE attribute(Attribute.of("org.gradle.jvm.environment", String::class.java), "android") } } } } configurations.all { resolutionStrategy.capabilitiesResolution { withCapability("com.google.guava:guava") { candidates .find { val variantName = it.javaClass.getDeclaredMethod("getVariantName") (variantName.invoke(it) as String).contains("android") } ?.apply { select(this) } } } } } if (name.contains("JreConstraint")) { dependencies { constraints { "api"("com.google.guava:guava") { attributes { // if the Gradle version is 7+, you can use // TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE attribute( Attribute.of("org.gradle.jvm.environment", String::class.java), "standard-jvm" ) } } } } configurations.all { resolutionStrategy.capabilitiesResolution { withCapability("com.google.guava:guava") { candidates .find { val variantName = it.javaClass.getDeclaredMethod("getVariantName") (variantName.invoke(it) as String).contains("jre") } ?.apply { select(this) } } } } } } dependencies { "api"("com.google.collections:google-collections:1.0") "api"("com.google.guava:listenablefuture:1.0") "api"("com.google.guava:guava:$guavaVersion") } tasks.register("testClasspath") { doLast { val classpathConfiguration = if (project.name.contains("RuntimeClasspath")) { if (project.name.endsWith("Java")) configurations["runtimeClasspath"] else configurations["debugRuntimeClasspath"] } else if (project.name.contains("CompileClasspath")) { if (project.name.endsWith("Java")) configurations["compileClasspath"] else configurations["debugCompileClasspath"] } else { error("unexpected classpath type: " + project.name) } val actualClasspath = classpathConfiguration.files.map { it.name }.toSet() if (actualClasspath != expectedClasspath) { throw RuntimeException( """ Expected: ${expectedClasspath.sorted()} Actual: ${actualClasspath.sorted()} """ .trimIndent() ) } } } }