Skip to content

Instantly share code, notes, and snippets.

@autonomousapps
Created March 16, 2022 20:13
Show Gist options
  • Save autonomousapps/f0133e58a612b6837f3f4f6554337035 to your computer and use it in GitHub Desktop.
Save autonomousapps/f0133e58a612b6837f3f4f6554337035 to your computer and use it in GitHub Desktop.
Plugin for resolving dependencies in a task action, to help with pre-populating a docker image
class ResolveDependenciesPlugin : Plugin<Project> {
override fun apply(target: Project): Unit = target.run {
pluginManager.withPlugin("java") {
registerTask(theJars = artifactFilesProvider("compileClasspath", "jar"))
}
pluginManager.withPlugin("com.android.base") {
// This lets us call `./gradlew resolveDependencies` and have it Just Work.
val lifecycleTask = tasks.register(RESOLVE_DEPENDENCIES_TASK_NAME)
androidComponents.onVariants { variant ->
val name = variant.name
val variantTask = registerTask(
theJars = artifactFilesProvider("${name}CompileClasspath", "jar"),
theAars = artifactFilesProvider("${name}CompileClasspath", "aar"),
variantName = name
)
lifecycleTask.configure {
it.dependsOn(variantTask)
}
}
}
}
private fun Project.registerTask(
theJars: Provider<FileCollection>,
theAars: Provider<FileCollection>? = null,
variantName: String = ""
): TaskProvider<ResolveDependenciesTask> {
val taskSlug = variantName.replaceFirstChar(Char::uppercase)
return tasks.register<ResolveDependenciesTask>("$RESOLVE_DEPENDENCIES_TASK_NAME$taskSlug") {
jars.setFrom(theJars)
theAars?.let { aars.setFrom(it) }
projectPath.set([email protected])
output.set(layout.buildDirectory.file(outputFilePath(variantName)))
}
}
@UntrackedTask(because = "We always want this to execute")
abstract class ResolveDependenciesTask : DefaultTask() {
init {
group = "Support"
description = "Resolves the compile classpath"
}
@get:Input
abstract val projectPath: Property<String>
@get:Optional
@get:CompileClasspath
abstract val jars: ConfigurableFileCollection
@get:Optional
@get:CompileClasspath
abstract val aars: ConfigurableFileCollection
@get:OutputFile
abstract val output: RegularFileProperty
@TaskAction fun action() {
// clean prior output and, in case a project has no external dependencies, create the file
val output = output.getAndDelete().also { it.createNewFile() }
logger.debug("Resolving dependencies for ${projectPath.get()}…")
// An example path (note we specifically want to see (`.gradle/caches/modules-2`):
// * /Users/<username>/.gradle/caches/modules-2/files-2.1/androidx.constraintlayout/constraintlayout/1.1.3/2d92d69c428d9d36fd09f5669ec4e60a1e8859a/constraintlayout-1.1.3.aar
jars.forEach { output.appendText("${it.path}\n") }
aars.forEach { output.appendText("${it.path}\n") }
}
}
companion object {
private const val RESOLVE_DEPENDENCIES_TASK_NAME = "resolveDependencies"
private const val OUTPUT_ROOT = "reports"
private const val OUTPUT_FILE_NAME = "compile_dependencies.txt"
/**
* Note that the "build" part of the path is the result of using `Project.layout.buildDirectory`.
*
* Visible for testing.
*/
internal fun outputFilePath(variantName: String = ""): String {
return if (variantName.isEmpty()) {
// jvm projects: build/reports/compile_dependencies.txt
"$OUTPUT_ROOT/$OUTPUT_FILE_NAME"
} else {
// android projects: build/reports/<debug|release>/compile_dependencies.txt
"$OUTPUT_ROOT/$variantName/$OUTPUT_FILE_NAME"
}
}
}
}
private object Artifacts {
private val attributeKey: Attribute<String> = Attribute.of("artifactType", String::class.java)
fun Project.artifactFilesProvider(
configurationName: String,
attrValue: String
): Provider<FileCollection> = provider {
configurations.getByName(configurationName)
.externalArtifactViewOf(attrValue)
.artifacts
.artifactFiles
}
fun Configuration.externalArtifactViewOf(
attrValue: String
): ArtifactView = incoming.artifactView { view ->
view.attributes.attribute(attributeKey, attrValue)
// If some dependency doesn't have the expected attribute, don't fail. Continue!
view.lenient(true)
// Only resolve external dependencies! Without this, all project dependencies will get _compiled_.
view.componentFilter { id -> id is ModuleComponentIdentifier }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment