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

com.autonomousapps.extension.DependenciesHandler.kt Maven / Gradle / Ivy

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

package com.autonomousapps.extension

import com.autonomousapps.internal.coordinatesOrPathMatch
import com.autonomousapps.model.Coordinates
import org.gradle.api.*
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.kotlin.dsl.setProperty
import org.intellij.lang.annotations.Language
import java.io.Serializable
import javax.inject.Inject

/**
 * ```
 * dependencyAnalysis {
 *   dependencies {
 *     // Set the given map. Used when printing advice and rewriting build scripts.
 *     map.set(/* map */)
 *
 *     // put a single key: value pair into the map. Used when printing advice and rewriting build scripts.
 *     map.put(key, value)
 *
 *     // put all entries from the given map into the map. Used when printing advice and rewriting build scripts.
 *     map.putAll(/* map */)
 *
 *     bundle("kotlin-stdlib") {
 *       // 1: include all in group as a single logical dependency
 *       includeGroup("org.jetbrains.kotlin")
 *
 *       // 2: include all supplied dependencies as a single logical dependency
 *       includeDependency("org.jetbrains.kotlin:kotlin-stdlib")
 *       includeDependency("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 *
 *       // 3: include all dependencies that match the regex as a single logical dependency
 *       include(".*kotlin-stdlib.*")
 *     }
 *   }
 * }
 * ```
 */
@Suppress("HasPlatformType")
open class DependenciesHandler @Inject constructor(objects: ObjectFactory) {

  val map = objects.mapProperty(String::class.java, String::class.java).convention(mutableMapOf())
  val bundles = objects.domainObjectContainer(BundleHandler::class.java)

  init {
    // With Kotlin plugin 1.4, the stdlib is now applied by default. It makes no sense to warn users
    // about this, even if it is "incorrect." So make all stdlib-related libraries a bundle and call
    // it a day.
    bundle("__kotlin-stdlib") {
      include(".*kotlin-stdlib.*")
    }
    // Firebase / Google services are tightly coupled
    bundle("__firebase") {
      includeGroup("com.google.firebase")
      includeGroup("com.google.android.gms")
    }
  }

  companion object {
    /** Transform [map] into lambda function. Returns requested key as value if key isn't present. */
    internal fun Map.toLambda(): (String) -> String = { s ->
      getOrDefault(s, s)
    }
  }

  fun bundle(name: String, action: Action) {
    try {
      bundles.create(name) {
        action.execute(this)
      }
    } catch (e: GradleException) {
      throw wrapException(e)
    }
  }

  internal fun serializableBundles(): SerializableBundles = SerializableBundles.of(bundles)

  class SerializableBundles(
    @get:Input val rules: Map>,
    @get:Input val primaries: Map
  ) : Serializable {

    /** Returns the collection of bundle rules that [coordinates] is a member of. (May be 0 or more.) */
    internal fun matchingBundles(coordinates: Coordinates): Map> {
      if (rules.isEmpty()) return emptyMap()

      return rules.filter { (_, regexes) ->
        regexes.any { regex ->
          coordinatesOrPathMatch(coordinates, regex)
        }
      }
    }

    companion object {
      internal fun of(
        bundles: NamedDomainObjectContainer
      ): SerializableBundles {
        val rules = mutableMapOf>()
        val primaries = mutableMapOf()
        bundles.asMap.map { (name, groups) ->
          rules[name] = groups.includes.get()

          val primary = groups.primary.get()
          if (primary.isNotEmpty()) {
            primaries[name] = primary
          }
        }

        return SerializableBundles(rules, primaries)
      }
    }
  }

  private fun wrapException(e: GradleException) = if (e is InvalidUserDataException)
    GradleException("You must configure this project either at the root or the project level, not both", e)
  else e
}

/**
 * ```
 * bundle("kotlin-stdlib") {
 *   // 0 (Optional): Specify the primary entry point that the user is "supposed" to declare.
 *   primary("org.something:primary-entry-point")
 *
 *   // 1: include all in group as a single logical dependency
 *   includeGroup("org.jetbrains.kotlin")
 *
 *   // 2: include all supplied dependencies as a single logical dependency
 *   includeDependency("org.jetbrains.kotlin:kotlin-stdlib")
 *   includeDependency("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 *
 *   // 3: include all dependencies that match the regex as a single logical dependency
 *   include(".*kotlin-stdlib.*")
 * }
 * ```
 */
open class BundleHandler @Inject constructor(
  private val name: String,
  objects: ObjectFactory
) : Named {

  override fun getName(): String = name

  val primary: Property = objects.property(String::class.java).convention("")
  val includes: SetProperty = objects.setProperty().convention(emptySet())

  fun primary(identifier: String) {
    primary.set(identifier)
    primary.disallowChanges()
  }

  fun includeGroup(group: String) {
    include("^$group:.*")
  }

  fun includeDependency(identifier: String) {
    include("^$identifier\$")
  }

  fun include(@Language("RegExp") regex: String) {
    include(regex.toRegex())
  }

  fun include(regex: Regex) {
    includes.add(regex)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy