Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix booster-task-graph #466

Merged
merged 1 commit into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,38 +67,71 @@ fun <T> Project.getProperty(name: String, defaultValue: T): T {
}
}

/**
* Returns the local android resources or empty list if the project is not an android project
*/
val Project.localAndroidResources: List<File>
get() = when {
isAndroid -> project.variants.mapNotNull(Variant::localAndroidResources).flatten()
else -> emptyList()
}

@JvmOverloads
fun Project.getUpstreamProjects(
transitive: Boolean = true,
variant: Variant? = null
): Set<Project> = getResolvedArtifactResults(transitive, variant).mapNotNull {
(it.id.componentIdentifier as? ProjectComponentIdentifier)?.projectPath?.let { projectPath ->
rootProject.project(projectPath)
}
filter: List<Variant>.() -> List<Variant>
): Set<Project> = getResolvedArtifacts(transitive, filter) {
it.id.componentIdentifier as? ProjectComponentIdentifier
}.map {
rootProject.project(it.projectPath)
}.toSet()

/**
* Returns the upstream artifacts of the target project, the dependencies can be filtered based on the target
* project’s variant, such as filtering the dependencies corresponding to the `debug` variant of the target project.
*
* @param T the result of transform function
* @param transitive whether to resolve the artifacts transitively
* @param filter filter the dependencies by variant
* @see [filterByNameOrBuildType]
*/
@JvmOverloads
fun <T> Project.getResolvedArtifacts(
transitive: Boolean = true,
filter: List<Variant>.() -> List<Variant>,
transform: (ResolvedArtifactResult) -> T?
): Set<T> = getResolvedArtifactResults(transitive, filter).mapNotNull {
transform(it)
}.toSet()

