com.autonomousapps.tasks.SynthesizeDependenciesTask.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.utils.bufferWriteJson
import com.autonomousapps.internal.utils.fromJson
import com.autonomousapps.internal.utils.fromJsonSet
import com.autonomousapps.internal.utils.fromNullableJsonSet
import com.autonomousapps.model.*
import com.autonomousapps.model.intermediates.*
import com.autonomousapps.services.InMemoryCache
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
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.io.File
import javax.inject.Inject
@CacheableTask
abstract class SynthesizeDependenciesTask @Inject constructor(
private val workerExecutor: WorkerExecutor
) : DefaultTask() {
init {
description = "Re-synthesize dependencies from analysis"
}
@get:Internal
abstract val inMemoryCache: Property
/** Needed to disambiguate other projects that might have otherwise identical inputs. */
@get:Input
abstract val projectPath: Property
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val compileDependencies: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val physicalArtifacts: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val explodedJars: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val inlineMembers: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val typealiases: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val serviceLoaders: RegularFileProperty
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val annotationProcessors: RegularFileProperty
/*
* Android-specific and therefore optional.
*/
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val manifestComponents: RegularFileProperty
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val androidRes: RegularFileProperty
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val nativeLibs: RegularFileProperty
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
abstract val androidAssets: RegularFileProperty
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction fun action() {
workerExecutor.noIsolation().submit(SynthesizeDependenciesWorkAction::class.java) {
inMemoryCache.set([email protected])
compileDependencies.set([email protected])
physicalArtifacts.set([email protected])
explodedJars.set([email protected])
inlineMembers.set([email protected])
typealiases.set([email protected])
serviceLoaders.set([email protected])
annotationProcessors.set([email protected])
manifestComponents.set([email protected])
androidRes.set([email protected])
nativeLibs.set([email protected])
androidAssets.set([email protected])
outputDir.set([email protected])
}
}
interface SynthesizeDependenciesParameters : WorkParameters {
val inMemoryCache: Property
val compileDependencies: RegularFileProperty
val physicalArtifacts: RegularFileProperty
val explodedJars: RegularFileProperty
val inlineMembers: RegularFileProperty
val typealiases: RegularFileProperty
val serviceLoaders: RegularFileProperty
val annotationProcessors: RegularFileProperty
// Android-specific and therefore optional
val manifestComponents: RegularFileProperty
val androidRes: RegularFileProperty
val nativeLibs: RegularFileProperty
val androidAssets: RegularFileProperty
val outputDir: DirectoryProperty
}
abstract class SynthesizeDependenciesWorkAction : WorkAction {
private val builders = sortedMapOf()
override fun execute() {
val outputDir = parameters.outputDir
val dependencies = parameters.compileDependencies.fromJson().coordinates
val physicalArtifacts = parameters.physicalArtifacts.fromJsonSet()
val explodedJars = parameters.explodedJars.fromJsonSet()
val inlineMembers = parameters.inlineMembers.fromJsonSet()
val typealiases = parameters.typealiases.fromJsonSet()
val serviceLoaders = parameters.serviceLoaders.fromJsonSet()
val annotationProcessors = parameters.annotationProcessors.fromJsonSet()
// Android-specific and therefore optional
val manifestComponents = parameters.manifestComponents.fromNullableJsonSet()
val androidRes = parameters.androidRes.fromNullableJsonSet()
val nativeLibs = parameters.nativeLibs.fromNullableJsonSet()
val androidAssets = parameters.androidAssets.fromNullableJsonSet()
physicalArtifacts.forEach { artifact ->
builders.merge(
artifact.coordinates,
DependencyBuilder(artifact.coordinates).apply { files.add(artifact.file) },
DependencyBuilder::concat
)
}
// A dependency can appear in the graph even though it's just a .pom (.module) file. E.g., kotlinx-coroutines-core.
// This is a fallback so all such dependencies have a file written to disk.
dependencies.forEach { dependency ->
// Do not add dependencies that are already known again
val coordinatesAlreadyKnown = builders.values.any {
it.coordinates == dependency
// TODO(tsr): including the following in the check is too aggressive and can lead to failures in the
// ProjectVariant::dependencies function when looking for a file. This can happen, I think, when a dependency
// is specified as an external dependency but ends up getting resolved as a local project dependency instead.
// The simplest thing is to include the json file for this dep twice. Wastes some disk space (etc), but
// solves the problem. I doubt this is the best solution to the problem.
// If the dependency is pointing at a project, there might already be an artifact
// stored under matching IncludedBuildCoordinates.
// || (it.coordinates is IncludedBuildCoordinates
// && dependency.identifier == it.coordinates.resolvedProject.identifier
// && dependency.gradleVariantIdentification.variantMatches(it.coordinates.resolvedProject))
}
if (!coordinatesAlreadyKnown) {
builders.merge(
dependency,
DependencyBuilder(dependency),
DependencyBuilder::concat
)
}
}
merge(explodedJars)
merge(inlineMembers)
merge(typealiases)
merge(serviceLoaders)
merge(annotationProcessors)
merge(manifestComponents)
merge(androidRes)
merge(nativeLibs)
merge(androidAssets)
// Write every dependency to its own file in the output directory
builders.values.asSequence()
.map { it.build() }
.forEach { dependency ->
val coordinates = dependency.coordinates
outputDir.file(coordinates.toFileName()).get().asFile.bufferWriteJson(dependency)
}
}
private fun > merge(dependencies: Set) {
dependencies.forEach {
builders.merge(
it.coordinates,
DependencyBuilder(it.coordinates).apply { capabilities.addAll(it.toCapabilities()) },
DependencyBuilder::concat
)
}
}
}
private class DependencyBuilder(val coordinates: Coordinates) {
val capabilities: MutableList = mutableListOf()
val files: MutableList = mutableListOf()
fun concat(other: DependencyBuilder): DependencyBuilder {
files.addAll(other.files)
other.capabilities.forEach { otherCapability ->
val existing = capabilities.find { it.javaClass.canonicalName == otherCapability.javaClass.canonicalName }
if (existing != null) {
val merged = existing.merge(otherCapability)
capabilities.remove(existing)
capabilities.add(merged)
} else {
capabilities.add(otherCapability)
}
}
return this
}
fun build(): Dependency {
val capabilities: Map = capabilities.associateBy { it.javaClass.canonicalName }
return when (coordinates) {
is ProjectCoordinates -> ProjectDependency(coordinates, capabilities, files)
is ModuleCoordinates -> ModuleDependency(coordinates, capabilities, files)
is FlatCoordinates -> FlatDependency(coordinates, capabilities, files)
is IncludedBuildCoordinates -> IncludedBuildDependency(coordinates, capabilities, files)
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy