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/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/LicensesTask.groovy b/oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy index a5d6e483..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 @@ -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 getGeneratedDirectory() - @OutputFile + @Internal // represented by getGeneratedDirectory() File licenses - @OutputFile + @Internal // represented by getGeneratedDirectory() 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 = new File(getGeneratedDirectory().get().asFile, "raw") 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 7ec1bbf9..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,80 +17,57 @@ package com.google.android.gms.oss.licenses.plugin import com.android.build.api.artifact.SingleArtifact -import com.android.build.gradle.api.BaseVariant +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) { - def variantTolicenseTaskMap = new HashMap() - project.androidComponents { - onVariants(selector().all(), { variant -> - def 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, - 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 $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 - }.get() - logger.debug("Created task ${licenseTask.name}") + } + } + } - variantTolicenseTaskMap[variant.name] = licenseTask + 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}") - String cleanupTaskName = "${variant.name}OssLicensesCleanUp" - TaskProvider cleanupTask = project.tasks.register( - cleanupTaskName, - LicensesCleanUpTask.class) { - it.dependenciesJson = dependenciesJson - it.dependencyDir = baseDir - it.licensesFile = licensesFile - it.metadataFile = licensesMetadataFile - it.licensesDir = rawResourceDir - } - logger.debug("Registered task $cleanupTaskName") + 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}") - // 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.rawResourceDir.parentFile).builtBy(licenseTask) - variant.registerGeneratedResFolders(generatedResFolder) + project.tasks.named("clean").configure { + it.dependsOn(cleanupTask) } } 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/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()); } } 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 59ab73ff..d7b1a337 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 @@ -64,38 +64,27 @@ public class LicensesTaskTest { public void setUp() throws IOException { File projectDir = temporaryFolder.newFolder(); File outputDir = temporaryFolder.newFolder(); - File outputLicenses = new File(outputDir, "testLicenses"); - File outputMetadata = new File(outputDir, "testMetadata"); project = ProjectBuilder.builder().withProjectDir(projectDir).build(); licensesTask = project.getTasks().create("generateLicenses", LicensesTask.class); - licensesTask.setRawResourceDir(outputDir); - licensesTask.setLicenses(outputLicenses); - licensesTask.setLicensesMetadata(outputMetadata); + licensesTask.getGeneratedDirectory().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 = new File(licensesTask.getGeneratedDirectory().get().getAsFile(), "raw"); + 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 @@ -115,6 +104,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); @@ -125,9 +115,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"); @@ -153,6 +146,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"); @@ -179,6 +173,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"); @@ -228,10 +223,12 @@ public void testGetBytesFromInputStream_specialCharacters() { @Test public void testAddGooglePlayServiceLicenses() throws IOException { - File tempOutput = new File(licensesTask.getRawResourceDir(), "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"); + + licensesTask.initOutputDir(); licensesTask.addGooglePlayServiceLicenses(artifact); String content = new String(Files.readAllBytes(licensesTask.getLicenses().toPath()), UTF_8); @@ -247,16 +244,17 @@ public void testAddGooglePlayServiceLicenses() throws IOException { @Test public void testAddGooglePlayServiceLicenses_withoutDuplicate() throws IOException { - File groupC = new File(licensesTask.getRawResourceDir(), "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(), "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"); + licensesTask.initOutputDir(); licensesTask.addGooglePlayServiceLicenses(artifactFoo); licensesTask.addGooglePlayServiceLicenses(artifactBar); @@ -287,6 +285,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)); @@ -303,6 +302,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; @@ -316,6 +317,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");