@JvmOverloads
fun Project.getResolvedArtifactResults(
transitive: Boolean = true,
variant: Variant? = null
filter: List<Variant>.() -> List<Variant>
): Set<ResolvedArtifactResult> = when {
variant == null -> emptySet()
isAndroid -> getResolvedArtifactResultsRecursively(transitive) {
filterByVariant(variant).map { v ->
variants.filter().map { v ->
AGP.run { v.getDependencies(transitive) }
}.flatten()
}

isJava -> getResolvedArtifactResultsRecursively(transitive) {
configurations.getByName(RUNTIME_CLASSPATH_CONFIGURATION_NAME).resolvedConfiguration.resolvedArtifacts.map {
ResolvedArtifactResultImpl(it.id, it.file)
}
}

else -> emptySet()
}.distinctBy {
it.id.componentIdentifier
}.toSet()

private fun Project.getResolvedArtifactResultsRecursively(transitive: Boolean, resolve: Project.() -> List<ResolvedArtifactResult>): Set<ResolvedArtifactResult> {
/**
* Returns the resolved artifact results recursively
*/
@JvmOverloads
fun Project.getResolvedArtifactResultsRecursively(
transitive: Boolean = true,
resolve: Project.() -> List<ResolvedArtifactResult>
): Set<ResolvedArtifactResult> {
val stack = Stack<Project>()
val results = mutableMapOf<ComponentIdentifier, ResolvedArtifactResult>()

Expand All @@ -125,44 +158,43 @@ private fun Project.getResolvedArtifactResultsRecursively(transitive: Boolean, r

/**
* Returns the jar files which could be the outputs of the jar task or createFullJar task
*
* @param variant The build variant
*/
fun Project.getJars(variant: Variant? = null): Set<File> = getJarTaskProviders(variant).map {
fun Project.getJars(
filter: List<Variant>.() -> List<Variant>
): Set<File> = getJarTaskProviders(filter).map {
it.get().outputs.files
}.flatten().toSet()

fun Project.getJarTaskProviders(variant: Variant? = null): Collection<TaskProvider<out Task>> = when {
/**
* Returns the jar task provider of all matched variants
* * Android Project
* - application: createFullJar
* - library: bundleClasses
* * Java Project
* - jar
*/
fun Project.getJarTaskProviders(
filter: List<Variant>.() -> List<Variant>
): Collection<TaskProvider<out Task>> = when {
isAndroid -> when (getAndroidComponentsOrNull<AndroidComponentsExtension<*, *, *>>()) {
is LibraryAndroidComponentsExtension -> filterByVariant(variant).mapNotNull(Variant::createFullJarTaskProvider)
is ApplicationAndroidComponentsExtension -> filterByVariant(variant).mapNotNull(Variant::bundleClassesTaskProvider)
is LibraryAndroidComponentsExtension -> variants.filter().mapNotNull(Variant::createFullJarTaskProvider)
is ApplicationAndroidComponentsExtension -> variants.filter().mapNotNull(Variant::bundleClassesTaskProvider)
else -> emptyList()
}

isJavaLibrary -> listOf(tasks.named(JavaPlugin.JAR_TASK_NAME))
else -> emptyList()
}

private fun Project.filterByVariant(variant: Variant? = null): Collection<Variant> {
val variants = when (getAndroidComponentsOrNull<AndroidComponentsExtension<*, *, *>>()) {
is ApplicationAndroidComponentsExtension -> plugins.getPlugin(AppPlugin::class.java).variantManager
is LibraryAndroidComponentsExtension -> plugins.getPlugin(LibraryPlugin::class.java).variantManager
val Project.variants: List<Variant>
get() = when (getAndroidComponentsOrNull<AndroidComponentsExtension<*, *, *>>()) {
is ApplicationAndroidComponentsExtension -> plugins.getPlugin(AppPlugin::class.java)
is LibraryAndroidComponentsExtension -> plugins.getPlugin(LibraryPlugin::class.java)
else -> null
}?.mainComponents?.map {
}?.variantManager?.mainComponents?.map {
it.variant
}?.filterIsInstance<Variant>() ?: emptyList()

if (null == variant) return variants

return variants.filter {
it.name == variant.name
}.takeIf {
it.isNotEmpty()
} ?: variants.filter {
it.buildType == variant.buildType
}
}

private data class ResolvedArtifactResultImpl(
private val artifactId: ComponentArtifactIdentifier,
private val artifactFile: File
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.didiglobal.booster.gradle

import com.android.build.api.artifact.ScopedArtifact
import com.android.build.api.variant.ScopedArtifacts
import com.android.build.api.variant.Variant
import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider

abstract class ScopedTask : DefaultTask() {

@get:Internal
abstract val variant: Property<Variant>

@get:InputFiles
@get:PathSensitive(PathSensitivity.NONE)
abstract val inputJars: ListProperty<RegularFile>

@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val inputDirectories: ListProperty<Directory>

interface CreationAction<T : ScopedTask> {

val artifactScope: ScopedArtifacts.Scope
get() = ScopedArtifacts.Scope.PROJECT

val artifactType: ScopedArtifact
get() = ScopedArtifact.CLASSES

fun configure(task: T) {}
}

}

/**
* Register a task for the specified variant
*
* @param prefix The prefix of task name
* @param action The task creation action
*/
inline fun <reified T : ScopedTask, reified A : ScopedTask.CreationAction<T>> Variant.registerTask(
prefix: String,
action: A
): TaskProvider<T> = project.tasks.register(getTaskName(prefix), T::class.java) {
it.variant.set(this)
action.configure(it)
}.also { taskProvider ->
artifacts.forScope(action.artifactScope)
.use(taskProvider)
.toGet(action.artifactType, ScopedTask::inputJars, ScopedTask::inputDirectories)
}

/**
* Register a task for the specified variant
*
* @param prefix The prefix of task name
* @param suffix The suffix of task name
* @param action The task creation action
*/
inline fun <reified T : ScopedTask, reified A : ScopedTask.CreationAction<T>> Variant.registerTask(
prefix: String,
suffix: String,
action: A
): TaskProvider<T> = project.tasks.register(getTaskName(prefix, suffix), T::class.java) {
it.variant.set(this)
action.configure(it)
}.also { taskProvider ->
artifacts.forScope(action.artifactScope)
.use(taskProvider)
.toGet(action.artifactType, ScopedTask::inputJars, ScopedTask::inputDirectories)
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,18 @@ fun Variant.getTaskName(prefix: String, suffix: String): String = AGP.run {
getTaskName(prefix, suffix)
}

val Variant.isApplication: Boolean
get() = AGP.run {
val Variant?.isApplication: Boolean
get() = null != this && AGP.run {
isApplication
}

val Variant.isLibrary: Boolean
get() = AGP.run {
val Variant?.isLibrary: Boolean
get() = null != this && AGP.run {
isLibrary
}

val Variant.isDynamicFeature: Boolean
get() = AGP.run {
val Variant?.isDynamicFeature: Boolean
get() = null != this && AGP.run {
isDynamicFeature
}

Expand Down Expand Up @@ -246,6 +246,11 @@ val Variant.symbolListWithPackageName: FileCollection
symbolListWithPackageName
}

val Variant.localAndroidResources: FileCollection
get() = AGP.run {
localAndroidResources
}

val Variant.allArtifacts: Map<String, FileCollection>
get() = AGP.run {
allArtifacts
Expand All @@ -256,7 +261,54 @@ val Variant.buildTools: BuildToolInfo
buildTools
}

val Variant.isPrecompileDependenciesResourcesEnabled: Boolean
get() = AGP.run {
val Variant?.isPrecompileDependenciesResourcesEnabled: Boolean
get() = null != this && AGP.run {
isPrecompileDependenciesResourcesEnabled
}

val Variant?.isDebuggable: Boolean
get() = null != this && AGP.run {
isDebuggable
}

/**
* Filter variants by variant name
*/
fun Variant?.filterByName(): List<Variant>.() -> List<Variant> = {
val variant = this@filterByName

if (null == variant) this else this.filter {
it.name == variant.name
}
}

/**
* Filter variants by build type
*/
fun Variant?.filterByBuildType(): List<Variant>.() -> List<Variant> = {
val variant = this@filterByBuildType

if (null == variant) this else this.filter {
it.buildType == variant.buildType
}
}

/**
* Filter variants by flavor name
*/
fun Variant?.filterByFlavorName(): List<Variant>.() -> List<Variant> = {
val variant = this@filterByFlavorName

if (null == variant) this else this.filter {
it.flavorName == variant.flavorName
}
}

/**
* Filter variants by variant name or build type
*/
fun Variant?.filterByNameOrBuildType(): List<Variant>.() -> List<Variant> = {
filterByName().invoke(this).takeIf {
it.isNotEmpty()
} ?: filterByBuildType().invoke(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ interface AGPInterface {

val Variant.isPrecompileDependenciesResourcesEnabled: Boolean

val Variant.isDebuggable: Boolean

fun Variant.getDependencies(
transitive: Boolean = true,
filter: (ComponentIdentifier) -> Boolean = { true }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal object V80 : AGPInterface {
get() = when (this) {
is VariantImpl<*> -> this
is AnalyticsEnabledVariant -> this.delegate as VariantImpl<*>
else -> TODO("No implementationed!")
else -> TODO("No implemented!")
}

@Suppress("UnstableApiUsage")
Expand Down Expand Up @@ -304,6 +304,9 @@ internal object V80 : AGPInterface {
override val Variant.isPrecompileDependenciesResourcesEnabled: Boolean
get() = component.androidResourcesCreationConfig?.isPrecompileDependenciesResourcesEnabled == true

override val Variant.isDebuggable: Boolean
get() = component.debuggable

override fun Variant.getDependencies(
transitive: Boolean,
filter: (ComponentIdentifier) -> Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal object V81 : AGPInterface {
get() = when (this) {
is VariantImpl<*> -> this
is AnalyticsEnabledVariant -> this.delegate as VariantImpl<*>
else -> TODO("No implementationed!")
else -> TODO("No implemented!")
}

@Suppress("UnstableApiUsage")
Expand Down Expand Up @@ -307,6 +307,9 @@ internal object V81 : AGPInterface {
override val Variant.isPrecompileDependenciesResourcesEnabled: Boolean
get() = component.androidResourcesCreationConfig?.isPrecompileDependenciesResourcesEnabled == true

override val Variant.isDebuggable: Boolean
get() = component.debuggable

override fun Variant.getDependencies(
transitive: Boolean,
filter: (ComponentIdentifier) -> Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal object V82 : AGPInterface {
get() = when (this) {
is VariantImpl<*> -> this
is AnalyticsEnabledVariant -> this.delegate as VariantImpl<*>
else -> TODO("No implementationed!")
else -> TODO("No implemented!")
}

@Suppress("UnstableApiUsage")
Expand Down Expand Up @@ -307,6 +307,9 @@ internal object V82 : AGPInterface {
override val Variant.isPrecompileDependenciesResourcesEnabled: Boolean
get() = component.androidResourcesCreationConfig?.isPrecompileDependenciesResourcesEnabled == true

override val Variant.isDebuggable: Boolean
get() = component.debuggable

override fun Variant.getDependencies(
transitive: Boolean,
filter: (ComponentIdentifier) -> Boolean
Expand Down
Loading