From 6b4fe4cc834ede1134b37ff99de7141a177308b4 Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Fri, 8 Aug 2025 12:11:15 -0700 Subject: [PATCH 1/4] Simplify LicensesCleanUpTask Delete the entire directory instead of individual files. This is in prepration to moving LicensesTask to a single output directory to make it compatible with the new AGP variants API. --- .../plugin/LicensesCleanUpTask.groovy | 44 ++++++------------- .../licenses/plugin/OssLicensesPlugin.groovy | 8 +--- .../plugin/LicensesCleanUpTaskTest.java | 34 +++++++------- 3 files changed, 31 insertions(+), 55 deletions(-) diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTask.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTask.groovy index e5ec315b..815e9b4f 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTask.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTask.groovy @@ -17,44 +17,26 @@ package com.google.android.gms.oss.licenses.plugin import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault /** - * Task to clean up the generated dependency.json, third_party_licenses and - * third_party_license_metadata files. + * Task to clean up the generated files. */ -class LicensesCleanUpTask extends DefaultTask { - - protected File dependenciesJson - - protected File dependencyDir - - protected File licensesFile - - protected File metadataFile - - protected File licensesDir +@DisableCachingByDefault(because = "Local deletion operation") +abstract class LicensesCleanUpTask extends DefaultTask { + @Optional + @InputDirectory + abstract DirectoryProperty getGeneratedDirectory() @TaskAction void action() { - if (dependenciesJson.exists()) { - dependenciesJson.delete() - } - - if (dependencyDir.isDirectory() && dependencyDir.list().length == 0) { - dependencyDir.delete() - } - - if (licensesFile.exists()) { - licensesFile.delete() - } - - if (metadataFile.exists()) { - metadataFile.delete() - } - - if (licensesDir.isDirectory() && licensesDir.list().length == 0) { - licensesDir.delete() + File directoryToDelete = getGeneratedDirectory().get().asFile + if (directoryToDelete.exists()) { + directoryToDelete.deleteDir() } } } diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy index 7ec1bbf9..45c43523 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy @@ -32,7 +32,7 @@ class OssLicensesPlugin implements Plugin { def variantTolicenseTaskMap = new HashMap() project.androidComponents { onVariants(selector().all(), { variant -> - def baseDir = new File(project.buildDir, + File baseDir = new File(project.buildDir, "generated/third_party_licenses/${variant.name}") def dependenciesJson = new File(baseDir, "dependencies.json") @@ -68,11 +68,7 @@ class OssLicensesPlugin implements Plugin { TaskProvider cleanupTask = project.tasks.register( cleanupTaskName, LicensesCleanUpTask.class) { - it.dependenciesJson = dependenciesJson - it.dependencyDir = baseDir - it.licensesFile = licensesFile - it.metadataFile = licensesMetadataFile - it.licensesDir = rawResourceDir + it.generatedDirectory.set(baseDir) } logger.debug("Registered task $cleanupTaskName") diff --git a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTaskTest.java b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTaskTest.java index ffcc4e61..36698fcf 100644 --- a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTaskTest.java +++ b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesCleanUpTaskTest.java @@ -15,6 +15,7 @@ package com.google.android.gms.oss.licenses.plugin; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; @@ -37,31 +38,28 @@ public class LicensesCleanUpTaskTest { public void testAction() throws IOException { File testDir = temporaryFolder.newFolder(); - File dependencyDir = new File(testDir, "dependency"); - dependencyDir.mkdir(); - + // Set up a generated directory with normal expected contents + File generatedDir = new File(testDir, "generated"); + File dependencyDir = new File(generatedDir, "dependency"); + assertTrue(dependencyDir.mkdirs()); File dependencyFile = new File(dependencyDir, "dependency.json"); + assertTrue(dependencyFile.createNewFile()); - File licensesDir = new File(testDir, "raw"); - licensesDir.mkdir(); - - File licensesFile = new File(licensesDir, "third_party_licenses"); - File metadataFile = new File(licensesDir, "third_party_license_metadata"); + File licensesDir = new File(generatedDir, "res/raw"); + assertTrue(licensesDir.mkdirs()); + assertTrue(new File(licensesDir, "third_party_licenses").createNewFile()); + assertTrue(new File(licensesDir, "third_party_license_metadata").createNewFile()); + // Create a licenses clean up task Project project = ProjectBuilder.builder().withProjectDir(testDir).build(); LicensesCleanUpTask task = project.getTasks().create("licensesCleanUp", LicensesCleanUpTask.class); - task.dependencyDir = dependencyDir; - task.dependenciesJson = dependencyFile; - task.licensesDir = licensesDir; - task.licensesFile = licensesFile; - task.metadataFile = metadataFile; + task.getGeneratedDirectory().set(generatedDir); + // Run the task action task.action(); - assertFalse(task.dependenciesJson.exists()); - assertFalse(task.dependencyDir.exists()); - assertFalse(task.licensesFile.exists()); - assertFalse(task.metadataFile.exists()); - assertFalse(task.licensesDir.exists()); + + // Ensure the directory is deleted + assertFalse(generatedDir.exists()); } } From 1978387cd8ff3a28c01aa92ab42087fe4d06ca72 Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Fri, 8 Aug 2025 13:02:13 -0700 Subject: [PATCH 2/4] Move to a single output directory for LicensesTask This is in preparation to use the new AGP variants API to register resource creation. --- .../oss/licenses/plugin/LicensesTask.groovy | 22 +++----- .../licenses/plugin/OssLicensesPlugin.groovy | 12 +---- .../oss/licenses/plugin/LicensesTaskTest.java | 50 ++++++++++--------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy index a5d6e483..8c6a94af 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy @@ -19,8 +19,10 @@ package com.google.android.gms.oss.licenses.plugin import groovy.json.JsonSlurper import groovy.xml.XmlSlurper import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -62,19 +64,17 @@ abstract class LicensesTask extends DefaultTask { abstract RegularFileProperty getDependenciesJson() @OutputDirectory - File rawResourceDir + abstract DirectoryProperty getRawResourceDir() - @OutputFile + @Internal // represented by getRawResourceDir() File licenses - @OutputFile + @Internal // represented by getRawResourceDir() File licensesMetadata @TaskAction void action() { initOutputDir() - initLicenseFile() - initLicensesMetadata() File dependenciesJsonFile = dependenciesJson.asFile.get() def artifactInfoSet = loadDependenciesJson(dependenciesJsonFile) @@ -126,21 +126,15 @@ abstract class LicensesTask extends DefaultTask { } protected void initOutputDir() { + File rawResourceDir = getRawResourceDir().get().asFile if (!rawResourceDir.exists()) { rawResourceDir.mkdirs() } - } - - protected void initLicenseFile() { - if (licenses == null) { - logger.error("License file is undefined") - } + licenses = new File(rawResourceDir, "third_party_licenses") licenses.newWriter().withWriter { w -> w << '' } - } - - protected void initLicensesMetadata() { + licensesMetadata = new File(rawResourceDir, "third_party_license_metadata") licensesMetadata.newWriter().withWriter { w -> w << '' } diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy index 45c43523..395ea258 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy @@ -45,20 +45,12 @@ class OssLicensesPlugin implements Plugin { } logger.debug("Registered task $dependencyTaskName") - def resourceBaseDir = new File(baseDir, "/res") - def rawResourceDir = new File(resourceBaseDir, "/raw") - def licensesFile = new File(rawResourceDir, "third_party_licenses") - def licensesMetadataFile = new File(rawResourceDir, - "third_party_license_metadata") - def licenseTask = project.tasks.register( "${variant.name}OssLicensesTask", LicensesTask.class) { markNotCompatibleWithConfigurationCache(it) it.dependenciesJson.set(dependencyTask.flatMap { it.dependenciesJson }) - it.rawResourceDir = rawResourceDir - it.licenses = licensesFile - it.licensesMetadata = licensesMetadataFile + it.getRawResourceDir().set(new File(baseDir, "/res/raw")) }.get() logger.debug("Created task ${licenseTask.name}") @@ -85,7 +77,7 @@ class OssLicensesPlugin implements Plugin { if (licenseTask == null) { return } - def generatedResFolder = project.files(licenseTask.rawResourceDir.parentFile).builtBy(licenseTask) + def generatedResFolder = project.files(licenseTask.getRawResourceDir().get().asFile.parentFile).builtBy(licenseTask) variant.registerGeneratedResFolders(generatedResFolder) } } diff --git a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java index 3472c809..1ce227e8 100644 --- a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java +++ b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java @@ -63,38 +63,27 @@ public class LicensesTaskTest { @Before public void setUp() throws IOException { File outputDir = temporaryFolder.newFolder(); - File outputLicenses = new File(outputDir, "testLicenses"); - File outputMetadata = new File(outputDir, "testMetadata"); project = ProjectBuilder.builder().withProjectDir(new File(BASE_DIR)).build(); licensesTask = project.getTasks().create("generateLicenses", LicensesTask.class); - licensesTask.setRawResourceDir(outputDir); - licensesTask.setLicenses(outputLicenses); - licensesTask.setLicensesMetadata(outputMetadata); + licensesTask.getRawResourceDir().set(outputDir); } @Test - public void testInitOutputDir() { + public void testInitOutputDir() throws IOException { licensesTask.initOutputDir(); - assertTrue(licensesTask.getRawResourceDir().exists()); - } - - @Test - public void testInitLicenseFile() throws IOException { - licensesTask.initLicenseFile(); - - assertTrue(licensesTask.getLicenses().exists()); - assertEquals(0, Files.size(licensesTask.getLicenses().toPath())); - } + File rawResourceDir = licensesTask.getRawResourceDir().get().getAsFile(); + assertTrue(rawResourceDir.exists()); - @Test - public void testInitLicensesMetadata() throws IOException { - licensesTask.initLicensesMetadata(); + File licenses = new File(rawResourceDir, "third_party_licenses"); + assertTrue(licenses.exists()); + assertEquals(0, Files.size(licenses.toPath())); - assertTrue(licensesTask.getLicensesMetadata().exists()); - assertEquals(0, Files.size(licensesTask.getLicensesMetadata().toPath())); + File licensesMetadata = new File(rawResourceDir, "third_party_license_metadata"); + assertTrue(licensesMetadata.exists()); + assertEquals(0, Files.size(licensesMetadata.toPath())); } @Test @@ -114,6 +103,7 @@ public void testAddLicensesFromPom() throws IOException { File deps1 = getResourceFile("dependencies/groupA/deps1.pom"); String name1 = "deps1"; String group1 = "groupA"; + licensesTask.initOutputDir(); licensesTask.addLicensesFromPom(deps1, group1, name1); String content = new String(Files.readAllBytes(licensesTask.getLicenses().toPath()), UTF_8); @@ -124,9 +114,12 @@ public void testAddLicensesFromPom() throws IOException { @Test public void testAddLicensesFromPom_withoutDuplicate() throws IOException { + licensesTask.initOutputDir(); + File deps1 = getResourceFile("dependencies/groupA/deps1.pom"); String name1 = "deps1"; String group1 = "groupA"; + licensesTask.initOutputDir(); licensesTask.addLicensesFromPom(deps1, group1, name1); File deps2 = getResourceFile("dependencies/groupB/bcd/deps2.pom"); @@ -152,6 +145,7 @@ public void testAddLicensesFromPom_withMultiple() throws IOException { File deps1 = getResourceFile("dependencies/groupA/deps1.pom"); String name1 = "deps1"; String group1 = "groupA"; + licensesTask.initOutputDir(); licensesTask.addLicensesFromPom(deps1, group1, name1); File deps2 = getResourceFile("dependencies/groupE/deps5.pom"); @@ -178,6 +172,7 @@ public void testAddLicensesFromPom_withDuplicate() throws IOException { File deps1 = getResourceFile("dependencies/groupA/deps1.pom"); String name1 = "deps1"; String group1 = "groupA"; + licensesTask.initOutputDir(); licensesTask.addLicensesFromPom(deps1, group1, name1); File deps2 = getResourceFile("dependencies/groupA/deps1.pom"); @@ -227,10 +222,12 @@ public void testGetBytesFromInputStream_specialCharacters() { @Test public void testAddGooglePlayServiceLicenses() throws IOException { - File tempOutput = new File(licensesTask.getRawResourceDir(), "dependencies/groupC"); + File tempOutput = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupC"); tempOutput.mkdirs(); createLicenseZip(tempOutput.getPath() + "play-services-foo-license.aar"); File artifact = new File(tempOutput.getPath() + "play-services-foo-license.aar"); + + licensesTask.initOutputDir(); licensesTask.addGooglePlayServiceLicenses(artifact); String content = new String(Files.readAllBytes(licensesTask.getLicenses().toPath()), UTF_8); @@ -246,16 +243,17 @@ public void testAddGooglePlayServiceLicenses() throws IOException { @Test public void testAddGooglePlayServiceLicenses_withoutDuplicate() throws IOException { - File groupC = new File(licensesTask.getRawResourceDir(), "dependencies/groupC"); + File groupC = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupC"); groupC.mkdirs(); createLicenseZip(groupC.getPath() + "/play-services-foo-license.aar"); File artifactFoo = new File(groupC.getPath() + "/play-services-foo-license.aar"); - File groupD = new File(licensesTask.getRawResourceDir(), "dependencies/groupD"); + File groupD = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupD"); groupD.mkdirs(); createLicenseZip(groupD.getPath() + "/play-services-bar-license.aar"); File artifactBar = new File(groupD.getPath() + "/play-services-bar-license.aar"); + licensesTask.initOutputDir(); licensesTask.addGooglePlayServiceLicenses(artifactFoo); licensesTask.addGooglePlayServiceLicenses(artifactBar); @@ -286,6 +284,7 @@ private void createLicenseZip(String name) throws IOException { @Test public void testAppendLicense() throws IOException { + licensesTask.initOutputDir(); licensesTask.appendDependency( new LicensesTask.Dependency("license1", "license1"), "test".getBytes(UTF_8)); @@ -302,6 +301,8 @@ public void testWriteMetadata() throws IOException { LicensesTask.Dependency dep2 = new LicensesTask.Dependency("test:bar", "Dependency 2"); licensesTask.licensesMap.put(dep1.getKey(), dep1.buildLicensesMetadata("0:4")); licensesTask.licensesMap.put(dep2.getKey(), dep2.buildLicensesMetadata("6:10")); + + licensesTask.initOutputDir(); licensesTask.writeMetadata(); String expected = "0:4 Dependency 1" + LINE_BREAK + "6:10 Dependency 2" + LINE_BREAK; @@ -315,6 +316,7 @@ public void testDependenciesWithNameDuplicatedNames() throws IOException { File deps6 = getResourceFile("dependencies/groupF/deps6.pom"); String name1 = "deps6"; String group1 = "groupF"; + licensesTask.initOutputDir(); licensesTask.addLicensesFromPom(deps6, group1, name1); File deps7 = getResourceFile("dependencies/groupF/deps7.pom"); From 4458fd797aa5319999983dae37735afdd8f93185 Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Fri, 8 Aug 2025 13:12:23 -0700 Subject: [PATCH 3/4] Move to the new AGP variants API The old API is getting removed with AGP 9.0.0. This bumps the minimum required version of AGP to 8.2 and moves to the new variants API. --- oss-licenses-plugin/build.gradle.kts | 4 +-- .../oss/licenses/plugin/LicensesTask.groovy | 8 ++--- .../licenses/plugin/OssLicensesPlugin.groovy | 33 +++++-------------- .../gms/oss/licenses/plugin/EndToEndTest.kt | 5 +-- .../oss/licenses/plugin/LicensesTaskTest.java | 10 +++--- 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/oss-licenses-plugin/build.gradle.kts b/oss-licenses-plugin/build.gradle.kts index 26f158b7..ac726782 100644 --- a/oss-licenses-plugin/build.gradle.kts +++ b/oss-licenses-plugin/build.gradle.kts @@ -35,8 +35,8 @@ gradlePlugin { dependencies { implementation(gradleApi()) implementation(localGroovy()) - implementation("com.android.tools.build:gradle:7.1.0") - implementation("com.android.tools.build:gradle-api:7.1.0") + implementation("com.android.tools.build:gradle:8.2.0") + implementation("com.android.tools.build:gradle-api:8.2.0") implementation("com.google.protobuf:protobuf-java:3.19.1") testImplementation("junit:junit:4.13.2") testImplementation("org.mockito:mockito-core:4.1.0") diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy index 8c6a94af..0ce482ba 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy @@ -64,12 +64,12 @@ abstract class LicensesTask extends DefaultTask { abstract RegularFileProperty getDependenciesJson() @OutputDirectory - abstract DirectoryProperty getRawResourceDir() + abstract DirectoryProperty getGeneratedDirectory() - @Internal // represented by getRawResourceDir() + @Internal // represented by getGeneratedDirectory() File licenses - @Internal // represented by getRawResourceDir() + @Internal // represented by getGeneratedDirectory() File licensesMetadata @TaskAction @@ -126,7 +126,7 @@ abstract class LicensesTask extends DefaultTask { } protected void initOutputDir() { - File rawResourceDir = getRawResourceDir().get().asFile + File rawResourceDir = new File(getGeneratedDirectory().get().asFile, "raw") if (!rawResourceDir.exists()) { rawResourceDir.mkdirs() } diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy index 395ea258..be49c928 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy @@ -17,7 +17,6 @@ package com.google.android.gms.oss.licenses.plugin import com.android.build.api.artifact.SingleArtifact -import com.android.build.gradle.api.BaseVariant import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task @@ -29,57 +28,41 @@ class OssLicensesPlugin implements Plugin { private static final logger = LoggerFactory.getLogger(DependencyTask.class) void apply(Project project) { - def variantTolicenseTaskMap = new HashMap() project.androidComponents { onVariants(selector().all(), { variant -> File baseDir = new File(project.buildDir, "generated/third_party_licenses/${variant.name}") def dependenciesJson = new File(baseDir, "dependencies.json") - String dependencyTaskName = "${variant.name}OssDependencyTask" TaskProvider dependencyTask = project.tasks.register( - dependencyTaskName, + "${variant.name}OssDependencyTask", DependencyTask.class) { it.dependenciesJson.set(dependenciesJson) it.libraryDependenciesReport.set(variant.artifacts.get(SingleArtifact.METADATA_LIBRARY_DEPENDENCIES_REPORT.INSTANCE)) } - logger.debug("Registered task $dependencyTaskName") + logger.debug("Registered task ${dependencyTask.name}") - def licenseTask = project.tasks.register( + TaskProvider licenseTask = project.tasks.register( "${variant.name}OssLicensesTask", LicensesTask.class) { markNotCompatibleWithConfigurationCache(it) it.dependenciesJson.set(dependencyTask.flatMap { it.dependenciesJson }) - it.getRawResourceDir().set(new File(baseDir, "/res/raw")) - }.get() - logger.debug("Created task ${licenseTask.name}") - - variantTolicenseTaskMap[variant.name] = licenseTask + } + logger.debug("Registered task ${licenseTask.name}") + variant.sources.resources.addGeneratedSourceDirectory(licenseTask, LicensesTask::getGeneratedDirectory) - String cleanupTaskName = "${variant.name}OssLicensesCleanUp" TaskProvider cleanupTask = project.tasks.register( - cleanupTaskName, + "${variant.name}OssLicensesCleanUp", LicensesCleanUpTask.class) { it.generatedDirectory.set(baseDir) } - logger.debug("Registered task $cleanupTaskName") + logger.debug("Registered task ${cleanupTask.name}") project.tasks.named("clean").configure { it.dependsOn(cleanupTask) } }) } - - // TODO: Switch to new Variant API when API is ready and before - // BaseVariant is removed in 8.0 - project.android.applicationVariants.all { BaseVariant variant -> - def licenseTask = variantTolicenseTaskMap[variant.name] - if (licenseTask == null) { - return - } - def generatedResFolder = project.files(licenseTask.getRawResourceDir().get().asFile.parentFile).builtBy(licenseTask) - variant.registerGeneratedResFolders(generatedResFolder) - } } private static void markNotCompatibleWithConfigurationCache(Task it) { diff --git a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/EndToEndTest.kt b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/EndToEndTest.kt index e8dc48dd..226467a3 100644 --- a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/EndToEndTest.kt +++ b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/EndToEndTest.kt @@ -27,6 +27,7 @@ class EndToEndTest { } android { compileSdkVersion = "android-31" + namespace = "com.example.app" } dependencies { implementation("com.google.android.gms:play-services-oss-licenses:17.0.0") @@ -40,14 +41,14 @@ class EndToEndTest { ) val result = GradleRunner.create() .withProjectDir(projectDir) - .withGradleVersion("7.6") + .withGradleVersion("8.2") .withArguments("releaseOssLicensesTask", "-s") .withPluginClasspath().build() Assert.assertEquals(result.task(":collectReleaseDependencies")!!.outcome, TaskOutcome.SUCCESS) Assert.assertEquals(result.task(":releaseOssDependencyTask")!!.outcome, TaskOutcome.SUCCESS) Assert.assertEquals(result.task(":releaseOssLicensesTask")!!.outcome, TaskOutcome.SUCCESS) val metadata = - File(projectDir, "build/generated/third_party_licenses/release/res/raw/third_party_license_metadata") + File(projectDir, "build/generated/resources/releaseOssLicensesTask/raw/third_party_license_metadata") Assert.assertTrue(metadata.readText().contains("play-services-oss-licenses")) } } diff --git a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java index 1ce227e8..aeadb413 100644 --- a/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java +++ b/oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/LicensesTaskTest.java @@ -67,14 +67,14 @@ public void setUp() throws IOException { project = ProjectBuilder.builder().withProjectDir(new File(BASE_DIR)).build(); licensesTask = project.getTasks().create("generateLicenses", LicensesTask.class); - licensesTask.getRawResourceDir().set(outputDir); + licensesTask.getGeneratedDirectory().set(outputDir); } @Test public void testInitOutputDir() throws IOException { licensesTask.initOutputDir(); - File rawResourceDir = licensesTask.getRawResourceDir().get().getAsFile(); + File rawResourceDir = new File(licensesTask.getGeneratedDirectory().get().getAsFile(), "raw"); assertTrue(rawResourceDir.exists()); File licenses = new File(rawResourceDir, "third_party_licenses"); @@ -222,7 +222,7 @@ public void testGetBytesFromInputStream_specialCharacters() { @Test public void testAddGooglePlayServiceLicenses() throws IOException { - File tempOutput = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupC"); + File tempOutput = temporaryFolder.newFolder(); tempOutput.mkdirs(); createLicenseZip(tempOutput.getPath() + "play-services-foo-license.aar"); File artifact = new File(tempOutput.getPath() + "play-services-foo-license.aar"); @@ -243,12 +243,12 @@ public void testAddGooglePlayServiceLicenses() throws IOException { @Test public void testAddGooglePlayServiceLicenses_withoutDuplicate() throws IOException { - File groupC = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupC"); + File groupC = temporaryFolder.newFolder(); groupC.mkdirs(); createLicenseZip(groupC.getPath() + "/play-services-foo-license.aar"); File artifactFoo = new File(groupC.getPath() + "/play-services-foo-license.aar"); - File groupD = new File(licensesTask.getRawResourceDir().get().getAsFile(), "dependencies/groupD"); + File groupD = temporaryFolder.newFolder(); groupD.mkdirs(); createLicenseZip(groupD.getPath() + "/play-services-bar-license.aar"); File artifactBar = new File(groupD.getPath() + "/play-services-bar-license.aar"); From adfc1c8c07f2340e95dab4b86a6144b5146cb435 Mon Sep 17 00:00:00 2001 From: Aurimas Liutikas Date: Fri, 8 Aug 2025 13:50:55 -0700 Subject: [PATCH 4/4] Make OSS licenses plugin reactive Prior to this change there was an implicit order of application of plugins with com.android.application having to be applied before com.google.android.gms.oss-licenses-plugin. This change makes licenses plugin apply all of the logic despite the order of application. --- .../licenses/plugin/OssLicensesPlugin.groovy | 74 ++++++++++--------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy index be49c928..f4bce952 100644 --- a/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy +++ b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy @@ -17,51 +17,57 @@ package com.google.android.gms.oss.licenses.plugin import com.android.build.api.artifact.SingleArtifact +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.ApplicationVariant +import com.android.build.gradle.AppPlugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.Directory +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider -import org.slf4j.LoggerFactory class OssLicensesPlugin implements Plugin { - - private static final logger = LoggerFactory.getLogger(DependencyTask.class) - void apply(Project project) { - project.androidComponents { - onVariants(selector().all(), { variant -> - File baseDir = new File(project.buildDir, - "generated/third_party_licenses/${variant.name}") - def dependenciesJson = new File(baseDir, "dependencies.json") - - TaskProvider dependencyTask = project.tasks.register( - "${variant.name}OssDependencyTask", - DependencyTask.class) { - it.dependenciesJson.set(dependenciesJson) - it.libraryDependenciesReport.set(variant.artifacts.get(SingleArtifact.METADATA_LIBRARY_DEPENDENCIES_REPORT.INSTANCE)) + project.plugins.configureEach { plugin -> + if (plugin instanceof AppPlugin) { + def androidComponents = project.extensions.getByType(ApplicationAndroidComponentsExtension) + androidComponents.onVariants(androidComponents.selector().all()) { variant -> + configureLicenceTasks(project, variant) } - logger.debug("Registered task ${dependencyTask.name}") + } + } + } - TaskProvider licenseTask = project.tasks.register( - "${variant.name}OssLicensesTask", - LicensesTask.class) { - markNotCompatibleWithConfigurationCache(it) - it.dependenciesJson.set(dependencyTask.flatMap { it.dependenciesJson }) - } - logger.debug("Registered task ${licenseTask.name}") - variant.sources.resources.addGeneratedSourceDirectory(licenseTask, LicensesTask::getGeneratedDirectory) + private static void configureLicenceTasks(Project project, ApplicationVariant variant) { + Provider baseDir = project.layout.buildDirectory.dir("generated/third_party_licenses/${variant.name}") + def dependenciesJson = baseDir.map { it.file("dependencies.json") } + TaskProvider dependencyTask = project.tasks.register( + "${variant.name}OssDependencyTask", + DependencyTask.class) { + it.dependenciesJson.set(dependenciesJson) + it.libraryDependenciesReport.set(variant.artifacts.get(SingleArtifact.METADATA_LIBRARY_DEPENDENCIES_REPORT.INSTANCE)) + } + project.logger.debug("Registered task ${dependencyTask.name}") - TaskProvider cleanupTask = project.tasks.register( - "${variant.name}OssLicensesCleanUp", - LicensesCleanUpTask.class) { - it.generatedDirectory.set(baseDir) - } - logger.debug("Registered task ${cleanupTask.name}") + TaskProvider licenseTask = project.tasks.register( + "${variant.name}OssLicensesTask", + LicensesTask.class) { + markNotCompatibleWithConfigurationCache(it) + it.dependenciesJson.set(dependencyTask.flatMap { it.dependenciesJson }) + } + project.logger.debug("Registered task ${licenseTask.name}") + variant.sources.resources.addGeneratedSourceDirectory(licenseTask, LicensesTask::getGeneratedDirectory) - project.tasks.named("clean").configure { - it.dependsOn(cleanupTask) - } - }) + TaskProvider cleanupTask = project.tasks.register( + "${variant.name}OssLicensesCleanUp", + LicensesCleanUpTask.class) { + it.generatedDirectory.set(baseDir) + } + project.logger.debug("Registered task ${cleanupTask.name}") + + project.tasks.named("clean").configure { + it.dependsOn(cleanupTask) } }