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

dagger.hilt.android.plugin.util.AndroidGradleCompat.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2021 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.hilt.android.plugin.util

import com.android.build.api.instrumentation.AsmClassVisitorFactory
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.instrumentation.InstrumentationParameters
import com.android.build.api.instrumentation.InstrumentationScope
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.api.variant.ApplicationVariant
import com.android.build.api.variant.Component
import com.android.build.api.variant.LibraryVariant
import com.android.build.api.variant.Variant
import org.gradle.api.Project

/**
 * Compatibility version of [com.android.build.api.variant.AndroidComponentsExtension]
 * - In AGP 4.2 its package is 'com.android.build.api.extension'
 * - In AGP 7.0 its packages is 'com.android.build.api.variant'
 */
sealed class AndroidComponentsExtensionCompat {

  /**
   * A combined compatibility function of
   * [com.android.build.api.variant.AndroidComponentsExtension.onVariants] that includes also
   * [AndroidTest] and [UnitTest] variants.
   */
  abstract fun onAllVariants(block: (ComponentCompat) -> Unit)

  class Api70Impl(
    private val actual: AndroidComponentsExtension<*, *, *>
  ) : AndroidComponentsExtensionCompat() {
    override fun onAllVariants(block: (ComponentCompat) -> Unit) {
      actual.onVariants { variant ->
        // Use reflection to get the AndroidTest component out of the variant because a binary
        // incompatible change was introduced in AGP 7.0-beta05 that changed the return type of the
        // method.
        fun ApplicationVariant.getAndroidTest() =
          this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
        fun LibraryVariant.getAndroidTest() =
          this::class.java.getDeclaredMethod("getAndroidTest").invoke(this) as? Component
        block.invoke(ComponentCompat.Api70Impl(variant))
        when (variant) {
          is ApplicationVariant -> variant.getAndroidTest()
          is LibraryVariant -> variant.getAndroidTest()
          else -> null
        }?.let { block.invoke(ComponentCompat.Api70Impl(it)) }
        // Use reflection too to get the UnitTest component since in 7.2
        // com.android.build.api.component.UnitTest was removed and replaced by
        // com.android.build.api.variant.UnitTest causing the return type of Variant#getUnitTest()
        // to change and break ABI.
        fun Variant.getUnitTest() =
          this::class.java.getDeclaredMethod("getUnitTest").invoke(this) as? Component
        variant.getUnitTest()?.let { block.invoke(ComponentCompat.Api70Impl(it)) }
      }
    }
  }

  class Api42Impl(private val actual: Any) : AndroidComponentsExtensionCompat() {

    private val extensionClazz =
      Class.forName("com.android.build.api.extension.AndroidComponentsExtension")

    private val variantSelectorClazz =
      Class.forName("com.android.build.api.extension.VariantSelector")

    override fun onAllVariants(block: (ComponentCompat) -> Unit) {
      val selector = extensionClazz.getDeclaredMethod("selector").invoke(actual)
      val allSelector = variantSelectorClazz.getDeclaredMethod("all").invoke(selector)
      val wrapFunction: (Any) -> Unit = {
        block.invoke(ComponentCompat.Api42Impl(it))
      }
      listOf("onVariants", "androidTests", "unitTests").forEach { methodName ->
        extensionClazz.getDeclaredMethod(
          methodName, variantSelectorClazz, Function1::class.java
        ).invoke(actual, allSelector, wrapFunction)
      }
    }
  }

  companion object {
    fun getAndroidComponentsExtension(project: Project): AndroidComponentsExtensionCompat {
      return if (
        findClass("com.android.build.api.variant.AndroidComponentsExtension") != null
      ) {
        val actualExtension = project.extensions.getByType(AndroidComponentsExtension::class.java)
        Api70Impl(actualExtension)
      } else {
        val actualExtension = project.extensions.getByType(
          Class.forName("com.android.build.api.extension.AndroidComponentsExtension")
        )
        Api42Impl(actualExtension)
      }
    }
  }
}

/**
 * Compatibility version of [com.android.build.api.variant.Component]
 * - In AGP 4.2 its package is 'com.android.build.api.component'
 * - In AGP 7.0 its packages is 'com.android.build.api.variant'
 */
@Suppress("UnstableApiUsage") // ASM Pipeline APIs
sealed class ComponentCompat {

  /**
   * Redeclaration of [com.android.build.api.variant.ComponentIdentity.name]
   */
  abstract val name: String

  /**
   * Redeclaration of [com.android.build.api.variant.Component.transformClassesWith]
   */
  abstract fun  transformClassesWith(
    classVisitorFactoryImplClass: Class>,
    scope: InstrumentationScope,
    instrumentationParamsConfig: (ParamT) -> Unit
  )

  /**
   * Redeclaration of [com.android.build.api.variant.Component.setAsmFramesComputationMode]
   */
  abstract fun setAsmFramesComputationMode(mode: FramesComputationMode)

  class Api70Impl(private val component: Component) : ComponentCompat() {

    override val name: String
      get() = component.name

    override fun  transformClassesWith(
      classVisitorFactoryImplClass: Class>,
      scope: InstrumentationScope,
      instrumentationParamsConfig: (ParamT) -> Unit
    ) {
      component.transformClassesWith(
        classVisitorFactoryImplClass, scope, instrumentationParamsConfig
      )
    }

    override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
      component.setAsmFramesComputationMode(mode)
    }
  }

  class Api42Impl(private val actual: Any) : ComponentCompat() {

    private val componentClazz = Class.forName("com.android.build.api.component.Component")

    override val name: String
      get() = componentClazz.getMethod("getName").invoke(actual) as String

    override fun  transformClassesWith(
      classVisitorFactoryImplClass: Class>,
      scope: InstrumentationScope,
      instrumentationParamsConfig: (ParamT) -> Unit
    ) {
      componentClazz.getDeclaredMethod(
        "transformClassesWith",
        Class::class.java, InstrumentationScope::class.java, Function1::class.java
      ).invoke(actual, classVisitorFactoryImplClass, scope, instrumentationParamsConfig)
    }

    override fun setAsmFramesComputationMode(mode: FramesComputationMode) {
      componentClazz.getDeclaredMethod(
        "setAsmFramesComputationMode", FramesComputationMode::class.java
      ).invoke(actual, mode)
    }
  }
}

fun findClass(fqName: String) = try {
  Class.forName(fqName)
} catch (ex: ClassNotFoundException) {
  null
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy