diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/internal/AndroidDependencyResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/internal/AndroidDependencyResolver.kt deleted file mode 100644 index f601687009a..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/android/internal/AndroidDependencyResolver.kt +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.gradle.targets.android.internal - -import com.android.build.gradle.BaseExtension -import com.android.build.gradle.api.AndroidSourceSet -import com.android.build.gradle.internal.LoggerWrapper -import com.android.build.gradle.internal.publishing.AndroidArtifacts -import com.android.sdklib.IAndroidTarget -import com.android.sdklib.repository.AndroidSdkHandler -import com.android.sdklib.repository.LoggerProgressIndicatorWrapper -import org.gradle.api.Project -import org.gradle.api.artifacts.* -import org.gradle.api.artifacts.component.ModuleComponentIdentifier -import org.gradle.api.artifacts.component.ModuleComponentSelector -import org.gradle.api.artifacts.result.ArtifactResult -import org.gradle.api.artifacts.result.ComponentResult -import org.gradle.api.artifacts.result.ResolvedArtifactResult -import org.gradle.api.artifacts.result.ResolvedComponentResult -import org.gradle.api.attributes.AttributeContainer -import org.gradle.api.attributes.AttributesSchema -import org.gradle.api.capabilities.Capability -import org.gradle.api.file.FileSystemLocation -import org.gradle.api.internal.artifacts.configurations.ConfigurationInternal -import org.gradle.api.internal.artifacts.dependencies.ProjectDependencyInternal -import org.gradle.api.internal.attributes.AttributeContainerInternal -import org.gradle.api.internal.attributes.AttributesSchemaInternal -import org.gradle.api.provider.Provider -import org.gradle.api.services.BuildService -import org.gradle.api.services.BuildServiceParameters -import org.gradle.api.services.BuildServiceRegistry -import org.gradle.internal.component.local.model.DefaultLocalComponentGraphResolveState -import org.gradle.internal.component.model.AttributeConfigurationSelector -import org.gradle.internal.component.model.ComponentResolveMetadata -import org.gradle.internal.component.model.ConfigurationMetadata -import org.gradle.internal.component.model.IvyArtifactName -import org.jetbrains.kotlin.gradle.plugin.mpp.internal -import org.jetbrains.kotlin.gradle.plugin.sources.android.findKotlinSourceSet -import org.jetbrains.kotlin.gradle.plugin.sources.internal -import org.jetbrains.kotlin.gradle.utils.forAllAndroidVariants -import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast -import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName -import java.io.File -import java.nio.file.Path -import kotlin.reflect.KFunction -import kotlin.reflect.full.functions -import kotlin.reflect.full.staticFunctions - -data class AndroidDependency( - val name: String? = null, - val jar: File? = null, - val source: File? = null, - val group: String? = null, - val version: String? = null, - val collection: Set? = null -) - -/** - * Used in KMM - */ -@Suppress("unused") -object AndroidDependencyResolver { - private fun getAndroidSdkJar(project: Project, versions: List): AndroidDependency? { - val androidExtension = project.extensions.findByName("android") as BaseExtension? ?: return null - val useNioPath = versions[0].toInt() >= 7 - - val sdkHandler = if (useNioPath) { - val androidLocationBuildService = getAndroidLocationBuildService(project) ?: return null - val androidLocationsProvider = getClassOrNull("com.android.prefs.AndroidLocationsProvider") ?: return null - val getInstance = - AndroidSdkHandler::class.java.getMethodOrNull("getInstance", androidLocationsProvider, Path::class.java) ?: return null - getInstance(null, androidLocationBuildService, androidExtension.sdkDirectory.toPath()) as AndroidSdkHandler - } else { - AndroidSdkHandler.getInstance(androidExtension.sdkDirectory) - } - val logger = LoggerProgressIndicatorWrapper(LoggerWrapper(project.logger)) - val androidTarget = - sdkHandler.getAndroidTargetManager(logger).getTargetFromHashString(androidExtension.compileSdkVersion, logger) ?: return null - - val jar: File - val sources: File - if (useNioPath) { - val getPath = IAndroidTarget::class.java.getMethodOrNull("getPath", Int::class.java) ?: return null - jar = (getPath(androidTarget, IAndroidTarget.ANDROID_JAR) as Path).toFile() - sources = (getPath(androidTarget, IAndroidTarget.SOURCES) as Path).toFile() - } else { - jar = File(androidTarget.getPath(IAndroidTarget.ANDROID_JAR)) - sources = File(androidTarget.getPath(IAndroidTarget.SOURCES)) - } - return AndroidDependency( - androidTarget.fullName, - jar, - sources - ) - } - - private fun getAndroidLocationBuildService(project: Project): BuildService? = - getClassOrNull("com.android.build.gradle.internal.services.BuildServicesKt")?.let { buildServices -> - getClassOrNull("com.android.build.gradle.internal.services.AndroidLocationsBuildService")?.let { androidLocationsService -> - val getBuildService = - buildServices.getMethodOrNull("getBuildService", BuildServiceRegistry::class.java, androidLocationsService.javaClass) - @Suppress("UNCHECKED_CAST") - (getBuildService?.invoke( - null, - project.gradle.sharedServices, - androidLocationsService - ) as Provider>?)?.get() - } - } - - private fun getClassOrNull(s: String) = - try { - Class.forName(s) - } catch (e: Exception) { - null - } - - private fun Class<*>.getMethodOrNull(name: String, vararg parameterTypes: Class<*>) = - try { - getMethod(name, *parameterTypes) - } catch (e: Exception) { - null - } - - private fun getAndroidPluginVersions(): List? { - val version = getClassOrNull("com.android.Version")?.let { - try { - it.getField("ANDROID_GRADLE_PLUGIN_VERSION").get(null) as String - } catch (e: Exception) { - return null - } - } ?: return null - return version.split('.') - } - - private fun isAndroidPluginCompatible(versions: List): Boolean { - return versions[0].toInt() >= 4 || versions[0].toInt() >= 3 && versions[1].toInt() >= 6 - } - - private data class SourceSetConfigs(val implConfigs: List, val compileConfigs: MutableList = ArrayList()) - - fun getAndroidSourceSetDependencies(project: Project): Map?> { - val androidPluginVersions = getAndroidPluginVersions() ?: return emptyMap() - if (!isAndroidPluginCompatible(androidPluginVersions)) return emptyMap() - - val androidSdkJar = getAndroidSdkJar(project, androidPluginVersions) ?: return emptyMap() - val sourceSet2Impl = HashMap() - val allImplConfigs = HashSet() - - project.forAllAndroidVariants { variant -> - val compileConfig = variant.compileConfiguration - variant.sourceSets.filterIsInstance(AndroidSourceSet::class.java).map { androidSourceSet -> - val implConfigs = listOf(project.configurations.getByName(androidSourceSet.implementationConfigurationName)) + - project.findKotlinSourceSet(androidSourceSet)?.internal?.compilations.orEmpty().map { compilation -> - compilation.internal.configurations.implementationConfiguration - } - allImplConfigs.addAll(implConfigs) - val sourceSetConfigs = - sourceSet2Impl.computeIfAbsent(lowerCamelCaseName("android", androidSourceSet.name)) { SourceSetConfigs(implConfigs) } - // The same sourceset can be included into multiple variants (e.g. androidMain) - sourceSetConfigs.compileConfigs.add(compileConfig) - } - } - - val attributesSchema = project.dependencies.attributesSchema - - return sourceSet2Impl.mapValues { (sourceSetName, sourceSetConfigs) -> - val dependencies = findDependencies(allImplConfigs, sourceSetConfigs, attributesSchema) - - val selfResolved = dependencies.filterIsInstance().map { dependency -> - val collection = dependency.resolve().takeIf(Collection::isNotEmpty) - AndroidDependency(dependency.name, group = dependency.group, collection = collection) - } - val resolvedExternal = - collectDependencies(dependencies.filterIsInstance(), sourceSetConfigs.compileConfigs) - - val result = (selfResolved + resolvedExternal + androidSdkJar).toMutableList() - - if (sourceSetName == "androidMain") { - // this is a terrible hack, but looks like the only way, other than proper support via light-classes - val task = project.tasks.findByName("processDebugResources") - if (task != null) { - getClassOrNull("com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask")?.let { linkAppClass -> - @Suppress("UNCHECKED_CAST") - val rClassOutputJar = - linkAppClass.getMethodOrNull("getRClassOutputJar")?.invoke(task) as Provider? - rClassOutputJar?.orNull?.asFile?.let { result += AndroidDependency("R.jar", it) } - } - } - } - - result - }.toMap() - } - - private fun collectDependencies( - dependencies: List, - compileClasspathConfigs: List - ): Set { - val processedJarArtifactType = try { - AndroidArtifacts.ArtifactType.valueOf("PROCESSED_JAR") - } catch (e: IllegalArgumentException) { - AndroidArtifacts.ArtifactType.JAR - } - val viewConfig: (ArtifactView.ViewConfiguration) -> Unit = { config -> - config.attributes { it.attribute(AndroidArtifacts.ARTIFACT_TYPE, processedJarArtifactType.type) } - config.isLenient = true - } - - val allResults = LinkedHashSet() - val allComponents = LinkedHashSet() - - compileClasspathConfigs.forEach { config -> - // If the current sourceset's implementation configuration is included into several compile classpath configuration's, - // we'll take only the results which are the same across all of them. The dependencies which resolve differently will appear - // in the more specific sourcesets. - with(config.incoming) { - allResults.addOrRetainAll(artifactView(viewConfig).artifacts.artifacts, ArtifactResult::getId) - allComponents.addOrRetainAll(resolutionResult.allComponents, ComponentResult::getId) - } - } - - val resolvedArtifacts = allResults.mapNotNull { - val componentIdentifier = it.id.componentIdentifier as? ModuleComponentIdentifier ?: return@mapNotNull null - val id = componentIdentifier.moduleIdentifier ?: return@mapNotNull null - id to AndroidDependency(id.name, it.file, null, id.group, componentIdentifier.version) - }.toMap() - - val resolutionResults = allComponents.mapNotNull { - val id = it.moduleVersion?.module ?: return@mapNotNull null - id to it - }.toMap() - - return dependencies.flatMapTo(HashSet()) { dependency -> - val deps = HashSet().also { doCollectDependencies(listOf(dependency.module), resolutionResults, it) } - deps.mapNotNull { resolvedArtifacts[it] } - } - } - - private tailrec fun doCollectDependencies( - modules: List, - resolutionResults: Map, - result: MutableSet - ) { - if (modules.isEmpty()) return - val newModules = modules.filter { result.add(it) }.mapNotNull { resolutionResults[it] }.flatMap { it.dependencies } - .mapNotNull { (it.requested as? ModuleComponentSelector)?.moduleIdentifier } - doCollectDependencies(newModules, resolutionResults, result) - } - - private fun findDependencies( - implConfigs: Set, - sourceSetConfigs: SourceSetConfigs, - attributesSchema: AttributesSchema - ): Set { - // Using the attributes of the first configuration. If there are multiple configurations, then differences in resolve would be - // discarded later anyway, so they won't make any difference - val attributes = sourceSetConfigs.compileConfigs.first().attributes - - return HashSet().also { - doFindDependencies(implConfigs, sourceSetConfigs.implConfigs, attributes, attributesSchema, it) - } - } - - private tailrec fun doFindDependencies( - implConfigs: Set, - configs: List, - attributes: AttributeContainer, - attributesSchema: AttributesSchema, - result: MutableSet, - visited: MutableSet = HashSet() - ) { - if (configs.isEmpty()) return - - val allDependencies = configs.flatMap { it.dependencies } - val allExtendsFrom = configs.flatMap { it.extendsFrom }.toMutableList() - - result.addAll(allDependencies.filter { it !is ProjectDependency }) - - val projectDependencies = allDependencies.filterIsInstance() - allExtendsFrom.addAll(selectConfigurations(projectDependencies, attributes, attributesSchema)) - - doFindDependencies( - implConfigs, - allExtendsFrom.filter { it !in implConfigs && visited.add(it) }, - attributes, - attributesSchema, - result - ) - } - - private fun selectConfigurations( - projectDependencies: List, - attributes: AttributeContainer, - attributesSchema: AttributesSchema, - ): List = if (isGradleVersionAtLeast(7, 6)) { - selectConfigurationsGradle76(projectDependencies, attributes, attributesSchema) - } else { - selectConfigurationsGradleLegacy(projectDependencies, attributes, attributesSchema) - } - - private fun selectConfigurationsGradle76( - projectDependencies: List, - attributes: AttributeContainer, - attributesSchema: AttributesSchema, - ) = projectDependencies.flatMap { projectDependency -> - val rootComponentMetaData = (projectDependency.findProjectConfiguration() as? ConfigurationInternal)?.toRootComponentMetaData() - ?: return@flatMap emptyList() - - val matching = AttributeConfigurationSelector.selectVariantsUsingAttributeMatching( - (attributes as? AttributeContainerInternal)?.asImmutable() ?: return@flatMap emptyList(), - emptyList(), - DefaultLocalComponentGraphResolveState(rootComponentMetaData), - (attributesSchema as? AttributesSchemaInternal) ?: return@flatMap emptyList(), - emptyList() - ) - matching.variants.mapNotNull { projectDependency.dependencyProject.configurations.findByName(it.name) } - } - - private fun selectConfigurationsGradleLegacy( - projectDependencies: List, - attributes: AttributeContainer, - attributesSchema: AttributesSchema, - ) = projectDependencies.mapNotNull { projectDependency -> - val configurationInternal = projectDependency.findProjectConfiguration() as? ConfigurationInternal ?: return@mapNotNull null - - @Suppress("UNCHECKED_CAST") val toRootComponentMetadata = - (configurationInternal::class.functions.find { it.name == "toRootComponentMetaData" } as? KFunction - ?: error("Method ConfigurationInternal.toRootComponentMetaData does not exist")) - val rootComponentMetaData = toRootComponentMetadata.call(configurationInternal) - - @Suppress("UNCHECKED_CAST") val selectConfiguration = - AttributeConfigurationSelector::class.staticFunctions.find { it.name == "selectConfigurationUsingAttributeMatching" } as? KFunction - ?: error("Static method AttributeConfigurationSelector.selectConfigurationUsingAttributeMatching does not exist") - val matching = selectConfiguration.call( - (attributes as? AttributeContainerInternal)?.asImmutable() ?: return@mapNotNull null, - emptyList(), - rootComponentMetaData, - (attributesSchema as? AttributesSchemaInternal) ?: return@mapNotNull null, - emptyList() - ) - projectDependency.dependencyProject.configurations.findByName(matching.name) - } - - private inline fun MutableSet.addOrRetainAll(c: Collection, crossinline selector: (E) -> K) { - if (isEmpty()) { - addAll(c) - } else { - val selectedSet = c.map(selector).toSet() - retainAll { selectedSet.contains(selector(it)) } - } - } -} \ No newline at end of file