Created
March 16, 2022 20:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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