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

com.autonomousapps.tasks.SynthesizeDependenciesTask.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.utils.*
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 {
    group = TASK_GROUP_DEP_INTERNAL
    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 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])
      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 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 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 || (
            // 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(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 - 2024 Weber Informatics LLC | Privacy Policy