Merge branch 'slava/mpp-import-experiments'
# Conflicts: # idea/idea-gradle/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt
This commit is contained in:
+20
-6
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.idea.configuration
|
|||||||
|
|
||||||
import com.google.common.graph.GraphBuilder
|
import com.google.common.graph.GraphBuilder
|
||||||
import com.google.common.graph.Graphs
|
import com.google.common.graph.Graphs
|
||||||
|
import com.intellij.ide.plugins.PluginManager
|
||||||
|
import com.intellij.openapi.extensions.PluginId
|
||||||
import com.intellij.openapi.externalSystem.model.DataNode
|
import com.intellij.openapi.externalSystem.model.DataNode
|
||||||
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
import com.intellij.openapi.externalSystem.model.ProjectKeys
|
||||||
import com.intellij.openapi.externalSystem.model.project.*
|
import com.intellij.openapi.externalSystem.model.project.*
|
||||||
@@ -73,6 +75,10 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
initializeModuleData(gradleModule, moduleDataNode, projectDataNode, resolverCtx)
|
initializeModuleData(gradleModule, moduleDataNode, projectDataNode, resolverCtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getExtraCommandLineArgs(): List<String> {
|
||||||
|
return if (!androidPluginPresent) listOf("-Pkotlin.include.android.dependencies=true") else emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
override fun populateModuleExtraModels(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
|
||||||
if (ExternalSystemApiUtil.find(ideModule, BuildScriptClasspathData.KEY) == null) {
|
if (ExternalSystemApiUtil.find(ideModule, BuildScriptClasspathData.KEY) == null) {
|
||||||
val buildScriptClasspathData = buildClasspathData(gradleModule, resolverCtx)
|
val buildScriptClasspathData = buildClasspathData(gradleModule, resolverCtx)
|
||||||
@@ -228,6 +234,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
val proxyObjectCloningCache = WeakHashMap<Any, Any>()
|
val proxyObjectCloningCache = WeakHashMap<Any, Any>()
|
||||||
|
|
||||||
private var nativeDebugAdvertised = false
|
private var nativeDebugAdvertised = false
|
||||||
|
private val androidPluginPresent = PluginManager.getPlugin(PluginId.findId("org.jetbrains.android"))?.isEnabled ?: false
|
||||||
|
|
||||||
fun initializeModuleData(
|
fun initializeModuleData(
|
||||||
gradleModule: IdeaModule,
|
gradleModule: IdeaModule,
|
||||||
@@ -279,7 +286,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
|
|
||||||
val sourceSetToCompilationData = LinkedHashMap<KotlinSourceSet, MutableSet<GradleSourceSetData>>()
|
val sourceSetToCompilationData = LinkedHashMap<KotlinSourceSet, MutableSet<GradleSourceSetData>>()
|
||||||
for (target in mppModel.targets) {
|
for (target in mppModel.targets) {
|
||||||
if (target.platform == KotlinPlatform.ANDROID) continue
|
if (delegateToAndroidPlugin(target)) continue
|
||||||
if (target.name == KotlinTarget.METADATA_TARGET_NAME) continue
|
if (target.name == KotlinTarget.METADATA_TARGET_NAME) continue
|
||||||
val targetData = KotlinTargetData(target.name).also {
|
val targetData = KotlinTargetData(target.name).also {
|
||||||
it.archiveFile = target.jar?.archiveFile
|
it.archiveFile = target.jar?.archiveFile
|
||||||
@@ -353,7 +360,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
|
|
||||||
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
|
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
|
||||||
for (sourceSet in mppModel.sourceSets.values) {
|
for (sourceSet in mppModel.sourceSets.values) {
|
||||||
if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
if (delegateToAndroidPlugin(sourceSet)) continue
|
||||||
if (sourceSet.actualPlatforms.supports(KotlinPlatform.COMMON) && ignoreCommonSourceSets) continue
|
if (sourceSet.actualPlatforms.supports(KotlinPlatform.COMMON) && ignoreCommonSourceSets) continue
|
||||||
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
|
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
|
||||||
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
|
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
|
||||||
@@ -451,7 +458,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
.toMap()
|
.toMap()
|
||||||
if (resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java) == null) return
|
if (resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java) == null) return
|
||||||
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
|
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
|
||||||
if (dataNode == null || sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) return@processSourceSets
|
if (dataNode == null || delegateToAndroidPlugin(sourceSet)) return@processSourceSets
|
||||||
|
|
||||||
createContentRootData(
|
createContentRootData(
|
||||||
sourceSet.sourceDirs,
|
sourceSet.sourceDirs,
|
||||||
sourceSet.sourceType,
|
sourceSet.sourceType,
|
||||||
@@ -561,7 +569,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
}
|
}
|
||||||
val closedSourceSetGraph = Graphs.transitiveClosure(sourceSetGraph)
|
val closedSourceSetGraph = Graphs.transitiveClosure(sourceSetGraph)
|
||||||
for (sourceSet in closedSourceSetGraph.nodes()) {
|
for (sourceSet in closedSourceSetGraph.nodes()) {
|
||||||
val isAndroid = sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID
|
val isAndroid = delegateToAndroidPlugin(sourceSet)
|
||||||
val fromDataNode = if (isAndroid) {
|
val fromDataNode = if (isAndroid) {
|
||||||
ideModule
|
ideModule
|
||||||
} else {
|
} else {
|
||||||
@@ -581,7 +589,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
sourceSetInfo.addSourceSets(dependeeSourceSets, selfName, gradleModule, resolverCtx)
|
sourceSetInfo.addSourceSets(dependeeSourceSets, selfName, gradleModule, resolverCtx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
if (delegateToAndroidPlugin(sourceSet)) continue
|
||||||
for (dependeeSourceSet in dependeeSourceSets) {
|
for (dependeeSourceSet in dependeeSourceSets) {
|
||||||
val toDataNode = getSiblingKotlinModuleData(dependeeSourceSet, gradleModule, ideModule, resolverCtx) ?: continue
|
val toDataNode = getSiblingKotlinModuleData(dependeeSourceSet, gradleModule, ideModule, resolverCtx) ?: continue
|
||||||
addDependency(fromDataNode, toDataNode, dependeeSourceSet.isTestModule)
|
addDependency(fromDataNode, toDataNode, dependeeSourceSet.isTestModule)
|
||||||
@@ -750,7 +758,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (target in mppModel.targets) {
|
for (target in mppModel.targets) {
|
||||||
if (target.platform == KotlinPlatform.ANDROID) continue
|
if (delegateToAndroidPlugin(target)) continue
|
||||||
for (compilation in target.compilations) {
|
for (compilation in target.compilations) {
|
||||||
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
|
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
|
||||||
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
|
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
|
||||||
@@ -971,6 +979,12 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
|
|||||||
"true",
|
"true",
|
||||||
ignoreCase = true
|
ignoreCase = true
|
||||||
) ?: false
|
) ?: false
|
||||||
|
|
||||||
|
private fun delegateToAndroidPlugin(kotlinTarget: KotlinTarget): Boolean =
|
||||||
|
androidPluginPresent && kotlinTarget.platform == KotlinPlatform.ANDROID
|
||||||
|
|
||||||
|
private fun delegateToAndroidPlugin(kotlinSourceSet: KotlinSourceSet): Boolean =
|
||||||
|
androidPluginPresent && kotlinSourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.gradle.api.provider.Provider
|
|||||||
import org.gradle.api.tasks.Exec
|
import org.gradle.api.tasks.Exec
|
||||||
import org.gradle.api.tasks.TaskProvider
|
import org.gradle.api.tasks.TaskProvider
|
||||||
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
|
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel.Companion.NO_KOTLIN_NATIVE_HOME
|
||||||
|
import org.jetbrains.plugins.gradle.DefaultExternalDependencyId
|
||||||
import org.jetbrains.plugins.gradle.model.*
|
import org.jetbrains.plugins.gradle.model.*
|
||||||
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
|
import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder
|
||||||
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
|
import org.jetbrains.plugins.gradle.tooling.ModelBuilderService
|
||||||
@@ -144,12 +145,13 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val sourceSets =
|
val sourceSets =
|
||||||
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
|
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
|
||||||
|
val androidDeps = buildAndroidDeps(kotlinExt.javaClass.classLoader, project)
|
||||||
|
|
||||||
// Some performance optimisation: do not build metadata dependencies if source set is not common
|
// Some performance optimisation: do not build metadata dependencies if source set is not common
|
||||||
val doBuildMetadataDependencies =
|
val doBuildMetadataDependencies =
|
||||||
project.properties["build_metadata_dependencies_for_actualised_source_sets"]?.toString()?.toBoolean()
|
project.properties["build_metadata_dependencies_for_actualised_source_sets"]?.toString()?.toBoolean()
|
||||||
?: DEFAULT_BUILD_METADATA_DEPENDENCIES_FOR_ACTUALISED_SOURCE_SETS
|
?: DEFAULT_BUILD_METADATA_DEPENDENCIES_FOR_ACTUALISED_SOURCE_SETS
|
||||||
val allSourceSetsProtos = sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project, dependencyMapper) }
|
val allSourceSetsProtos = sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project, dependencyMapper, androidDeps) }
|
||||||
val allSourceSets = if (doBuildMetadataDependencies) {
|
val allSourceSets = if (doBuildMetadataDependencies) {
|
||||||
allSourceSetsProtos.map { proto -> proto.buildKotlinSourceSetImpl(true) }
|
allSourceSetsProtos.map { proto -> proto.buildKotlinSourceSetImpl(true) }
|
||||||
} else {
|
} else {
|
||||||
@@ -173,11 +175,28 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildAndroidDeps(classLoader: ClassLoader, project: Project): Map<String, List<Any>>? {
|
||||||
|
val includeAndroidDeps = project.properties["kotlin.include.android.dependencies"]?.toString()?.toBoolean() == true
|
||||||
|
if (includeAndroidDeps) {
|
||||||
|
try {
|
||||||
|
val resolverClass = classLoader.loadClass("org.jetbrains.kotlin.gradle.targets.android.internal.AndroidDependencyResolver")
|
||||||
|
val getAndroidSourceSetDependencies = resolverClass.getMethodOrNull("getAndroidSourceSetDependencies", Project::class.java)
|
||||||
|
val resolver = resolverClass.getField("INSTANCE").get(null)
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return getAndroidSourceSetDependencies?.let { it(resolver, project) } as Map<String, List<Any>>?
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logger.info("Unexpected exception", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildSourceSet(
|
private fun buildSourceSet(
|
||||||
gradleSourceSet: Named,
|
gradleSourceSet: Named,
|
||||||
dependencyResolver: DependencyResolver,
|
dependencyResolver: DependencyResolver,
|
||||||
project: Project,
|
project: Project,
|
||||||
dependencyMapper: KotlinDependencyMapper
|
dependencyMapper: KotlinDependencyMapper,
|
||||||
|
androidDeps: Map<String, List<Any>>?
|
||||||
): KotlinSourceSetProto? {
|
): KotlinSourceSetProto? {
|
||||||
val sourceSetClass = gradleSourceSet.javaClass
|
val sourceSetClass = gradleSourceSet.javaClass
|
||||||
val getLanguageSettings = sourceSetClass.getMethodOrNull("getLanguageSettings") ?: return null
|
val getLanguageSettings = sourceSetClass.getMethodOrNull("getLanguageSettings") ?: return null
|
||||||
@@ -192,7 +211,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
|||||||
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
|
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
|
||||||
|
|
||||||
val sourceSetDependenciesBuilder: () -> Array<KotlinDependencyId> = {
|
val sourceSetDependenciesBuilder: () -> Array<KotlinDependencyId> = {
|
||||||
buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project).map { dependencyMapper.getId(it) }.distinct()
|
buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project, androidDeps).map { dependencyMapper.getId(it) }.distinct()
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
}
|
}
|
||||||
return KotlinSourceSetProto(
|
return KotlinSourceSetProto(
|
||||||
@@ -598,7 +617,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
|||||||
private fun buildSourceSetDependencies(
|
private fun buildSourceSetDependencies(
|
||||||
gradleSourceSet: Named,
|
gradleSourceSet: Named,
|
||||||
dependencyResolver: DependencyResolver,
|
dependencyResolver: DependencyResolver,
|
||||||
project: Project
|
project: Project,
|
||||||
|
androidDeps: Map<String, List<Any>>?
|
||||||
): List<KotlinDependency> {
|
): List<KotlinDependency> {
|
||||||
return ArrayList<KotlinDependency>().apply {
|
return ArrayList<KotlinDependency>().apply {
|
||||||
val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
|
val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
|
||||||
@@ -614,9 +634,33 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
|||||||
this += buildDependencies(
|
this += buildDependencies(
|
||||||
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
|
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
|
||||||
)
|
)
|
||||||
|
this += buildAndroidSourceSetDependencies(androidDeps, gradleSourceSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildAndroidSourceSetDependencies(
|
||||||
|
androidDeps: Map<String, List<Any>>?,
|
||||||
|
gradleSourceSet: Named
|
||||||
|
): Collection<KotlinDependency> {
|
||||||
|
return androidDeps?.get(gradleSourceSet.name)?.map { it ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val collection = it["getCollection"] as Set<File>?
|
||||||
|
if (collection == null) {
|
||||||
|
DefaultExternalLibraryDependency().apply {
|
||||||
|
(id as? DefaultExternalDependencyId)?.apply {
|
||||||
|
name = it["getName"] as String?
|
||||||
|
group = it["getGroup"] as String?
|
||||||
|
version = it["getVersion"] as String?
|
||||||
|
}
|
||||||
|
file = it["getJar"] as File
|
||||||
|
source = it["getSource"] as File?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DefaultFileCollectionDependency(collection)
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun safelyGetArguments(compileKotlinTask: Task, accessor: Method?) = try {
|
private fun safelyGetArguments(compileKotlinTask: Task, accessor: Method?) = try {
|
||||||
accessor?.invoke(compileKotlinTask) as? List<String>
|
accessor?.invoke(compileKotlinTask) as? List<String>
|
||||||
|
|||||||
+5
-4
@@ -9,15 +9,16 @@ import groovy.lang.Closure
|
|||||||
import org.gradle.api.InvalidUserCodeException
|
import org.gradle.api.InvalidUserCodeException
|
||||||
import org.gradle.api.NamedDomainObjectCollection
|
import org.gradle.api.NamedDomainObjectCollection
|
||||||
import org.gradle.util.ConfigureUtil
|
import org.gradle.util.ConfigureUtil
|
||||||
import org.jetbrains.kotlin.gradle.plugin.*
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
|
||||||
|
|
||||||
open class KotlinMultiplatformExtension :
|
open class KotlinMultiplatformExtension :
|
||||||
KotlinProjectExtension(),
|
KotlinProjectExtension(),
|
||||||
KotlinTargetContainerWithPresetFunctions,
|
KotlinTargetContainerWithPresetFunctions,
|
||||||
KotlinTargetContainerWithNativeShortcuts
|
KotlinTargetContainerWithNativeShortcuts {
|
||||||
{
|
|
||||||
override lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>>
|
override lateinit var presets: NamedDomainObjectCollection<KotlinTargetPreset<*>>
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
|
|||||||
+22
-6
@@ -1,7 +1,6 @@
|
|||||||
package org.jetbrains.kotlin.gradle.plugin
|
package org.jetbrains.kotlin.gradle.plugin
|
||||||
|
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.*
|
||||||
import com.android.build.gradle.BasePlugin
|
|
||||||
import com.android.build.gradle.api.AndroidSourceSet
|
import com.android.build.gradle.api.AndroidSourceSet
|
||||||
import com.android.build.gradle.api.BaseVariant
|
import com.android.build.gradle.api.BaseVariant
|
||||||
import com.android.builder.model.SourceProvider
|
import com.android.builder.model.SourceProvider
|
||||||
@@ -805,7 +804,6 @@ class KotlinConfigurationTools internal constructor(
|
|||||||
abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTools: KotlinConfigurationTools) {
|
abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTools: KotlinConfigurationTools) {
|
||||||
protected val logger = Logging.getLogger(this.javaClass)
|
protected val logger = Logging.getLogger(this.javaClass)
|
||||||
|
|
||||||
abstract fun forEachVariant(project: Project, action: (BaseVariant) -> Unit): Unit
|
|
||||||
abstract fun getResDirectories(variantData: BaseVariant): FileCollection
|
abstract fun getResDirectories(variantData: BaseVariant): FileCollection
|
||||||
abstract fun getFlavorNames(variant: BaseVariant): List<String>
|
abstract fun getFlavorNames(variant: BaseVariant): List<String>
|
||||||
abstract fun getBuildTypeName(variant: BaseVariant): String
|
abstract fun getBuildTypeName(variant: BaseVariant): String
|
||||||
@@ -879,7 +877,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
|
|||||||
androidPluginIds.joinToString("\n\t") { "* $it" })
|
androidPluginIds.joinToString("\n\t") { "* $it" })
|
||||||
}
|
}
|
||||||
|
|
||||||
forEachVariant(project) { variant ->
|
project.forEachVariant { variant ->
|
||||||
val variantName = getVariantName(variant)
|
val variantName = getVariantName(variant)
|
||||||
|
|
||||||
// Create the compilation and configure it first, then add to the compilations container. As this code is executed
|
// Create the compilation and configure it first, then add to the compilations container. As this code is executed
|
||||||
@@ -899,7 +897,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
|
|||||||
}
|
}
|
||||||
|
|
||||||
project.whenEvaluated {
|
project.whenEvaluated {
|
||||||
forEachVariant(project) { variant ->
|
forEachVariant { variant ->
|
||||||
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
|
val compilation = kotlinAndroidTarget.compilations.getByName(getVariantName(variant))
|
||||||
postprocessVariant(variant, compilation, project, ext, plugin)
|
postprocessVariant(variant, compilation, project, ext, plugin)
|
||||||
|
|
||||||
@@ -966,7 +964,7 @@ abstract class AbstractAndroidProjectHandler(private val kotlinConfigurationTool
|
|||||||
if (kotlinAndroidTarget.disambiguationClassifier != null) {
|
if (kotlinAndroidTarget.disambiguationClassifier != null) {
|
||||||
|
|
||||||
val sourceSetToVariants = mutableMapOf<AndroidSourceSet, MutableList<BaseVariant>>().apply {
|
val sourceSetToVariants = mutableMapOf<AndroidSourceSet, MutableList<BaseVariant>>().apply {
|
||||||
forEachVariant(project) { variant ->
|
project.forEachVariant { variant ->
|
||||||
for (sourceSet in getSourceProviders(variant)) {
|
for (sourceSet in getSourceProviders(variant)) {
|
||||||
val androidSourceSet = sourceSet as? AndroidSourceSet ?: continue
|
val androidSourceSet = sourceSet as? AndroidSourceSet ?: continue
|
||||||
getOrPut(androidSourceSet) { mutableListOf() }.add(variant)
|
getOrPut(androidSourceSet) { mutableListOf() }.add(variant)
|
||||||
@@ -1243,3 +1241,21 @@ internal fun compareVersionNumbers(v1: String?, v2: String?): Int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Project.forEachVariant(action: (BaseVariant) -> Unit) {
|
||||||
|
val androidExtension = this.extensions.getByName("android")
|
||||||
|
when (androidExtension) {
|
||||||
|
is AppExtension -> androidExtension.applicationVariants.all(action)
|
||||||
|
is LibraryExtension -> {
|
||||||
|
androidExtension.libraryVariants.all(action)
|
||||||
|
if (androidExtension is FeatureExtension) {
|
||||||
|
androidExtension.featureVariants.all(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TestExtension -> androidExtension.applicationVariants.all(action)
|
||||||
|
}
|
||||||
|
if (androidExtension is TestedExtension) {
|
||||||
|
androidExtension.testVariants.all(action)
|
||||||
|
androidExtension.unitTestVariants.all(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-19
@@ -6,7 +6,8 @@
|
|||||||
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
|
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
|
||||||
package org.jetbrains.kotlin.gradle.plugin
|
package org.jetbrains.kotlin.gradle.plugin
|
||||||
|
|
||||||
import com.android.build.gradle.*
|
import com.android.build.gradle.BaseExtension
|
||||||
|
import com.android.build.gradle.BasePlugin
|
||||||
import com.android.build.gradle.api.*
|
import com.android.build.gradle.api.*
|
||||||
import com.android.build.gradle.tasks.MergeResources
|
import com.android.build.gradle.tasks.MergeResources
|
||||||
import com.android.builder.model.SourceProvider
|
import com.android.builder.model.SourceProvider
|
||||||
@@ -29,24 +30,6 @@ class Android25ProjectHandler(
|
|||||||
kotlinConfigurationTools: KotlinConfigurationTools
|
kotlinConfigurationTools: KotlinConfigurationTools
|
||||||
) : AbstractAndroidProjectHandler(kotlinConfigurationTools) {
|
) : AbstractAndroidProjectHandler(kotlinConfigurationTools) {
|
||||||
|
|
||||||
override fun forEachVariant(project: Project, action: (BaseVariant) -> Unit) {
|
|
||||||
val androidExtension = project.extensions.getByName("android")
|
|
||||||
when (androidExtension) {
|
|
||||||
is AppExtension -> androidExtension.applicationVariants.all(action)
|
|
||||||
is LibraryExtension -> {
|
|
||||||
androidExtension.libraryVariants.all(action)
|
|
||||||
if (androidExtension is FeatureExtension) {
|
|
||||||
androidExtension.featureVariants.all(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TestExtension -> androidExtension.applicationVariants.all(action)
|
|
||||||
}
|
|
||||||
if (androidExtension is TestedExtension) {
|
|
||||||
androidExtension.testVariants.all(action)
|
|
||||||
androidExtension.unitTestVariants.all(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun wireKotlinTasks(
|
override fun wireKotlinTasks(
|
||||||
project: Project,
|
project: Project,
|
||||||
compilation: KotlinJvmAndroidCompilation,
|
compilation: KotlinJvmAndroidCompilation,
|
||||||
|
|||||||
+2
-2
@@ -64,7 +64,7 @@ open class KotlinAndroidTarget(
|
|||||||
private fun checkPublishLibraryVariantsExist() {
|
private fun checkPublishLibraryVariantsExist() {
|
||||||
fun AbstractAndroidProjectHandler.getLibraryVariantNames() =
|
fun AbstractAndroidProjectHandler.getLibraryVariantNames() =
|
||||||
mutableSetOf<String>().apply {
|
mutableSetOf<String>().apply {
|
||||||
forEachVariant(project) {
|
project.forEachVariant {
|
||||||
if (getLibraryOutputTask(it) != null)
|
if (getLibraryOutputTask(it) != null)
|
||||||
add(getVariantName(it))
|
add(getVariantName(it))
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ open class KotlinAndroidTarget(
|
|||||||
|
|
||||||
private fun AbstractAndroidProjectHandler.doCreateComponents(): Set<KotlinTargetComponent> {
|
private fun AbstractAndroidProjectHandler.doCreateComponents(): Set<KotlinTargetComponent> {
|
||||||
val publishableVariants = mutableListOf<BaseVariant>()
|
val publishableVariants = mutableListOf<BaseVariant>()
|
||||||
.apply { forEachVariant(project) { add(it) } }
|
.apply { project.forEachVariant { add(it) } }
|
||||||
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
|
.toList() // Defensive copy against unlikely modification by the lambda that captures the list above in forEachVariant { }
|
||||||
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
|
.filter { getLibraryOutputTask(it) != null && publishLibraryVariants?.contains(getVariantName(it)) ?: true }
|
||||||
|
|
||||||
|
|||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.intellij.openapi.util.Version
|
||||||
|
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.ResolvedComponentResult
|
||||||
|
import org.gradle.api.file.FileSystemLocation
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.forEachVariant
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.reflect.full.findParameterByName
|
||||||
|
|
||||||
|
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<File>? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
object AndroidDependencyResolver {
|
||||||
|
private fun getAndroidSdkJar(project: Project): AndroidDependency? {
|
||||||
|
val androidExtension = project.extensions.findByName("android") as BaseExtension? ?: return null
|
||||||
|
val sdkLocation = getClassOrNull("com.android.build.gradle.internal.SdkLocator")?.let { sdkLocatorClass ->
|
||||||
|
val sdkLocation =
|
||||||
|
sdkLocatorClass.getMethodOrNull("getSdkLocation", File::class.java)?.invoke(sdkLocatorClass, project.rootDir)
|
||||||
|
sdkLocation?.javaClass?.getMethodOrNull("getDirectory")?.invoke(sdkLocation) as? File
|
||||||
|
} ?: return null
|
||||||
|
val sdkHandler = AndroidSdkHandler.getInstance(sdkLocation)
|
||||||
|
val logger = LoggerProgressIndicatorWrapper(LoggerWrapper(project.logger))
|
||||||
|
val androidTarget =
|
||||||
|
sdkHandler.getAndroidTargetManager(logger).getTargetFromHashString(androidExtension.compileSdkVersion, logger) ?: return null
|
||||||
|
return AndroidDependency(
|
||||||
|
androidTarget.fullName,
|
||||||
|
File(androidTarget.getPath(IAndroidTarget.ANDROID_JAR)),
|
||||||
|
File(androidTarget.getPath(IAndroidTarget.SOURCES))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 isAndroidPluginCompatible(): Boolean {
|
||||||
|
val version = getClassOrNull("com.android.Version")?.let {
|
||||||
|
try {
|
||||||
|
it.getField("ANDROID_GRADLE_PLUGIN_VERSION").get(null) as String
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} ?: return false
|
||||||
|
return Version.parseVersion(version)?.isOrGreaterThan(3, 6) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAndroidSourceSetDependencies(project: Project): Map<String, List<AndroidDependency>?> {
|
||||||
|
if (!isAndroidPluginCompatible()) return emptyMap()
|
||||||
|
|
||||||
|
data class SourceSetConfigs(val implConfig: Configuration, val compileConfig: Configuration)
|
||||||
|
|
||||||
|
val androidSdkJar = getAndroidSdkJar(project) ?: return emptyMap()
|
||||||
|
val sourceSet2Impl = HashMap<String, SourceSetConfigs>()
|
||||||
|
val allImplConfigs = HashSet<Configuration>()
|
||||||
|
|
||||||
|
project.forEachVariant { variant ->
|
||||||
|
val compileConfig = variant.compileConfiguration
|
||||||
|
variant.sourceSets.filterIsInstance(AndroidSourceSet::class.java).map {
|
||||||
|
val implConfig = project.configurations.getByName(it.implementationConfigurationName)
|
||||||
|
allImplConfigs.add(implConfig)
|
||||||
|
sourceSet2Impl[lowerCamelCaseName("android", it.name)] = SourceSetConfigs(implConfig, compileConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceSet2Impl.mapValues { entry ->
|
||||||
|
val dependencies = findDependencies(allImplConfigs, entry.value.implConfig)
|
||||||
|
|
||||||
|
val selfResolved = dependencies.filterIsInstance<SelfResolvingDependency>().map { AndroidDependency(collection = it.resolve()) }
|
||||||
|
val resolvedExternal = dependencies.filterIsInstance<ExternalModuleDependency>()
|
||||||
|
.flatMap { collectDependencies(it.module, entry.value.compileConfig) }
|
||||||
|
|
||||||
|
val result = (selfResolved + resolvedExternal + androidSdkJar).toMutableList()
|
||||||
|
|
||||||
|
if (entry.key == "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")
|
||||||
|
getClassOrNull("com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask")?.let { linkAppClass ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val rClassOutputJar =
|
||||||
|
linkAppClass.getMethodOrNull("getRClassOutputJar")?.invoke(task) as Provider<FileSystemLocation>?
|
||||||
|
rClassOutputJar?.orNull?.asFile?.let { result += AndroidDependency("R.jar", it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
private fun collectDependencies(
|
||||||
|
module: ModuleIdentifier,
|
||||||
|
compileClasspathConf: Configuration
|
||||||
|
): List<AndroidDependency> {
|
||||||
|
val viewConfig: (ArtifactView.ViewConfiguration) -> Unit = { config ->
|
||||||
|
config.attributes { it.attribute(AndroidArtifacts.ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.JAR.type) }
|
||||||
|
config.isLenient = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolvedArtifacts = compileClasspathConf.incoming.artifactView(viewConfig).artifacts.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 = compileClasspathConf.incoming.resolutionResult.allComponents.mapNotNull {
|
||||||
|
val id = it.moduleVersion?.module ?: return@mapNotNull null
|
||||||
|
id to it
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
val deps = HashSet<ModuleIdentifier>().also { doCollectDependencies(listOf(module), resolutionResults, it) }
|
||||||
|
return deps.mapNotNull { resolvedArtifacts[it] }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
private tailrec fun doCollectDependencies(
|
||||||
|
modules: List<ModuleIdentifier>,
|
||||||
|
resolutionResults: Map<ModuleIdentifier, ResolvedComponentResult>,
|
||||||
|
result: MutableSet<ModuleIdentifier>
|
||||||
|
) {
|
||||||
|
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<Configuration>, conf: Configuration): Set<Dependency> {
|
||||||
|
return HashSet<Dependency>().also { doFindDependencies(implConfigs, listOf(conf), it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private tailrec fun doFindDependencies(
|
||||||
|
implConfigs: Set<Configuration>, configs: List<Configuration>, result: MutableSet<Dependency>,
|
||||||
|
visited: MutableSet<Configuration> = HashSet()
|
||||||
|
) {
|
||||||
|
if (configs.isEmpty()) return
|
||||||
|
result.addAll(configs.flatMap { it.dependencies })
|
||||||
|
doFindDependencies(implConfigs, configs.flatMap { it.extendsFrom }.filter { it !in implConfigs && visited.add(it) }, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -208,7 +208,7 @@ class AndroidSubplugin : KotlinGradleSubplugin<KotlinCompile> {
|
|||||||
|
|
||||||
val resDirectoriesForAllVariants = mutableListOf<FileCollection>()
|
val resDirectoriesForAllVariants = mutableListOf<FileCollection>()
|
||||||
|
|
||||||
androidProjectHandler.forEachVariant(project) { variant ->
|
project.forEachVariant { variant ->
|
||||||
if (getTestedVariantData(variant) != null) return@forEachVariant
|
if (getTestedVariantData(variant) != null) return@forEachVariant
|
||||||
resDirectoriesForAllVariants += androidProjectHandler.getResDirectories(variant)
|
resDirectoriesForAllVariants += androidProjectHandler.getResDirectories(variant)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user