Skip to content

Commit 5b8301d

Browse files
feat: make plugin tasks cacheable and relocatable
1 parent 4fc44a0 commit 5b8301d

File tree

6 files changed

+70
-48
lines changed

6 files changed

+70
-48
lines changed

oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/ArtifactFiles.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,24 @@
1616

1717
package com.google.android.gms.oss.licenses.plugin
1818

19+
import org.gradle.api.tasks.InputFile
20+
import org.gradle.api.tasks.Optional
21+
import org.gradle.api.tasks.PathSensitive
22+
import org.gradle.api.tasks.PathSensitivity
1923
import java.io.Serializable
2024

2125
/**
2226
* Data class to hold the resolved physical files for a single dependency.
2327
*/
2428
class ArtifactFiles implements Serializable {
29+
@InputFile
30+
@PathSensitive(PathSensitivity.NONE)
31+
@Optional
2532
File pomFile
33+
34+
@InputFile
35+
@PathSensitive(PathSensitivity.NONE)
36+
@Optional
2637
File libraryFile
2738

2839
ArtifactFiles(File pomFile, File libraryFile) {

oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/DependencyTask.groovy

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ import groovy.json.JsonBuilder
2121
import groovy.json.JsonGenerator
2222
import org.gradle.api.DefaultTask
2323
import org.gradle.api.file.RegularFileProperty
24+
import org.gradle.api.tasks.CacheableTask
2425
import org.gradle.api.tasks.InputFile
26+
import org.gradle.api.tasks.Optional
2527
import org.gradle.api.tasks.OutputFile
28+
import org.gradle.api.tasks.PathSensitive
29+
import org.gradle.api.tasks.PathSensitivity
2630
import org.gradle.api.tasks.TaskAction
2731
import org.slf4j.LoggerFactory
2832

@@ -37,14 +41,16 @@ import static com.android.tools.build.libraries.metadata.Library.LibraryOneofCas
3741
* If the protobuf is not present (e.g. debug variants) it writes a single
3842
* dependency on the {@link DependencyUtil#ABSENT_ARTIFACT}.
3943
*/
44+
@CacheableTask
4045
abstract class DependencyTask extends DefaultTask {
4146
private static final logger = LoggerFactory.getLogger(DependencyTask.class)
4247

4348
@OutputFile
4449
abstract RegularFileProperty getDependenciesJson()
4550

4651
@InputFile
47-
@org.gradle.api.tasks.Optional
52+
@PathSensitive(PathSensitivity.NONE)
53+
@Optional
4854
abstract RegularFileProperty getLibraryDependenciesReport()
4955

5056
@TaskAction

oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/DependencyUtil.groovy

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.gradle.api.Project
2020
import org.gradle.api.artifacts.Configuration
2121
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
2222
import org.gradle.api.artifacts.result.ResolvedArtifactResult
23+
import org.gradle.api.provider.Provider
2324
import org.gradle.maven.MavenModule
2425
import org.gradle.maven.MavenPomArtifact
2526

@@ -45,52 +46,54 @@ class DependencyUtil {
4546
*
4647
* @param project The Gradle project used to create the resolution query.
4748
* @param runtimeConfiguration The configuration whose dependencies should be resolved.
48-
* @return A map of GAV coordinates to their resolved ArtifactFiles.
49+
* @return A provider for a map of GAV coordinates to their resolved ArtifactFiles.
4950
*/
50-
static Map<String, ArtifactFiles> resolveArtifacts(Project project, Configuration runtimeConfiguration) {
51-
// We create an ArtifactView to gather the component identifiers and library files.
52-
// We specifically target external Maven dependencies (ModuleComponentIdentifiers).
53-
def runtimeArtifactView = runtimeConfiguration.incoming.artifactView {
54-
it.componentFilter { id -> id instanceof ModuleComponentIdentifier }
55-
}
56-
57-
def artifactsMap = [:]
58-
59-
// 1. Gather library files directly from the view
60-
runtimeArtifactView.artifacts.each { artifact ->
61-
def id = artifact.id.componentIdentifier
62-
if (id instanceof ModuleComponentIdentifier) {
63-
String key = "${id.group}:${id.module}:${id.version}".toString()
64-
artifactsMap[key] = new ArtifactFiles(null, artifact.file)
51+
static Provider<Map<String, ArtifactFiles>> resolveArtifacts(Project project, Configuration runtimeConfiguration) {
52+
return project.provider {
53+
// We create an ArtifactView to gather the component identifiers and library files.
54+
// We specifically target external Maven dependencies (ModuleComponentIdentifiers).
55+
def runtimeArtifactView = runtimeConfiguration.incoming.artifactView {
56+
it.componentFilter { id -> id instanceof ModuleComponentIdentifier }
57+
}
58+
59+
def artifactsMap = [:]
60+
61+
// 1. Gather library files directly from the view
62+
runtimeArtifactView.artifacts.each { artifact ->
63+
def id = artifact.id.componentIdentifier
64+
if (id instanceof ModuleComponentIdentifier) {
65+
String key = "${id.group}:${id.module}:${id.version}".toString()
66+
artifactsMap[key] = new ArtifactFiles(null, artifact.file)
67+
}
6568
}
66-
}
6769

68-
// 2. Fetch corresponding POM files using ArtifactResolutionQuery
69-
def componentIds = runtimeArtifactView.artifacts.collect { it.id.componentIdentifier }
70-
71-
if (!componentIds.isEmpty()) {
72-
def result = project.dependencies.createArtifactResolutionQuery()
73-
.forComponents(componentIds)
74-
.withArtifacts(MavenModule, MavenPomArtifact)
75-
.execute()
70+
// 2. Fetch corresponding POM files using ArtifactResolutionQuery
71+
def componentIds = runtimeArtifactView.artifacts.collect { it.id.componentIdentifier }
72+
73+
if (!componentIds.isEmpty()) {
74+
def result = project.dependencies.createArtifactResolutionQuery()
75+
.forComponents(componentIds)
76+
.withArtifacts(MavenModule, MavenPomArtifact)
77+
.execute()
7678

77-
result.resolvedComponents.each { component ->
78-
component.getArtifacts(MavenPomArtifact).each { artifact ->
79-
if (artifact instanceof ResolvedArtifactResult) {
80-
def id = component.id
81-
String key = "${id.group}:${id.module}:${id.version}".toString()
82-
83-
// Update the existing entry with the POM file
84-
if (artifactsMap.containsKey(key)) {
85-
artifactsMap[key].pomFile = artifact.file
86-
} else {
87-
artifactsMap[key] = new ArtifactFiles(artifact.file, null)
79+
result.resolvedComponents.each { component ->
80+
component.getArtifacts(MavenPomArtifact).each { artifact ->
81+
if (artifact instanceof ResolvedArtifactResult) {
82+
def id = component.id
83+
String key = "${id.group}:${id.module}:${id.version}".toString()
84+
85+
// Update the existing entry with the POM file
86+
if (artifactsMap.containsKey(key)) {
87+
artifactsMap[key].pomFile = artifact.file
88+
} else {
89+
artifactsMap[key] = new ArtifactFiles(artifact.file, null)
90+
}
8891
}
8992
}
9093
}
9194
}
95+
96+
return artifactsMap
9297
}
93-
94-
return artifactsMap
9598
}
9699
}

oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/LicensesTask.groovy

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ import groovy.xml.XmlSlurper
2121
import org.gradle.api.DefaultTask
2222
import org.gradle.api.file.DirectoryProperty
2323
import org.gradle.api.file.RegularFileProperty
24+
import org.gradle.api.tasks.CacheableTask
2425
import org.gradle.api.tasks.InputFile
2526
import org.gradle.api.tasks.Internal
27+
import org.gradle.api.tasks.Nested
2628
import org.gradle.api.tasks.OutputDirectory
2729
import org.gradle.api.tasks.OutputFile
30+
import org.gradle.api.tasks.PathSensitive
31+
import org.gradle.api.tasks.PathSensitivity
2832
import org.gradle.api.tasks.TaskAction
2933
import org.slf4j.LoggerFactory
3034

@@ -38,6 +42,7 @@ import java.util.zip.ZipFile
3842
* mappings (POMs and Library artifacts) are provided as lazy input properties,
3943
* making the task a pure function of its inputs.
4044
*/
45+
@CacheableTask
4146
abstract class LicensesTask extends DefaultTask {
4247
private static final String UTF_8 = "UTF-8"
4348
private static final byte[] LINE_SEPARATOR = System
@@ -66,10 +71,11 @@ abstract class LicensesTask extends DefaultTask {
6671
* A map of GAV coordinates (group:name:version) to their resolved POM and Library files.
6772
* Populated by OssLicensesPlugin during configuration.
6873
*/
69-
@org.gradle.api.tasks.Input
74+
@Nested
7075
abstract org.gradle.api.provider.MapProperty<String, ArtifactFiles> getArtifactFiles()
7176

7277
@InputFile
78+
@PathSensitive(PathSensitivity.NONE)
7379
abstract RegularFileProperty getDependenciesJson()
7480

7581
@OutputDirectory

oss-licenses-plugin/src/main/groovy/com/google/android/gms/oss/licenses/plugin/OssLicensesPlugin.groovy

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ class OssLicensesPlugin implements Plugin<Project> {
5858

5959
// Task 1: Dependency Identification
6060
// This task reads the AGP METADATA_LIBRARY_DEPENDENCIES_REPORT protobuf.
61-
def dependenciesJson = baseDir.map { it.file("dependencies.json") }
6261
TaskProvider<DependencyTask> dependencyTask = project.tasks.register(
6362
"${variant.name}OssDependencyTask",
6463
DependencyTask.class) {
65-
it.dependenciesJson.set(dependenciesJson)
64+
it.dependenciesJson.set(baseDir.map { it.file("dependencies.json") })
6665
it.libraryDependenciesReport.set(variant.artifacts.get(SingleArtifact.METADATA_LIBRARY_DEPENDENCIES_REPORT.INSTANCE))
6766
}
6867
project.logger.debug("Registered task ${dependencyTask.name}")
@@ -73,10 +72,7 @@ class OssLicensesPlugin implements Plugin<Project> {
7372
"${variant.name}OssLicensesTask",
7473
LicensesTask.class) {
7574
it.dependenciesJson.set(dependencyTask.flatMap { it.dependenciesJson })
76-
77-
it.artifactFiles.set(project.provider {
78-
DependencyUtil.resolveArtifacts(project, variant.runtimeConfiguration)
79-
})
75+
it.artifactFiles.set(DependencyUtil.resolveArtifacts(project, variant.runtimeConfiguration))
8076
}
8177
project.logger.debug("Registered task ${licenseTask.name}")
8278

oss-licenses-plugin/src/test/java/com/google/android/gms/oss/licenses/plugin/DependencyResolutionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public void testComplexDependencyGraphResolution() throws IOException {
9292
runtimeClasspath.resolve();
9393

9494
// Execute resolution logic
95-
Map<String, ArtifactFiles> artifactFiles = DependencyUtil.resolveArtifacts(appProject, runtimeClasspath);
95+
Map<String, ArtifactFiles> artifactFiles = DependencyUtil.resolveArtifacts(appProject, runtimeClasspath).get();
9696

9797
// Assertions
9898
// - Guava resolved to the higher version
@@ -121,7 +121,7 @@ public void testPomResolution() throws IOException {
121121
Configuration runtimeClasspath = appProject.getConfigurations().getByName("runtimeClasspath");
122122
runtimeClasspath.resolve();
123123

124-
Map<String, ArtifactFiles> artifactFiles = DependencyUtil.resolveArtifacts(appProject, runtimeClasspath);
124+
Map<String, ArtifactFiles> artifactFiles = DependencyUtil.resolveArtifacts(appProject, runtimeClasspath).get();
125125

126126
assertThat(artifactFiles).containsKey("com.google.guava:guava:33.0.0-jre");
127127
assertThat(artifactFiles.get("com.google.guava:guava:33.0.0-jre").getPomFile()).isNotNull();

0 commit comments

Comments
 (0)