All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.autonomousapps.tasks.AdviceTask.kt Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
@file:Suppress("UnstableApiUsage")

package com.autonomousapps.tasks

import com.autonomousapps.TASK_GROUP_DEP
import com.autonomousapps.advice.ComponentWithTransitives
import com.autonomousapps.extension.Behavior
import com.autonomousapps.extension.DependenciesHandler
import com.autonomousapps.internal.*
import com.autonomousapps.internal.advice.Advisor
import com.autonomousapps.internal.advice.filter.*
import com.autonomousapps.internal.utils.*
import com.autonomousapps.services.InMemoryCache
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*

/**
 * Produces human- and machine-readable advice on how to modify a project's dependencies in order to have a healthy
 * build.
 */
@CacheableTask
abstract class AdviceTask : DefaultTask() {

  init {
    group = TASK_GROUP_DEP
    description = "Provides advice on how best to declare the project's dependencies"
  }

  /**
   * A [`Set`][Component].
   */
  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val allComponentsReport: RegularFileProperty

  /**
   * A [`Set`][ComponentWithTransitives].
   */
  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val allComponentsWithTransitives: RegularFileProperty

  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val unusedDependenciesReport: RegularFileProperty

  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val usedTransitiveDependenciesReport: RegularFileProperty

  @get:Optional
  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val abiDependenciesReport: RegularFileProperty

  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val allDeclaredDependenciesReport: RegularFileProperty

  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val unusedProcsReport: RegularFileProperty

  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val serviceLoaders: RegularFileProperty

  /**
   * [`Set`][VariantDependency]
   */
  @get:PathSensitive(PathSensitivity.NONE)
  @get:InputFile
  abstract val usedVariantDependencies: RegularFileProperty

  @get:Internal
  lateinit var dependenciesHandler: DependenciesHandler

  @Input
  fun getDependencyBundles(): Map> {
    return dependenciesHandler.bundles.asMap.map { (name, groups) ->
      name to groups.includes.get()
    }.toMap()
  }

  @get:Input
  abstract val ignoreKtx: Property

  @get:Input
  abstract val dataBindingEnabled: Property

  @get:Input
  abstract val viewBindingEnabled: Property

  /*
   * Severity
   */

  @get:Input
  abstract val anyBehavior: Property

  @get:Input
  abstract val unusedDependenciesBehavior: Property

  @get:Input
  abstract val usedTransitiveDependenciesBehavior: Property

  @get:Input
  abstract val incorrectConfigurationBehavior: Property

  @get:Input
  abstract val unusedProcsBehavior: Property

  @get:Input
  abstract val compileOnlyBehavior: Property

  /*
   * Outputs
   */

  @get:OutputFile
  abstract val adviceReport: RegularFileProperty

  @get:OutputFile
  abstract val advicePrettyReport: RegularFileProperty

  @get:OutputFile
  abstract val adviceConsoleReport: RegularFileProperty

  @get:OutputFile
  abstract val adviceConsolePrettyReport: RegularFileProperty

  @get:Internal
  abstract val inMemoryCacheProvider: Property

  @TaskAction
  fun action() {
    // Output
    val adviceFile = adviceReport.getAndDelete()
    val advicePrettyFile = advicePrettyReport.getAndDelete()
    val adviceConsoleFile = adviceConsoleReport.getAndDelete()
    val adviceConsolePrettyFile = adviceConsolePrettyReport.getAndDelete()

    // Inputs
    val usedVariantDependencies = usedVariantDependencies.fromJsonSet()
    val allComponents = allComponentsReport.fromJsonSet()
    val allComponentsWithTransitives = allComponentsWithTransitives.fromJsonSet()
    val unusedDirectComponents = unusedDependenciesReport.fromJsonSet()
    val usedTransitiveComponents = usedTransitiveDependenciesReport.fromJsonSet()
    val abiDeps = abiDependenciesReport.orNull?.asFile?.readText()?.fromJsonSet()
      ?.mapToSet { it.dependency }
      ?: emptySet()
    val allDeclaredDeps = allDeclaredDependenciesReport.fromJsonSet()
      .mapToSet { it.dependency }
      .filterToSet { it.configurationName != null }
    val unusedProcs = unusedProcsReport.fromJsonSet()

    // Print to the console several lists:
    // 1. Dependencies that should be removed.
    // 2. Dependencies that are already declared and whose configurations should be modified.
    // 3. Dependencies that should be added and the configurations on which to add them.
    // 4. Dependencies that are candidates to be compileOnly, but aren't currently.
    // 5. Annotation processors that are declared but are not used.

    val advisor = Advisor(
      usedVariantDependencies = usedVariantDependencies,
      allComponents = allComponents,
      allComponentsWithTransitives = allComponentsWithTransitives,
      unusedComponentsWithTransitives = unusedDirectComponents,
      usedTransitiveComponents = usedTransitiveComponents,
      abiDeps = abiDeps,
      allDeclaredDeps = allDeclaredDeps,
      unusedProcs = unusedProcs,
      serviceLoaders = serviceLoaders.fromJsonSet(),
      dependencyBundles = getDependencyBundles(),
      ignoreKtx = ignoreKtx.get()
    )

    val computedAdvice = advisor.compute(filterSpecBuilder())
    val advices = computedAdvice.getAdvices()
    val consoleReport = ConsoleReport.from(computedAdvice)

    adviceFile.writeText(advices.toJson())
    advicePrettyFile.writeText(advices.toPrettyString())
    adviceConsoleFile.writeText(consoleReport.toJson())
    adviceConsolePrettyFile.writeText(consoleReport.toPrettyString())

    if (advices.isNotEmpty()) {
      logger.debug("See machine-readable report at ${adviceFile.path}")
      logger.debug("See pretty report at ${advicePrettyFile.path}")
      logger.debug("See machine-readable console report at ${adviceConsoleFile.path}")
      logger.debug("See pretty console report at ${adviceConsolePrettyFile.path}")
    }
  }

  private fun filterSpecBuilder() = FilterSpecBuilder().apply {
    universalFilter = CompositeFilter(filters)
    anyBehavior = [email protected]()
    unusedDependenciesBehavior = [email protected]()
    usedTransitivesBehavior = usedTransitiveDependenciesBehavior.get()
    incorrectConfigurationsBehavior = incorrectConfigurationBehavior.get()
    unusedProcsBehavior = [email protected]()
    compileOnlyBehavior = [email protected]()
  }

  private val filters: List by lazy(mode = LazyThreadSafetyMode.NONE) {
    val filters = mutableListOf()
    if (dataBindingEnabled.get()) {
      filters.add(DataBindingFilter())
    }
    if (viewBindingEnabled.get()) {
      filters.add(ViewBindingFilter())
    }
    filters
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy