com.autonomousapps.tasks.SynthesizeProjectViewTask.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dependency-analysis-gradle-plugin Show documentation
Show all versions of dependency-analysis-gradle-plugin Show documentation
Analyzes dependency usage in Android and JVM projects
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.tasks
import com.autonomousapps.internal.UsagesExclusions
import com.autonomousapps.internal.utils.*
import com.autonomousapps.model.*
import com.autonomousapps.model.declaration.SourceSetKind
import com.autonomousapps.model.declaration.Variant
import com.autonomousapps.model.internal.*
import com.autonomousapps.model.internal.AndroidAssetSource
import com.autonomousapps.model.internal.AndroidResSource
import com.autonomousapps.model.internal.CodeSource
import com.autonomousapps.model.internal.DependencyGraphView
import com.autonomousapps.model.internal.ProjectVariant
import com.autonomousapps.model.internal.Source
import com.autonomousapps.model.internal.intermediates.AnnotationProcessorDependency
import com.autonomousapps.model.internal.intermediates.consumer.ExplodingAbi
import com.autonomousapps.model.internal.intermediates.consumer.ExplodingBytecode
import com.autonomousapps.model.internal.intermediates.consumer.ExplodingSourceCode
import com.autonomousapps.model.internal.intermediates.consumer.MemberAccess
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
import java.util.TreeSet
import javax.inject.Inject
@CacheableTask
abstract class SynthesizeProjectViewTask @Inject constructor(
private val workerExecutor: WorkerExecutor,
) : DefaultTask() {
init {
description = "Synthesizes project usages information into a single view"
}
@get:Input
abstract val projectPath: Property
/** May be null. */
@get:Optional
@get:Input
abstract val buildType: Property
/** May be null. */
@get:Optional
@get:Input
abstract val flavor: Property
@get:Input
abstract val variant: Property
@get:Input
abstract val kind: Property
/** [`DependencyGraphView`][DependencyGraphView] */
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val graph: RegularFileProperty
/** [`Set`][com.autonomousapps.model.internal.intermediates.AnnotationProcessorDependency] */
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val annotationProcessors: RegularFileProperty
/** [`Set`][ExplodingBytecode] */
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val explodedBytecode: RegularFileProperty
/** [`Set`][ExplodingSourceCode] */
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val explodedSourceCode: RegularFileProperty
/** [`UsagesExclusions`][com.autonomousapps.internal.UsagesExclusions] */
@get:Optional
@get:Input
abstract val usagesExclusions: Property
/** [`Set`][ExplodingAbi] */
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val explodingAbi: RegularFileProperty
/** [`Set`][AndroidResSource] */
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val androidResSource: RegularFileProperty
/** [`Set`][AndroidAssetSource] */
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val androidAssetsSource: RegularFileProperty
/**
* A string representing the fully-qualified class name (FQCN) of the test instrumentation runner if (1) this is an
* Android project and (2) a test instrumentation runner is declared. (May be null.)
*/
@get:Optional
@get:Input
abstract val testInstrumentationRunner: Property
@get:OutputFile
abstract val output: RegularFileProperty
@TaskAction fun action() {
workerExecutor.noIsolation().submit(SynthesizeProjectViewWorkAction::class.java) {
projectPath.set([email protected])
buildType.set([email protected])
flavor.set([email protected])
variant.set([email protected])
kind.set([email protected])
graph.set([email protected])
annotationProcessors.set([email protected])
explodedBytecode.set([email protected])
explodedSourceCode.set([email protected])
explodingAbi.set([email protected])
usagesExclusions.set([email protected])
androidResSource.set([email protected])
androidAssetsSource.set([email protected])
testInstrumentationRunner.set([email protected])
output.set([email protected])
}
}
interface SynthesizeProjectViewParameters : WorkParameters {
val projectPath: Property
/** May be null. */
val buildType: Property
/** May be null. */
val flavor: Property
val variant: Property
val kind: Property
val graph: RegularFileProperty
val annotationProcessors: RegularFileProperty
val explodedBytecode: RegularFileProperty
val explodedSourceCode: RegularFileProperty
val usagesExclusions: Property
// Optional
val explodingAbi: RegularFileProperty
val androidResSource: RegularFileProperty
val androidAssetsSource: RegularFileProperty
val testInstrumentationRunner: Property
val output: RegularFileProperty
}
abstract class SynthesizeProjectViewWorkAction : WorkAction {
private val builders = sortedMapOf()
@Suppress("UnstableApiUsage") // Guava Graph
override fun execute() {
val output = parameters.output.getAndDelete()
val graph = parameters.graph.fromJson()
val explodedBytecode = parameters.explodedBytecode.fromJsonSet()
val explodingAbi = parameters.explodingAbi.fromNullableJsonSet()
val explodedSourceCode = parameters.explodedSourceCode.fromJsonSet()
val androidResSource = parameters.androidResSource.fromNullableJsonSet()
val androidAssetsSource = parameters.androidAssetsSource.fromNullableJsonSet()
val testInstrumentationRunner = parameters.testInstrumentationRunner.orNull
explodedBytecode.forEach { bytecode ->
builders.merge(
bytecode.className,
CodeSourceBuilder(bytecode.className).apply {
relativePath = bytecode.relativePath
nonAnnotationClasses.addAll(bytecode.nonAnnotationClasses)
annotationClasses.addAll(bytecode.annotationClasses)
invisibleAnnotationClasses.addAll(bytecode.invisibleAnnotationClasses)
// // TODO(tsr): flatten into a single set? Do we need the map?
// // Merge the two maps
// bytecode.binaryClassAccesses.forEach { (className, memberAccesses) ->
// binaryClassAccesses.merge(className, memberAccesses.toMutableSet()) { acc, inc ->
// acc.apply { addAll(inc) }
// }
// }
},
CodeSourceBuilder::concat
)
}
explodingAbi.forEach { abi ->
builders.merge(
abi.className,
CodeSourceBuilder(abi.className).apply {
exposedClasses.addAll(abi.exposedClasses)
},
CodeSourceBuilder::concat
)
}
explodedSourceCode.forEach { source ->
builders.merge(
source.className,
CodeSourceBuilder(source.className).apply {
imports.addAll(source.imports)
kind = source.kind
relativePath = source.relativePath
},
CodeSourceBuilder::concat
)
}
val codeSource = builders.values.asSequence()
// relativePath will be null for synthetic classes, like R class files
.filterNot { it.relativePath == null }
.map { it.build() }
.toSet()
val projectCoordinates = ProjectCoordinates(
parameters.projectPath.get(),
GradleVariantIdentification.EMPTY
)
val ignoreSelfDependencies = parameters.buildType.isPresent // ignore on Android
val classpath = graph.graph.nodes().asSequence().filterNot {
ignoreSelfDependencies && it is IncludedBuildCoordinates && it.resolvedProject.identifier == projectCoordinates.identifier
}.toSortedSet()
val annotationProcessors = parameters.annotationProcessors.fromJsonSet()
.mapToSet { it.coordinates }
val usagesExclusions = parameters.usagesExclusions.orNull?.fromJson() ?: UsagesExclusions.NONE
val projectVariant = ProjectVariant(
coordinates = projectCoordinates,
buildType = parameters.buildType.orNull,
flavor = parameters.flavor.orNull,
variant = Variant(parameters.variant.get(), parameters.kind.get()),
sources = TreeSet
© 2015 - 2025 Weber Informatics LLC | Privacy Policy