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

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

There is a newer version: 2.0.2
Show newest version
package com.autonomousapps.tasks

import com.autonomousapps.TASK_GROUP_DEP_INTERNAL
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.intermediates.AnnotationProcessorDependency
import com.autonomousapps.model.intermediates.ExplodingAbi
import com.autonomousapps.model.intermediates.ExplodingBytecode
import com.autonomousapps.model.intermediates.ExplodingSourceCode
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 {
    group = TASK_GROUP_DEP_INTERNAL
    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.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

  @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])
      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 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().orEmpty()
      val explodedSourceCode = parameters.explodedSourceCode.fromJsonSet()
      val androidResSource = parameters.androidResSource.fromNullableJsonSet().orEmpty()
      val androidAssetsSource = parameters.androidAssetsSource.fromNullableJsonSet().orEmpty()

      explodedBytecode.forEach { bytecode ->
        builders.merge(
          bytecode.className,
          CodeSourceBuilder(bytecode.className).apply {
            relativePath = bytecode.relativePath
            usedClasses.addAll(bytecode.usedClasses)
          },
          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(emptySet(), emptyMap())
      )
      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().also { sources ->
          codeSource.mapTo(sources) { it.excludeUsages(usagesExclusions) }
          androidResSource.mapTo(sources) { it.excludeUsages(usagesExclusions) }
          sources.addAll(androidAssetsSource)
        },
        classpath = classpath,
        annotationProcessors = annotationProcessors
      )

      output.bufferWriteJson(projectVariant)
    }

    private fun CodeSource.excludeUsages(usagesExclusions: UsagesExclusions): CodeSource {
      return copy(
        usedClasses = usagesExclusions.excludeClassesFromSet(usedClasses),
        imports = usagesExclusions.excludeClassesFromSet(imports),
      )
    }

    private fun AndroidResSource.excludeUsages(usagesExclusions: UsagesExclusions): AndroidResSource {
      return copy(
        usedClasses = usagesExclusions.excludeClassesFromSet(usedClasses),
      )
    }
  }
}

private class CodeSourceBuilder(val className: String) {

  var relativePath: String? = null
  var kind: CodeSource.Kind = CodeSource.Kind.UNKNOWN
  val usedClasses = mutableSetOf()
  val exposedClasses = mutableSetOf()
  val imports = mutableSetOf()

  fun concat(other: CodeSourceBuilder): CodeSourceBuilder {
    usedClasses.addAll(other.usedClasses)
    exposedClasses.addAll(other.exposedClasses)
    imports.addAll(other.imports)
    other.relativePath?.let { relativePath = it }
    kind = other.kind
    return this
  }

  fun build(): CodeSource {
    val relativePath = checkNotNull(relativePath) { "'relativePath' was null for $className" }
    return CodeSource(
      relativePath = relativePath,
      kind = kind,
      className = className,
      usedClasses = usedClasses,
      exposedClasses = exposedClasses,
      imports = imports
    )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy