Re-structure KotlinMPPGradleProjectResolver.populateModuleDependencies

This commit is contained in:
Yaroslav Chernyshev
2021-03-11 22:30:12 +03:00
parent 18a06dc37e
commit 960a7dca45
20 changed files with 1445 additions and 384 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.configuration
import com.google.common.graph.GraphBuilder
import com.google.common.graph.Graphs
import com.intellij.build.events.MessageEvent
import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.externalSystem.model.DataNode
@@ -39,10 +40,17 @@ import org.jetbrains.kotlin.config.ExternalSystemRunTask
import org.jetbrains.kotlin.config.ExternalSystemTestRunTask
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.PlatformVersion
import org.jetbrains.kotlin.idea.configuration.GradlePropertiesFileFacade.Companion.KOTLIN_NOT_IMPORTED_COMMON_SOURCE_SETS_SETTING
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibrariesDependencySubstitutor
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibrariesFixer
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibraryNameUtil.KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE
import org.jetbrains.kotlin.idea.configuration.mpp.createPopulateModuleDependenciesContext
import org.jetbrains.kotlin.idea.configuration.mpp.getCompilations
import org.jetbrains.kotlin.idea.configuration.mpp.populateModuleDependenciesByCompilations
import org.jetbrains.kotlin.idea.configuration.mpp.populateModuleDependenciesBySourceSetVisibilityGraph
import org.jetbrains.kotlin.idea.configuration.utils.UnsafeTestSourceSetHeuristicApi
import org.jetbrains.kotlin.idea.configuration.utils.predictedProductionSourceSetName
import org.jetbrains.kotlin.idea.platform.IdePlatformKindTooling
import org.jetbrains.kotlin.idea.util.NotNullableCopyableDataNodeUserDataProperty
import org.jetbrains.kotlin.util.removeSuffixIfPresent
@@ -94,7 +102,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
override fun populateModuleContentRoots(gradleModule: IdeaModule, ideModule: DataNode<ModuleData>) {
val mppModel = resolverCtx.getExtraProject(gradleModule, KotlinMPPGradleModel::class.java)
val mppModel = resolverCtx.getMppModel(gradleModule)
if (mppModel == null) {
return super.populateModuleContentRoots(gradleModule, ideModule)
} else {
@@ -117,7 +125,9 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
val projectDataNode = ideModule.getDataNode(ProjectKeys.PROJECT)!!
val moduleOutputsMap = projectDataNode.getUserData(MODULES_OUTPUTS)!!
val outputDirs = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
getCompilations(gradleModule, mppModel, ideModule, resolverCtx)
.filterNot { (_, compilation) -> delegateToAndroidPlugin(compilation) }
.forEach { (dataNode, compilation) ->
var gradleOutputMap = dataNode.getUserData(GradleProjectResolver.GRADLE_OUTPUTS)
if (gradleOutputMap == null) {
gradleOutputMap = MultiMap.create()
@@ -228,7 +238,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
}
private fun Collection<ExternalDependency>.modifyDependenciesOnMppModules(
internal fun Collection<ExternalDependency>.modifyDependenciesOnMppModules(
ideProject: DataNode<ProjectData>,
resolverCtx: ProjectResolverContext
) {
@@ -289,8 +299,8 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
val path = toCanonicalPath(target.jar!!.archiveFile!!.absolutePath)
val currentModules = userData[path] ?: ArrayList<String>().apply { userData[path] = this }
// Test modules should not be added. Otherwise we could get dependnecy of java.mail on jvmTest
val allSourceSets = target.compilations.filter { !it.isTestModule }.flatMap { it.allSourceSets }.toSet()
val availableViaDependsOn = allSourceSets.flatMap { it.dependsOnSourceSets }.mapNotNull { mppModel.sourceSets[it] }
val allSourceSets = target.compilations.filter { !it.isTestModule }.flatMap { it.declaredSourceSets }.toSet()
val availableViaDependsOn = allSourceSets.flatMap { it.allDependsOnSourceSets }.mapNotNull { mppModel.sourceSetsByName[it] }
allSourceSets.union(availableViaDependsOn).forEach { sourceSet ->
currentModules.add(getKotlinModuleId(gradleModule, sourceSet, resolverCtx))
}
@@ -362,7 +372,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
resolverCtx
) ?: continue
kotlinSourceSet.externalSystemRunTasks =
compilation.allSourceSets.firstNotNullResult { sourceSetToRunTasks[it] } ?: emptyList()
compilation.declaredSourceSets.firstNotNullResult { sourceSetToRunTasks[it] } ?: emptyList()
if (compilation.platform == KotlinPlatform.JVM || compilation.platform == KotlinPlatform.ANDROID) {
compilationData.targetCompatibility = (kotlinSourceSet.compilerArguments as? K2JVMCompilerArguments)?.jvmTarget
@@ -374,9 +384,9 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
}
for (sourceSet in compilation.allSourceSets) {
for (sourceSet in compilation.declaredSourceSets) {
sourceSetToCompilationData.getOrPut(sourceSet.name) { LinkedHashSet() } += compilationData
for (dependentSourceSetName in sourceSet.dependsOnSourceSets) {
for (dependentSourceSetName in sourceSet.allDependsOnSourceSets) {
sourceSetToCompilationData.getOrPut(dependentSourceSetName) { LinkedHashSet() } += compilationData
}
}
@@ -394,9 +404,9 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
for (sourceSet in mppModel.sourceSets.values) {
for (sourceSet in mppModel.sourceSetsByName.values) {
if (delegateToAndroidPlugin(sourceSet)) continue
if (sourceSet.actualPlatforms.supports(KotlinPlatform.COMMON) && ignoreCommonSourceSets) continue
if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.COMMON && ignoreCommonSourceSets) continue
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
if (existingSourceSetDataNode?.kotlinSourceSet != null) continue
@@ -410,15 +420,14 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
it.group = externalProject.group
it.version = externalProject.version
val name = sourceSet.name
val baseName = name.removeSuffix("Test")
if (baseName != name) {
if (sourceSet.isTestModule) {
it.productionModuleId = getInternalModuleName(
gradleModule,
externalProject,
sourceSet,
resolverCtx,
baseName + "Main"
@OptIn(UnsafeTestSourceSetHeuristicApi::class)
predictedProductionSourceSetName(sourceSet.name)
)
}
@@ -430,7 +439,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
.mapNotNull { compilationData -> compilationData.targetCompatibility }
.minWithOrNull(VersionComparatorUtil.COMPARATOR)
if (sourceSet.actualPlatforms.getSinglePlatform() == KotlinPlatform.NATIVE) {
if (sourceSet.actualPlatforms.singleOrNull() == KotlinPlatform.NATIVE) {
it.konanTargets = compilationDataRecords
.flatMap { compilationData -> compilationData.konanTargets }
.toSet()
@@ -438,7 +447,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
}
}
val kotlinSourceSet = createSourceSetInfo(sourceSet, gradleModule, resolverCtx) ?: continue
val kotlinSourceSet = createSourceSetInfo(mppModel, sourceSet, gradleModule, resolverCtx) ?: continue
kotlinSourceSet.externalSystemRunTasks = sourceSetToRunTasks[sourceSet] ?: emptyList()
val sourceSetDataNode =
@@ -495,15 +504,15 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
)
}
val allRunTasks = testRunTasks + nativeMainRunTasks
compilation.allSourceSets.forEach { sourceSet ->
compilation.declaredSourceSets.forEach { sourceSet ->
sourceSetToRunTasks.getOrPut(sourceSet) { LinkedHashSet() } += allRunTasks
sourceSet.dependsOnSourceSets.forEach { dependentModule ->
dependsOnReverseGraph.getOrPut(dependentModule) { LinkedHashSet() } += sourceSet
mppModel.resolveAllDependsOnSourceSets(sourceSet).forEach { dependentModule ->
dependsOnReverseGraph.getOrPut(dependentModule.name) { LinkedHashSet() } += sourceSet
}
}
}
}
mppModel.sourceSets.forEach { (sourceSetName, sourceSet) ->
mppModel.sourceSetsByName.forEach { (sourceSetName, sourceSet) ->
dependsOnReverseGraph[sourceSetName]?.forEach { dependingSourceSet ->
sourceSetToRunTasks.getOrPut(sourceSet) { LinkedHashSet() } += sourceSetToRunTasks[dependingSourceSet] ?: emptyList()
}
@@ -519,7 +528,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
val mppModel = resolverCtx.getMppModel(gradleModule) ?: return
val sourceSetToPackagePrefix = mppModel.targets.flatMap { it.compilations }
.flatMap { compilation ->
compilation.allSourceSets.map { sourceSet -> sourceSet.name to compilation.kotlinTaskProperties.packagePrefix }
compilation.declaredSourceSets.map { sourceSet -> sourceSet.name to compilation.kotlinTaskProperties.packagePrefix }
}
.toMap()
if (resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java) == null) return
@@ -563,7 +572,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
mppModelPureKotlinSourceFolders + ideModule.pureKotlinSourceFolders
}
private data class CompilationWithDependencies(
internal data class CompilationWithDependencies(
val compilation: KotlinCompilation,
val substitutedDependencies: List<ExternalDependency>
) {
@@ -581,275 +590,17 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
) {
val mppModel = resolverCtx.getMppModel(gradleModule) ?: return
mppModel.dependencyMap.values.modifyDependenciesOnMppModules(ideProject, resolverCtx)
val sourceSetMap = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return
val artifactsMap = ideProject.getUserData(CONFIGURATION_ARTIFACTS) ?: return
val substitutor = KotlinNativeLibrariesDependencySubstitutor(mppModel, gradleModule, resolverCtx)
val sourceSetToCompilations = mutableMapOf<String, MutableList<CompilationWithDependencies>>()
val processedModuleIds = HashSet<String>()
processCompilations(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, compilation ->
if (processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx))) {
val substitutedDependencies =
substitutor.substituteDependencies(compilation.dependencies.mapNotNull { mppModel.dependencyMap[it] })
buildDependencies(
resolverCtx,
sourceSetMap,
artifactsMap,
dataNode,
preprocessDependencies(substitutedDependencies),
ideProject
)
KotlinNativeLibrariesFixer.applyTo(dataNode, ideProject)
for (sourceSet in compilation.allSourceSets) {
(sourceSet.dependsOnSourceSets + sourceSet.name).forEach {
sourceSetToCompilations
.getOrPut(it) { mutableListOf() }
.add(CompilationWithDependencies(compilation, substitutedDependencies))
}
if (sourceSet.fullName() == compilation.fullName()) continue
val targetDataNode = getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx) ?: continue
addDependency(dataNode, targetDataNode, sourceSet.isTestModule)
}
}
}
val sourceSetGraph = GraphBuilder.directed().build<KotlinSourceSet>()
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
sourceSetGraph.addNode(sourceSet)
val productionSourceSet = dataNode
?.data
?.productionModuleId
?.let { ideModule.findChildModuleByInternalName(it) }
?.kotlinSourceSet
?.kotlinModule
?.toSourceSet(mppModel)
if (productionSourceSet != null) {
sourceSetGraph.putEdge(sourceSet, productionSourceSet)
}
for (targetSourceSetName in sourceSet.dependsOnSourceSets) {
val targetSourceSet = mppModel.sourceSets[targetSourceSetName] ?: continue
sourceSetGraph.putEdge(sourceSet, targetSourceSet)
}
// Workaround: Non-android source sets have commonMain/commonTest in their dependsOn
// Remove when the same is implemented for Android modules as well
if (sourceSet.actualPlatforms.supports(KotlinPlatform.ANDROID)) {
val commonSourceSetName = if (sourceSet.isTestModule) {
KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME
} else {
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
}
val commonSourceSet = mppModel.sourceSets[commonSourceSetName]
if (commonSourceSet != null && commonSourceSet != sourceSet) {
sourceSetGraph.putEdge(sourceSet, commonSourceSet)
}
}
}
val closedSourceSetGraph = Graphs.transitiveClosure(sourceSetGraph)
for (sourceSet in closedSourceSetGraph.nodes()) {
val isAndroid = delegateToAndroidPlugin(sourceSet)
val fromDataNode = if (isAndroid) {
ideModule
} else {
getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx)
} ?: continue
val dependeeSourceSets = closedSourceSetGraph.successors(sourceSet)
val sourceSetInfos = if (isAndroid) {
ideModule.kotlinAndroidSourceSets?.filter {
(it.kotlinModule as? KotlinCompilation)?.allSourceSets?.contains(sourceSet) ?: false
} ?: emptyList()
} else {
listOfNotNull(fromDataNode.kotlinSourceSet)
}
for (sourceSetInfo in sourceSetInfos) {
if (sourceSetInfo.kotlinModule is KotlinCompilation) {
val selfName = sourceSetInfo.kotlinModule.fullName()
sourceSetInfo.addSourceSets(dependeeSourceSets, selfName, gradleModule, resolverCtx)
}
}
if (delegateToAndroidPlugin(sourceSet)) continue
for (dependeeSourceSet in dependeeSourceSets) {
val toDataNode = getSiblingKotlinModuleData(dependeeSourceSet, gradleModule, ideModule, resolverCtx) ?: continue
addDependency(fromDataNode, toDataNode, dependeeSourceSet.isTestModule)
}
if (processedModuleIds.add(getKotlinModuleId(gradleModule, sourceSet, resolverCtx))) {
val mergedSubstitutedDependencies = LinkedHashSet<KotlinDependency>().apply {
val forceNativeDependencyPropagation: Boolean
val excludeInheritedNativeDependencies: Boolean
if (mppModel.extraFeatures.isHMPPEnabled && sourceSet.actualPlatforms.getSinglePlatform() == KotlinPlatform.NATIVE) {
forceNativeDependencyPropagation = mppModel.extraFeatures.isNativeDependencyPropagationEnabled
excludeInheritedNativeDependencies = !forceNativeDependencyPropagation
} else {
forceNativeDependencyPropagation = false
excludeInheritedNativeDependencies = false
}
addAll(substitutor.substituteDependencies(sourceSet))
dependeeSourceSets.flatMapTo(this) { dependeeSourceSet ->
substitutor.substituteDependencies(dependeeSourceSet).run {
if (excludeInheritedNativeDependencies)
filter { !it.name.startsWith(KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE) }
else this
}
}
if (forceNativeDependencyPropagation) {
sourceSetToCompilations[sourceSet.name]?.let { compilations ->
addAll(propagatedNativeDependencies(compilations))
}
}
if (mppModel.extraFeatures.isHMPPEnabled) {
addAll(propagatedPlatformDependencies(mppModel, sourceSet))
}
}
buildDependencies(
resolverCtx,
sourceSetMap,
artifactsMap,
fromDataNode,
preprocessDependencies(mergedSubstitutedDependencies),
ideProject
)
@Suppress("UNCHECKED_CAST")
KotlinNativeLibrariesFixer.applyTo(fromDataNode as DataNode<GradleSourceSetData>, ideProject)
}
}
val context = createPopulateModuleDependenciesContext(
gradleModule = gradleModule,
ideProject = ideProject,
ideModule = ideModule,
resolverCtx = resolverCtx
) ?: return
populateModuleDependenciesByCompilations(context)
populateModuleDependenciesBySourceSetVisibilityGraph(context)
}
private fun KotlinNativeLibrariesDependencySubstitutor.substituteDependencies(kotlinModule: KotlinModule): List<ExternalDependency> =
substituteDependencies(kotlinModule.dependencies.mapNotNull { mppModel.dependencyMap[it] })
// We can't really commonize native platform libraries yet.
// But APIs for different targets may be very similar.
// E.g. ios_arm64 and ios_x64 have almost identical platform libraries.
// We handle these special cases and resolve common sources for such
// targets against libraries of one of them. E.g. common sources for
// ios_x64 and ios_arm64 will be resolved against ios_arm64 libraries.
//
// Currently such special casing is available for Apple platforms
// (iOS, watchOS and tvOS) and native Android (ARM, X86).
// TODO: Do we need to support user's interop libraries too?
private fun propagatedNativeDependencies(compilations: List<CompilationWithDependencies>): List<ExternalDependency> {
if (compilations.size <= 1) {
return emptyList()
}
val copyFrom = when {
compilations.all { it.isAppleCompilation } ->
compilations.selectFirstAvailableTarget(
"watchos_arm64", "watchos_arm32", "watchos_x64", "watchos_x86",
"ios_arm64", "ios_arm32", "ios_x64",
"tvos_arm64", "tvos_x64"
)
compilations.all { it.konanTarget?.startsWith("android") == true } ->
compilations.selectFirstAvailableTarget(
"android_arm64", "android_arm32", "android_x64", "android_x86"
)
else -> return emptyList()
}
return copyFrom.dependencyNames.mapNotNull { (name, dependency) ->
when {
!name.startsWith(KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE) -> null // Support only default platform libs for now.
compilations.all { it.dependencyNames.containsKey(name) } -> dependency
else -> null
}
}
}
/**
* Source sets sharing code between JVM and Android are the only intermediate source sets that
* can effectively consume a dependency's platform artifact.
* When a library only offers a JVM variant, then Android and JVM consume this variant of the library.
*
* This will be replaced later on by [KT-43450](https://youtrack.jetbrains.com/issue/KT-43450)
*
* @return all dependencies being present across given JVM and Android compilations that this [sourceSet] can also participates in.
*/
private fun propagatedPlatformDependencies(
mppModel: KotlinMPPGradleModel,
sourceSet: KotlinSourceSet,
): Set<ExternalDependency> {
if (
sourceSet.actualPlatforms.platforms.sorted() == listOf(KotlinPlatform.JVM, KotlinPlatform.ANDROID).sorted()
) {
return mppModel.targets
.filter { target -> target.platform == KotlinPlatform.JVM || target.platform == KotlinPlatform.ANDROID }
.flatMap { target -> target.compilations }
.filter { compilation -> compilation.dependsOnSourceSet(mppModel, sourceSet) }
.map { targetCompilations -> targetCompilations.dependencies.mapNotNull(mppModel.dependencyMap::get).toSet() }
.reduceOrNull { acc, dependencies -> acc.intersect(dependencies) }.orEmpty()
}
return emptySet()
}
private fun KotlinCompilation.dependsOnSourceSet(mppModel: KotlinMPPGradleModel, sourceSet: KotlinSourceSet): Boolean {
return allSourceSets.any { containedSourceSet -> sourceSet.isOrDependsOnSourceSet(mppModel, containedSourceSet) }
}
private fun KotlinSourceSet.isOrDependsOnSourceSet(mppModel: KotlinMPPGradleModel, sourceSet: KotlinSourceSet): Boolean {
if (this == sourceSet) return true
return this.dependsOnSourceSets
.map { dependencySourceSetName -> mppModel.sourceSets.getValue(dependencySourceSetName) }
.any { dependencySourceSet -> dependencySourceSet.isOrDependsOnSourceSet(mppModel, sourceSet) }
}
private val CompilationWithDependencies.isAppleCompilation: Boolean
get() = konanTarget?.let {
it.startsWith("ios") || it.startsWith("watchos") || it.startsWith("tvos")
} ?: false
private fun Iterable<CompilationWithDependencies>.selectFirstAvailableTarget(@NonNls vararg targetsByPriority: String): CompilationWithDependencies {
for (target in targetsByPriority) {
val result = firstOrNull { it.konanTarget == target }
if (result != null) {
return result
}
}
return first()
}
private fun KotlinModule.toSourceSet(mppModel: KotlinMPPGradleModel) = when (this) {
is KotlinSourceSet -> this
is KotlinCompilation -> mppModel.sourceSets[fullName()]
else -> null
}
private fun preprocessDependencies(dependencies: Collection<KotlinDependency>): List<ExternalDependency> {
return dependencies
.groupBy { it.id }
.mapValues { it.value.firstOrNull { it.scope == "COMPILE" } ?: it.value.lastOrNull() }
.values
.filterNotNull()
}
private fun addDependency(fromModule: DataNode<*>, toModule: DataNode<*>, dependOnTestModule: Boolean) {
if (fromModule.data == toModule.data) return
val fromData = fromModule.data as? ModuleData ?: return
val toData = toModule.data as? ModuleData ?: return
val existing = fromModule.children.mapNotNull { it.data as? ModuleDependencyData }
.filter { it.target.id == (toModule.data as? ModuleData)?.id }
val nodeToModify =
existing.singleOrNull() ?: existing.firstOrNull { it.scope == DependencyScope.COMPILE } ?: existing.firstOrNull()
if (nodeToModify != null) {
nodeToModify.scope = DependencyScope.COMPILE
nodeToModify.isProductionOnTestDependency = nodeToModify.isProductionOnTestDependency || dependOnTestModule
return
}
val moduleDependencyData = ModuleDependencyData(fromData, toData).also {
it.scope = DependencyScope.COMPILE
it.isExported = false
it.isProductionOnTestDependency = dependOnTestModule
}
fromModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData)
}
private fun getSiblingKotlinModuleData(
internal fun getSiblingKotlinModuleData(
kotlinModule: KotlinModule,
gradleModule: IdeaModule,
ideModule: DataNode<ModuleData>,
@@ -885,36 +636,13 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
sourceSetsMap[dataNode.data.id] = dataNode
}
}
for (sourceSet in mppModel.sourceSets.values) {
for (sourceSet in mppModel.sourceSetsByName.values) {
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
val moduleDataNode = sourceSetsMap[moduleId]
processor(moduleDataNode, sourceSet)
}
}
private fun processCompilations(
gradleModule: IdeaModule,
mppModel: KotlinMPPGradleModel,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext,
processor: (DataNode<GradleSourceSetData>, KotlinCompilation) -> Unit
) {
val sourceSetsMap = HashMap<String, DataNode<GradleSourceSetData>>()
for (dataNode in ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
if (dataNode.kotlinSourceSet != null) {
sourceSetsMap[dataNode.data.id] = dataNode
}
}
for (target in mppModel.targets) {
if (delegateToAndroidPlugin(target)) continue
for (compilation in target.compilations) {
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
processor(moduleDataNode, compilation)
}
}
}
private val IdeaModule.jdkNameIfAny
get() = try {
jdkName
@@ -988,7 +716,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
if (effectiveClassesDir != null) {
sourcesWithTypes += compilation.sourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.outputDir = effectiveClassesDir
dirSet.srcDirs = compilation.allSourceSets.flatMapTo(LinkedHashSet()) { it.sourceDirs }
dirSet.srcDirs = compilation.declaredSourceSets.flatMapTo(LinkedHashSet()) { it.sourceDirs }
dirSet.gradleOutputDirs += compilation.output.classesDirs
dirSet.setInheritedCompilerOutput(false)
}
@@ -996,7 +724,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
if (resourcesDir != null) {
sourcesWithTypes += compilation.resourceType to DefaultExternalSourceDirectorySet().also { dirSet ->
dirSet.outputDir = resourcesDir
dirSet.srcDirs = compilation.allSourceSets.flatMapTo(LinkedHashSet()) { it.resourceDirs }
dirSet.srcDirs = compilation.declaredSourceSets.flatMapTo(LinkedHashSet()) { it.resourceDirs }
dirSet.gradleOutputDirs += resourcesDir
dirSet.setInheritedCompilerOutput(false)
}
@@ -1034,7 +762,9 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
private val KotlinModule.resourceType
get() = if (isTestModule) ExternalSystemSourceType.TEST_RESOURCE else ExternalSystemSourceType.RESOURCE
@OptIn(ExperimentalGradleToolingApi::class)
private fun createSourceSetInfo(
mppModel: KotlinMPPGradleModel,
sourceSet: KotlinSourceSet,
gradleModule: IdeaModule,
resolverCtx: ProjectResolverContext
@@ -1044,13 +774,15 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
val languageSettings = sourceSet.languageSettings
info.moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
info.gradleModuleId = getModuleId(resolverCtx, gradleModule)
info.actualPlatforms.addSimplePlatforms(sourceSet.actualPlatforms.platforms)
info.actualPlatforms.pushPlatforms(sourceSet.actualPlatforms)
info.isTestModule = sourceSet.isTestModule
info.dependsOn = sourceSet.dependsOnSourceSets.toList().map {
getGradleModuleQualifiedName(resolverCtx, gradleModule, it)
info.dependsOn = mppModel.resolveAllDependsOnSourceSets(sourceSet).map { dependsOnSourceSet ->
getGradleModuleQualifiedName(resolverCtx, gradleModule, dependsOnSourceSet.name)
}
//TODO(auskov): target flours are lost here
info.compilerArguments = createCompilerArguments(emptyList(), sourceSet.actualPlatforms.getSinglePlatform()).also {
info.compilerArguments = createCompilerArguments(
emptyList(), sourceSet.actualPlatforms.singleOrNull() ?: KotlinPlatform.COMMON
).also {
it.multiPlatform = true
it.languageVersion = languageSettings.languageVersion
it.apiVersion = languageSettings.apiVersion
@@ -1086,9 +818,9 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
return KotlinSourceSetInfo(compilation).also { sourceSetInfo ->
sourceSetInfo.moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
sourceSetInfo.gradleModuleId = getModuleId(resolverCtx, gradleModule)
sourceSetInfo.actualPlatforms.addSimplePlatforms(listOf(compilation.platform))
sourceSetInfo.actualPlatforms.pushPlatforms(listOf(compilation.platform))
sourceSetInfo.isTestModule = compilation.isTestModule
sourceSetInfo.dependsOn = compilation.allSourceSets.flatMap { it.dependsOnSourceSets }.map {
sourceSetInfo.dependsOn = compilation.declaredSourceSets.flatMap { it.allDependsOnSourceSets }.map {
getGradleModuleQualifiedName(resolverCtx, gradleModule, it)
}.distinct().toList()
sourceSetInfo.compilerArguments =
@@ -1105,7 +837,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
/** Checks if our IDE doesn't support such platform */
private fun KotlinPlatform.isNotSupported() = IdePlatformKindTooling.getToolingIfAny(this) == null
private fun KotlinSourceSetInfo.addSourceSets(
internal fun KotlinSourceSetInfo.addSourceSets(
sourceSets: Collection<KotlinModule>,
selfName: String,
gradleModule: IdeaModule,
@@ -1131,7 +863,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
private fun getKotlinModuleId(gradleModule: IdeaModule, kotlinModule: KotlinModule, resolverCtx: ProjectResolverContext) =
getGradleModuleQualifiedName(resolverCtx, gradleModule, kotlinModule.fullName())
private fun getGradleModuleQualifiedName(
internal fun getGradleModuleQualifiedName(
resolverCtx: ProjectResolverContext,
gradleModule: IdeaModule,
simpleName: String
@@ -1144,10 +876,13 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtensionComp
ignoreCase = true
) ?: false
private fun delegateToAndroidPlugin(kotlinTarget: KotlinTarget): Boolean =
internal fun delegateToAndroidPlugin(compilation: KotlinCompilation): Boolean =
androidPluginPresent && compilation.platform == KotlinPlatform.ANDROID
internal fun delegateToAndroidPlugin(kotlinTarget: KotlinTarget): Boolean =
androidPluginPresent && kotlinTarget.platform == KotlinPlatform.ANDROID
private fun delegateToAndroidPlugin(kotlinSourceSet: KotlinSourceSet): Boolean =
internal fun delegateToAndroidPlugin(kotlinSourceSet: KotlinSourceSet): Boolean =
androidPluginPresent && kotlinSourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID
}
}
@@ -9,9 +9,11 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.util.Key
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.KotlinDependency
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
import org.jetbrains.kotlin.idea.configuration.buildClasspathData
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibraryNameUtil.buildIDELibraryName
import org.jetbrains.kotlin.idea.configuration.mpp.KotlinDependenciesPreprocessor
import org.jetbrains.kotlin.idea.inspections.gradle.findKotlinPluginVersion
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.plugins.gradle.ExternalDependencyId
@@ -21,10 +23,15 @@ import java.io.File
// KT-29613, KT-29783
internal class KotlinNativeLibrariesDependencySubstitutor(
val mppModel: KotlinMPPGradleModel,
private val mppModel: KotlinMPPGradleModel,
private val gradleModule: IdeaModule,
private val resolverCtx: ProjectResolverContext
) {
) : KotlinDependenciesPreprocessor {
override fun invoke(dependencies: Iterable<KotlinDependency>): List<KotlinDependency> {
return substituteDependencies(dependencies.toList())
}
// Substitutes `ExternalDependency` entries that represent KLIBs with new dependency entries with proper type and name:
// - every `FileCollectionDependency` is checked whether it points to an existing KLIB, and substituted if it is
// - similarly for every `ExternalLibraryDependency` with `groupId == "Kotlin/Native"` (legacy KLIB provided by Gradle plugin <= 1.3.20)
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import org.jetbrains.kotlin.gradle.KotlinDependency
internal object DistinctIdKotlinDependenciesPreprocessor : KotlinDependenciesPreprocessor {
override fun invoke(dependencies: Iterable<KotlinDependency>): List<KotlinDependency> {
return dependencies
.groupBy { dependency -> dependency.id }
.mapValues { (_, dependenciesWithSameId) ->
dependenciesWithSameId.firstOrNull { it.scope == "COMPILE" } ?: dependenciesWithSameId.lastOrNull()
}
.values
.filterNotNull()
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import org.jetbrains.kotlin.gradle.KotlinDependency
internal interface KotlinDependenciesPreprocessor {
operator fun invoke(dependencies: Iterable<KotlinDependency>): List<KotlinDependency>
}
internal operator fun KotlinDependenciesPreprocessor.plus(
other: KotlinDependenciesPreprocessor
): KotlinDependenciesPreprocessor {
return SequentialKotlinDependenciesPreprocessor(this, other)
}
private class SequentialKotlinDependenciesPreprocessor(
private val first: KotlinDependenciesPreprocessor,
private val second: KotlinDependenciesPreprocessor
) : KotlinDependenciesPreprocessor {
override fun invoke(dependencies: Iterable<KotlinDependency>): List<KotlinDependency> {
return second(first(dependencies))
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.model.project.ProjectData
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver.Companion.CompilationWithDependencies
import org.jetbrains.kotlin.idea.configuration.getMppModel
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibrariesDependencySubstitutor
import org.jetbrains.plugins.gradle.model.ExternalSourceSet
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolver
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
import com.intellij.openapi.util.Pair as IntelliJPair
internal typealias ModuleId = String
internal typealias ArtifactPath = String
internal data class PopulateModuleDependenciesContext(
val resolverCtx: ProjectResolverContext,
val mppModel: KotlinMPPGradleModel,
val gradleModule: IdeaModule,
val ideProject: DataNode<ProjectData>,
val ideModule: DataNode<ModuleData>,
val dependenciesPreprocessor: KotlinDependenciesPreprocessor,
val sourceSetMap: Map<ModuleId, IntelliJPair<DataNode<GradleSourceSetData>, ExternalSourceSet>>,
val artifactsMap: Map<ArtifactPath, ModuleId>,
val processedModuleIds: MutableSet<ModuleId> = mutableSetOf()
)
internal fun KotlinMPPGradleProjectResolver.Companion.createPopulateModuleDependenciesContext(
gradleModule: IdeaModule,
ideProject: DataNode<ProjectData>,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
): PopulateModuleDependenciesContext? {
val mppModel = resolverCtx.getMppModel(gradleModule) ?: return null
mppModel.dependencyMap.values.modifyDependenciesOnMppModules(ideProject, resolverCtx)
val sourceSetMap = ideProject.getUserData(GradleProjectResolver.RESOLVED_SOURCE_SETS) ?: return null
val artifactsMap = ideProject.getUserData(GradleProjectResolver.CONFIGURATION_ARTIFACTS) ?: return null
val dependenciesPreprocessor = KotlinNativeLibrariesDependencySubstitutor(mppModel, gradleModule, resolverCtx)
.plus(DistinctIdKotlinDependenciesPreprocessor)
return PopulateModuleDependenciesContext(
resolverCtx = resolverCtx,
mppModel = mppModel,
gradleModule = gradleModule,
ideProject = ideProject,
ideModule = ideModule,
dependenciesPreprocessor = dependenciesPreprocessor,
sourceSetMap = sourceSetMap,
artifactsMap = artifactsMap,
)
}
internal fun PopulateModuleDependenciesContext.getDependencies(module: KotlinModule): List<KotlinDependency> {
return dependenciesPreprocessor(module.dependencies.mapNotNull { id -> mppModel.dependencyMap[id] })
}
internal fun PopulateModuleDependenciesContext.getCompilationsWithDependencies(
sourceSet: KotlinSourceSet
): List<CompilationWithDependencies> {
return mppModel.getCompilations(sourceSet).map { compilation -> CompilationWithDependencies(compilation, getDependencies(compilation)) }
}
internal fun KotlinCompilation.dependsOnSourceSet(mppModel: KotlinMPPGradleModel, sourceSet: KotlinSourceSet): Boolean {
return declaredSourceSets.any { containedSourceSet -> sourceSet.isOrDependsOnSourceSet(mppModel, containedSourceSet) }
}
internal fun KotlinSourceSet.isOrDependsOnSourceSet(mppModel: KotlinMPPGradleModel, sourceSet: KotlinSourceSet): Boolean {
if (this == sourceSet) return true
return this.declaredDependsOnSourceSets
.map { dependencySourceSetName -> mppModel.sourceSetsByName.getValue(dependencySourceSetName) }
.any { dependencySourceSet -> dependencySourceSet.isOrDependsOnSourceSet(mppModel, sourceSet) }
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.ProjectKeys
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData
import com.intellij.openapi.roots.DependencyScope
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
internal fun KotlinMPPGradleProjectResolver.Companion.addDependency(
fromModule: DataNode<*>, toModule: DataNode<*>, dependOnTestModule: Boolean
) {
if (fromModule.data == toModule.data) return
val fromData = fromModule.data as? ModuleData ?: return
val toData = toModule.data as? ModuleData ?: return
val existing = fromModule.children.mapNotNull { it.data as? ModuleDependencyData }
.filter { it.target.id == (toModule.data as? ModuleData)?.id }
val nodeToModify = existing.singleOrNull() ?: existing.firstOrNull { it.scope == DependencyScope.COMPILE } ?: existing.firstOrNull()
if (nodeToModify != null) {
nodeToModify.scope = DependencyScope.COMPILE
nodeToModify.isProductionOnTestDependency = nodeToModify.isProductionOnTestDependency || dependOnTestModule
return
}
val moduleDependencyData = ModuleDependencyData(fromData, toData).also {
it.scope = DependencyScope.COMPILE
it.isExported = false
it.isProductionOnTestDependency = dependOnTestModule
}
fromModule.createChild(ProjectKeys.MODULE_DEPENDENCY, moduleDependencyData)
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.util.Pair
import org.jetbrains.kotlin.gradle.KotlinDependency
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibrariesFixer
import org.jetbrains.plugins.gradle.model.ExternalSourceSet
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.GradleProjectResolverUtil
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
@Throws(IllegalStateException::class)
internal fun KotlinMPPGradleProjectResolver.Companion.buildDependencies(
resolverCtx: ProjectResolverContext,
sourceSetMap: Map<String, Pair<DataNode<GradleSourceSetData>, ExternalSourceSet>>,
artifactsMap: Map<ArtifactPath, ModuleId>,
ownerDataNode: DataNode<GradleSourceSetData>,
dependencies: Collection<KotlinDependency>,
ideProject: DataNode<ProjectData>
) {
GradleProjectResolverUtil.buildDependencies(
resolverCtx, sourceSetMap, artifactsMap, ownerDataNode, dependencies, ideProject
)
KotlinNativeLibrariesFixer.applyTo(ownerDataNode, ideProject)
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ModuleData
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.KotlinCompilation
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet
import org.jetbrains.kotlin.idea.configuration.utils.getKotlinModuleId
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
internal fun KotlinMPPGradleProjectResolver.Companion.getCompilations(
gradleModule: IdeaModule,
mppModel: KotlinMPPGradleModel,
ideModule: DataNode<ModuleData>,
resolverCtx: ProjectResolverContext
): Sequence<Pair<DataNode<GradleSourceSetData>, KotlinCompilation>> {
val sourceSetsMap = HashMap<String, DataNode<GradleSourceSetData>>()
for (dataNode in ExternalSystemApiUtil.findAll(ideModule, GradleSourceSetData.KEY)) {
if (dataNode.kotlinSourceSet != null) {
sourceSetsMap[dataNode.data.id] = dataNode
}
}
return sequence {
for (target in mppModel.targets) {
for (compilation in target.compilations) {
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
yield(moduleDataNode to compilation)
}
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.utils.fullName
import org.jetbrains.kotlin.idea.configuration.utils.getKotlinModuleId
internal fun KotlinMPPGradleProjectResolver.Companion.populateModuleDependenciesByCompilations(
context: PopulateModuleDependenciesContext
): Unit = with(context) {
getCompilations(gradleModule, mppModel, ideModule, resolverCtx)
.filterNot { (_, compilation) -> delegateToAndroidPlugin(compilation) }
.filter { (_, compilation) -> processedModuleIds.add(getKotlinModuleId(gradleModule, compilation, resolverCtx)) }
.forEach { (dataNode, compilation) ->
buildDependencies(
resolverCtx, sourceSetMap, artifactsMap, dataNode, getDependencies(compilation), ideProject
)
for (sourceSet in compilation.declaredSourceSets) {
if (sourceSet.fullName() == compilation.fullName()) continue
val targetDataNode = getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx) ?: continue
addDependency(dataNode, targetDataNode, sourceSet.isTestModule)
}
}
}
@@ -0,0 +1,234 @@
/*
* Copyright 2010-2021 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.idea.configuration.mpp
import com.google.common.graph.Graph
import com.intellij.openapi.externalSystem.model.DataNode
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver.Companion.CompilationWithDependencies
import org.jetbrains.kotlin.idea.configuration.klib.KotlinNativeLibraryNameUtil
import org.jetbrains.kotlin.idea.configuration.kotlinAndroidSourceSets
import org.jetbrains.kotlin.idea.configuration.kotlinSourceSet
import org.jetbrains.kotlin.idea.configuration.utils.createSourceSetVisibilityGraph
import org.jetbrains.kotlin.idea.configuration.utils.fullName
import org.jetbrains.kotlin.idea.configuration.utils.getKotlinModuleId
import org.jetbrains.kotlin.idea.configuration.utils.transitiveClosure
import org.jetbrains.plugins.gradle.model.ExternalDependency
import org.jetbrains.plugins.gradle.model.data.GradleSourceSetData
internal fun KotlinMPPGradleProjectResolver.Companion.populateModuleDependenciesBySourceSetVisibilityGraph(
context: PopulateModuleDependenciesContext
): Unit = with(context) {
val sourceSetVisibilityGraph = createSourceSetVisibilityGraph(mppModel).transitiveClosure
for (sourceSet in sourceSetVisibilityGraph.nodes()) {
populateSourceSetInfos(context, sourceSetVisibilityGraph, sourceSet)
if (delegateToAndroidPlugin(sourceSet)) continue
val visibleSourceSets = sourceSetVisibilityGraph.successors(sourceSet)
val fromDataNode = getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx)?.cast<GradleSourceSetData>()
?: return
/* Add dependencies from current sourceSet to all visible source sets (dependsOn, test to production, ...)*/
for (visibleSourceSet in visibleSourceSets) {
val toDataNode = getSiblingKotlinModuleData(visibleSourceSet, gradleModule, ideModule, resolverCtx) ?: continue
addDependency(fromDataNode, toDataNode, visibleSourceSet.isTestModule)
}
if (!processedModuleIds.add(getKotlinModuleId(gradleModule, sourceSet, resolverCtx))) continue
val settings = dependencyPopulationSettings(mppModel, sourceSet)
val directDependencies = getDependencies(sourceSet).toSet()
val dependenciesFromVisibleSourceSets = getDependenciesFromVisibleSourceSets(settings, visibleSourceSets)
val dependenciesFromNativePropagation = getPropagatedNativeDependencies(settings, sourceSet)
val dependenciesFromPlatformPropagation = getPropagatedPlatformDependencies(sourceSet)
val dependencies = dependenciesPreprocessor(
directDependencies + dependenciesFromVisibleSourceSets +
dependenciesFromNativePropagation + dependenciesFromPlatformPropagation
)
buildDependencies(resolverCtx, sourceSetMap, artifactsMap, fromDataNode, dependencies, ideProject)
}
}
private data class DependencyPopulationSettings(
val forceNativeDependencyPropagation: Boolean,
val excludeInheritedNativeDependencies: Boolean
)
private fun dependencyPopulationSettings(mppModel: KotlinMPPGradleModel, sourceSet: KotlinSourceSet): DependencyPopulationSettings {
val forceNativeDependencyPropagation: Boolean
val excludeInheritedNativeDependencies: Boolean
if (mppModel.extraFeatures.isHMPPEnabled && sourceSet.actualPlatforms.singleOrNull() == KotlinPlatform.NATIVE) {
forceNativeDependencyPropagation = mppModel.extraFeatures.isNativeDependencyPropagationEnabled
excludeInheritedNativeDependencies = !forceNativeDependencyPropagation
} else {
forceNativeDependencyPropagation = false
excludeInheritedNativeDependencies = false
}
return DependencyPopulationSettings(
forceNativeDependencyPropagation = forceNativeDependencyPropagation,
excludeInheritedNativeDependencies = excludeInheritedNativeDependencies
)
}
private fun PopulateModuleDependenciesContext.getDependenciesFromVisibleSourceSets(
settings: DependencyPopulationSettings,
visibleSourceSets: Set<KotlinSourceSet>
): Set<KotlinDependency> {
val inheritedDependencies = visibleSourceSets
.flatMap { visibleSourceSet -> getDependencies(visibleSourceSet) }
return if (settings.excludeInheritedNativeDependencies) {
inheritedDependencies.filter { !it.name.startsWith(KotlinNativeLibraryNameUtil.KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE) }
} else {
inheritedDependencies
}.toSet()
}
private fun PopulateModuleDependenciesContext.getPropagatedNativeDependencies(
settings: DependencyPopulationSettings,
sourceSet: KotlinSourceSet
): Set<KotlinDependency> {
if (!settings.forceNativeDependencyPropagation) {
return emptySet()
}
return getPropagatedNativeDependencies(getCompilationsWithDependencies(sourceSet)).toSet()
}
/**
* We can't really commonize native platform libraries yet.
* But APIs for different targets may be very similar.
* E.g. ios_arm64 and ios_x64 have almost identical platform libraries
* We handle these special cases and resolve common sources for such
* targets against libraries of one of them. E.g. common sources for
* ios_x64 and ios_arm64 will be resolved against ios_arm64 libraries.
* Currently such special casing is available for Apple platforms
* (iOS, watchOS and tvOS) and native Android (ARM, X86).
* TODO: Do we need to support user's interop libraries too?
*/
private fun getPropagatedNativeDependencies(compilations: List<CompilationWithDependencies>): List<ExternalDependency> {
if (compilations.size <= 1) {
return emptyList()
}
val copyFrom = when {
compilations.all { it.isAppleCompilation } ->
compilations.selectFirstAvailableTarget(
"watchos_arm64", "watchos_arm32", "watchos_x86",
"ios_arm64", "ios_arm32", "ios_x64",
"tvos_arm64", "tvos_x64"
)
compilations.all { it.konanTarget?.startsWith("android") == true } ->
compilations.selectFirstAvailableTarget(
"android_arm64", "android_arm32", "android_x64", "android_x86"
)
else -> return emptyList()
}
return copyFrom.dependencyNames.mapNotNull { (name, dependency) ->
when {
!name.startsWith(KotlinNativeLibraryNameUtil.KOTLIN_NATIVE_LIBRARY_PREFIX_PLUS_SPACE) -> null // Support only default platform libs for now.
compilations.all { it.dependencyNames.containsKey(name) } -> dependency
else -> null
}
}
}
private val CompilationWithDependencies.isAppleCompilation: Boolean
get() = konanTarget?.let {
it.startsWith("ios") || it.startsWith("watchos") || it.startsWith("tvos")
} ?: false
private fun Iterable<CompilationWithDependencies>.selectFirstAvailableTarget(
@NonNls vararg targetsByPriority: String
): CompilationWithDependencies {
for (target in targetsByPriority) {
val result = firstOrNull { it.konanTarget == target }
if (result != null) {
return result
}
}
return first()
}
private fun PopulateModuleDependenciesContext.getPropagatedPlatformDependencies(
sourceSet: KotlinSourceSet
): Set<KotlinDependency> {
if (!mppModel.extraFeatures.isHMPPEnabled) {
return emptySet()
}
return getPropagatedPlatformDependencies(mppModel, sourceSet)
}
/**
* Source sets sharing code between JVM and Android are the only intermediate source sets that
* can effectively consume a dependency's platform artifact.
* When a library only offers a JVM variant, then Android and JVM consume this variant of the library.
*
* This will be replaced later on by [KT-43450](https://youtrack.jetbrains.com/issue/KT-43450)
*
* @return all dependencies being present across given JVM and Android compilations that this [sourceSet] can also participates in.
*/
private fun getPropagatedPlatformDependencies(
mppModel: KotlinMPPGradleModel,
sourceSet: KotlinSourceSet,
): Set<ExternalDependency> {
if (
sourceSet.actualPlatforms.platforms.sorted() == listOf(KotlinPlatform.JVM, KotlinPlatform.ANDROID).sorted()
) {
return mppModel.targets
.filter { target -> target.platform == KotlinPlatform.JVM || target.platform == KotlinPlatform.ANDROID }
.flatMap { target -> target.compilations }
.filter { compilation -> compilation.dependsOnSourceSet(mppModel, sourceSet) }
.map { targetCompilations -> targetCompilations.dependencies.mapNotNull(mppModel.dependencyMap::get).toSet() }
.reduceOrNull { acc, dependencies -> acc.intersect(dependencies) }.orEmpty()
}
return emptySet()
}
// TODO: Move this maybe to another semantic part of KotlinMPPGradleProjectResolver?
private fun KotlinMPPGradleProjectResolver.Companion.populateSourceSetInfos(
context: PopulateModuleDependenciesContext,
closedSourceSetGraph: Graph<KotlinSourceSet>,
sourceSet: KotlinSourceSet
) = with(context) {
val isAndroid = delegateToAndroidPlugin(sourceSet)
val fromDataNode = if (isAndroid) ideModule
else getSiblingKotlinModuleData(sourceSet, gradleModule, ideModule, resolverCtx) ?: return
val dependeeSourceSets = closedSourceSetGraph.successors(sourceSet)
val sourceSetInfos = if (isAndroid) {
ideModule.kotlinAndroidSourceSets?.filter {
(it.kotlinModule as? KotlinCompilation)?.declaredSourceSets?.contains(sourceSet) ?: false
} ?: emptyList()
} else {
listOfNotNull(fromDataNode.kotlinSourceSet)
}
for (sourceSetInfo in sourceSetInfos) {
if (sourceSetInfo.kotlinModule is KotlinCompilation) {
val selfName = sourceSetInfo.kotlinModule.fullName()
sourceSetInfo.addSourceSets(dependeeSourceSets, selfName, gradleModule, resolverCtx)
}
}
}
inline fun <reified T : Any> DataNode<*>.cast(): DataNode<T> {
if (data !is T) {
throw ClassCastException("DataNode<${data.javaClass.canonicalName}> cannot be cast to DataNode<${T::class.java.canonicalName}>")
}
@Suppress("UNCHECKED_CAST")
return this as DataNode<T>
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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.idea.configuration.utils
import org.gradle.tooling.model.idea.IdeaModule
import org.jetbrains.kotlin.gradle.KotlinCompilation
import org.jetbrains.kotlin.gradle.KotlinModule
import org.jetbrains.kotlin.gradle.compilationFullName
import org.jetbrains.kotlin.idea.configuration.KotlinMPPGradleProjectResolver
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
internal fun KotlinModule.fullName(simpleName: String = name) = when (this) {
is KotlinCompilation -> compilationFullName(simpleName, disambiguationClassifier)
else -> simpleName
}
internal fun KotlinMPPGradleProjectResolver.Companion.getKotlinModuleId(
gradleModule: IdeaModule, kotlinModule: KotlinModule, resolverCtx: ProjectResolverContext
) = getGradleModuleQualifiedName(resolverCtx, gradleModule, kotlinModule.fullName())
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2021 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.
*/
@file:Suppress("UnstableApiUsage")
package org.jetbrains.kotlin.idea.configuration.utils
import com.google.common.graph.*
import org.jetbrains.kotlin.gradle.KotlinMPPGradleModel
import org.jetbrains.kotlin.gradle.KotlinPlatform
import org.jetbrains.kotlin.gradle.KotlinSourceSet
internal fun createSourceSetVisibilityGraph(model: KotlinMPPGradleModel): ImmutableGraph<KotlinSourceSet> {
val graph = createSourceSetDependsOnGraph(model)
graph.putInferredTestToProductionEdges()
return graph.immutable
}
internal fun createSourceSetDependsOnGraph(model: KotlinMPPGradleModel): MutableGraph<KotlinSourceSet> {
return createSourceSetDependsOnGraph(model.sourceSetsByName)
}
internal fun createSourceSetDependsOnGraph(
sourceSetsByName: Map<String, KotlinSourceSet>
): MutableGraph<KotlinSourceSet> {
val graph = GraphBuilder.directed().build<KotlinSourceSet>()
val sourceSets = sourceSetsByName.values.toSet()
for (sourceSet in sourceSets) {
graph.addNode(sourceSet)
val dependsOnSourceSets = getFixedDependsOnSourceSets(sourceSetsByName, sourceSet)
for (dependsOnSourceSet in dependsOnSourceSets) {
graph.addNode(dependsOnSourceSet)
graph.putEdge(sourceSet, dependsOnSourceSet)
}
}
return graph
}
internal fun MutableGraph<KotlinSourceSet>.putInferredTestToProductionEdges() {
val sourceSets = this.nodes()
for (sourceSet in sourceSets) {
if (sourceSet.isTestModule) {
@OptIn(UnsafeTestSourceSetHeuristicApi::class)
val predictedMainSourceSetName = predictedProductionSourceSetName(sourceSet.name)
val predictedMainSourceSet = sourceSets.firstOrNull { it.name == predictedMainSourceSetName } ?: continue
putEdge(sourceSet, predictedMainSourceSet)
}
}
}
private fun getFixedDependsOnSourceSets(
sourceSetsByName: Map<String, KotlinSourceSet>, sourceSet: KotlinSourceSet
): Set<KotlinSourceSet> {
/*
Workaround for older Kotlin Gradle Plugin versions that did not explicitly declare a dependsOn relation
from a Kotlin source set to "commonMain"
(Can probably be dropped in Kotlin 1.5)
*/
val implicitDependsOnEdgeForAndroid = if (
sourceSet.actualPlatforms.contains(KotlinPlatform.ANDROID) && sourceSet.declaredDependsOnSourceSets.isEmpty()
) {
val commonSourceSetName = if (sourceSet.isTestModule) KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME
else KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
listOfNotNull(sourceSetsByName[commonSourceSetName])
} else emptyList()
return sourceSet.declaredDependsOnSourceSets.map(sourceSetsByName::getValue)
.plus(implicitDependsOnEdgeForAndroid)
/*
Gracefully filter out source sets that declare a dependency on themselves.
This also fixes KTIJ-1025
*/
.filter { dependencySourceSet -> dependencySourceSet != sourceSet }
.toSet()
}
/**
* @see Graphs.transitiveClosure
*/
internal val <T> Graph<T>.transitiveClosure: Graph<T> get() = Graphs.transitiveClosure(this)
/**
* @see ImmutableGraph.copyOf
*/
internal val <T> Graph<T>.immutable: ImmutableGraph<T> get() = ImmutableGraph.copyOf(this)
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2021 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.idea.configuration.utils
// TODO: Replace heuristic with proper import!
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
annotation class UnsafeTestSourceSetHeuristicApi
@UnsafeTestSourceSetHeuristicApi
internal fun isProbablyTestSourceSet(name: String): Boolean {
return name.endsWith("Test") || name == "test"
}
@UnsafeTestSourceSetHeuristicApi
internal fun isProbablyProductionSourceSet(name: String): Boolean {
return !isProbablyTestSourceSet(name)
}
@UnsafeTestSourceSetHeuristicApi
internal fun predictedProductionSourceSetName(testSourceSetName: String): String {
/*
This implementation uses a lot of implicit knowledge and should be replaced.
This implicit knowledge is/was used at other places as well that should also
use an explicit API once available.
See:
https://github.com/JetBrains/intellij-community/blob/c05e8d6de8d1c690eb99c2fff8629c0de373c48f/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/CommonGradleProjectResolverExtension.java#L153
https://github.com/JetBrains/intellij-kotlin/blob/fe6ee9dfb42ad1cdd9e55449e2481125f1631133/gradle/gradle-idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt#L424
https://github.com/JetBrains/intellij-kotlin/blob/fe6ee9dfb42ad1cdd9e55449e2481125f1631133/gradle/gradle-idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMPPGradleProjectResolver.kt#L627
*/
if (testSourceSetName.endsWith("Test")) {
return testSourceSetName.removeSuffix("Test") + "Main"
}
if (testSourceSetName == "test") {
return "main"
}
return testSourceSetName + "Main"
}
@@ -0,0 +1,167 @@
/*
* Copyright 2010-2021 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.idea.configuration.utils
import com.google.common.graph.EndpointPair
import com.google.common.graph.EndpointPair.ordered
import com.google.common.graph.Graph
import com.google.common.graph.GraphBuilder
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME
import org.jetbrains.kotlin.gradle.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME
import org.junit.Test
import kotlin.test.assertEquals
class KotlinSourceSetGraphUtilTest {
@Test
fun `simple layout`() {
val sourceSetsByName = mapOf(
createKotlinSourceSetPair(COMMON_MAIN_SOURCE_SET_NAME),
createKotlinSourceSetPair(COMMON_TEST_SOURCE_SET_NAME),
createKotlinSourceSetPair("jvmMain", setOf(COMMON_MAIN_SOURCE_SET_NAME)),
createKotlinSourceSetPair("jvmTest", setOf(COMMON_TEST_SOURCE_SET_NAME))
)
val dependsOnGraph = createSourceSetDependsOnGraph(sourceSetsByName)
assertEquals(
listOf(
ordered("jvmMain", COMMON_MAIN_SOURCE_SET_NAME),
ordered("jvmTest", COMMON_TEST_SOURCE_SET_NAME)
).sorted(),
dependsOnGraph.nameEdges().sorted()
)
}
@Test
fun `hierarchical layout`() {
val sourceSetsByName = mapOf(
createKotlinSourceSetPair(COMMON_MAIN_SOURCE_SET_NAME),
createKotlinSourceSetPair(COMMON_TEST_SOURCE_SET_NAME),
createKotlinSourceSetPair("jvmAndJsMain", setOf(COMMON_MAIN_SOURCE_SET_NAME)),
createKotlinSourceSetPair("jvmAndJsTest", setOf(COMMON_TEST_SOURCE_SET_NAME)),
createKotlinSourceSetPair("jvmMain", setOf("jvmAndJsMain")),
createKotlinSourceSetPair("jvmTest", setOf("jvmAndJsTest")),
createKotlinSourceSetPair("jsMain", setOf("jvmAndJsMain")),
createKotlinSourceSetPair("jsTest", setOf("jvmAndJsTest")),
)
val dependsOnGraph = createSourceSetDependsOnGraph(sourceSetsByName)
assertEquals(
listOf(
ordered("jvmMain", "jvmAndJsMain"),
ordered("jvmTest", "jvmAndJsTest"),
ordered("jsMain", "jvmAndJsMain"),
ordered("jsTest", "jvmAndJsTest"),
ordered("jvmAndJsMain", COMMON_MAIN_SOURCE_SET_NAME),
ordered("jvmAndJsTest", COMMON_TEST_SOURCE_SET_NAME)
).sorted(),
dependsOnGraph.nameEdges().sorted()
)
}
@Test
fun `source set depending on multiple other source sets`() {
val sourceSetsByName = mapOf(
createKotlinSourceSetPair("common1"),
createKotlinSourceSetPair("common2"),
createKotlinSourceSetPair("consumerA", setOf("common1", "common2")),
createKotlinSourceSetPair("consumerB", setOf("common1", "consumerA"))
)
val dependsOnGraph = createSourceSetDependsOnGraph(sourceSetsByName)
assertEquals(
listOf(
ordered("consumerA", "common1"),
ordered("consumerA", "common2"),
ordered("consumerB", "common1"),
ordered("consumerB", "consumerA")
).sorted(),
dependsOnGraph.nameEdges().sorted()
)
}
@Test
fun `infer default test to production edges`() {
val sourceSetsByName = mapOf(
createKotlinSourceSetPair(COMMON_MAIN_SOURCE_SET_NAME),
createKotlinSourceSetPair(COMMON_TEST_SOURCE_SET_NAME, isTestModule = true),
createKotlinSourceSetPair("main"),
createKotlinSourceSetPair("test", isTestModule = true),
createKotlinSourceSetPair("abcMain"),
createKotlinSourceSetPair("abcTest", isTestModule = true)
)
val graph = GraphBuilder.directed().build<KotlinSourceSet>()
sourceSetsByName.values.forEach { sourceSet -> graph.addNode(sourceSet) }
graph.putInferredTestToProductionEdges()
assertEquals(
listOf(
ordered(COMMON_TEST_SOURCE_SET_NAME, COMMON_MAIN_SOURCE_SET_NAME),
ordered("test", "main"),
ordered("abcTest", "abcMain")
).sorted(),
graph.nameEdges().sorted()
)
}
@Test
fun `depends on graph will automatically infer missing dependsOn edges for Android source sets`() {
val sourceSetsByName = mapOf(
createKotlinSourceSetPair(COMMON_MAIN_SOURCE_SET_NAME),
createKotlinSourceSetPair(COMMON_TEST_SOURCE_SET_NAME, isTestModule = true),
createKotlinSourceSetPair("androidMain", platforms = setOf(KotlinPlatform.ANDROID)),
createKotlinSourceSetPair("androidTest", platforms = setOf(KotlinPlatform.ANDROID), isTestModule = true)
)
assertEquals(
listOf(
ordered("androidMain", COMMON_MAIN_SOURCE_SET_NAME),
ordered("androidTest", COMMON_TEST_SOURCE_SET_NAME)
).sorted(),
createSourceSetDependsOnGraph(sourceSetsByName).nameEdges().sorted()
)
}
}
private fun createKotlinSourceSetPair(
name: String,
declaredDependsOnSourceSets: Set<String> = emptySet(),
allDependsOnSourceSets: Set<String> = declaredDependsOnSourceSets,
platforms: Set<KotlinPlatform> = emptySet(),
isTestModule: Boolean = false,
): Pair<String, KotlinSourceSet> = name to KotlinSourceSetImpl(
name = name,
languageSettings = KotlinLanguageSettingsImpl(
languageVersion = null,
apiVersion = null,
isProgressiveMode = false,
enabledLanguageFeatures = emptySet(),
experimentalAnnotationsInUse = emptySet(),
compilerPluginArguments = emptyArray(),
compilerPluginClasspath = emptySet(),
freeCompilerArgs = emptyArray()
),
sourceDirs = emptySet(),
resourceDirs = emptySet(),
dependencies = emptyArray(),
declaredDependsOnSourceSets = declaredDependsOnSourceSets,
allDependsOnSourceSets = allDependsOnSourceSets,
defaultActualPlatforms = KotlinPlatformContainerImpl().apply { pushPlatforms(platforms) },
defaultIsTestModule = isTestModule
)
private fun Graph<KotlinSourceSet>.nameEdges() = edges()
.map { edge -> ordered(edge.source().name, edge.target().name) }
private fun Iterable<EndpointPair<String>>.sorted() = this.sortedBy { it.source() + " -> " + it.target() }
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 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.idea.configuration.utils
import org.junit.Test
import kotlin.test.assertEquals
@OptIn(UnsafeTestSourceSetHeuristicApi::class)
class TestSourceSetUtilsTest {
@Test
fun `test predicted production source set name for various examples`() {
assertEquals("commonMain", predictedProductionSourceSetName("commonTest"))
assertEquals("main", predictedProductionSourceSetName("test"))
assertEquals("abcMain", predictedProductionSourceSetName("abc"))
assertEquals("abctestMain", predictedProductionSourceSetName("abctest"))
}
}
+7 -1
View File
@@ -12,11 +12,17 @@ dependencies {
compileOnly(intellijPluginDep("gradle"))
compileOnly(intellijDep()) { includeJars("slf4j-api-1.7.25") }
testImplementation(commonDep("junit:junit"))
testImplementation(projectTests(":compiler:tests-common"))
testCompileOnly(projectTests(":idea:idea-test-framework"))
testCompileOnly(intellijDep())
}
sourceSets {
"main" { projectDefault() }
"test" {}
"test" { projectDefault() }
}
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2021 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
@ExperimentalGradleToolingApi
fun KotlinMPPGradleModel.getCompilations(sourceSet: KotlinSourceSet): Set<KotlinCompilation> {
return targets.flatMap { target -> target.compilations }
.filter { compilation -> compilationDependsOnSourceSet(compilation, sourceSet) }
.toSet()
}
@ExperimentalGradleToolingApi
fun KotlinMPPGradleModel.compilationDependsOnSourceSet(
compilation: KotlinCompilation, sourceSet: KotlinSourceSet
): Boolean {
return compilation.declaredSourceSets.any { sourceSetInCompilation ->
sourceSetInCompilation == sourceSet || sourceSetInCompilation.isDependsOn(this, sourceSet)
}
}
@@ -16,30 +16,25 @@ class KotlinSourceSetProto(
private val dependencies: () -> Array<KotlinDependencyId>,
val dependsOnSourceSets: Set<String>
) {
fun buildKotlinSourceSetImpl(
doBuildDependencies: Boolean,
allSourceSetsProtosByNames: Map<String, KotlinSourceSetProto>,
dependsOnCache: HashMap<String, Set<String>>
) = KotlinSourceSetImpl(
name,
languageSettings,
sourceDirs,
resourceDirs,
if (doBuildDependencies) dependencies.invoke() else emptyArray(),
calculateDependsOnClosure(this, allSourceSetsProtosByNames, dependsOnCache)
name = name,
languageSettings = languageSettings,
sourceDirs = sourceDirs,
resourceDirs = resourceDirs,
dependencies = if (doBuildDependencies) dependencies.invoke() else emptyArray(),
declaredDependsOnSourceSets = dependsOnSourceSets,
allDependsOnSourceSets = allDependsOnSourceSets(allSourceSetsProtosByNames)
)
}
private fun calculateDependsOnClosure(
currentSourceSetProto: KotlinSourceSetProto,
sourceSetsMap: Map<String, KotlinSourceSetProto>,
cache: MutableMap<String, Set<String>>
): Set<String> {
return cache.computeIfAbsent(currentSourceSetProto.name) {
currentSourceSetProto.dependsOnSourceSets.flatMap { name ->
val nextSourceSet = sourceSetsMap[name] ?: error("Source set $name is not found in map $sourceSetsMap")
calculateDependsOnClosure(nextSourceSet, sourceSetsMap, cache).union(setOf(name))
}.toSet()
fun KotlinSourceSetProto.allDependsOnSourceSets(sourceSetsByName: Map<String, KotlinSourceSetProto>): Set<String> {
return mutableSetOf<String>().apply {
addAll(dependsOnSourceSets)
dependsOnSourceSets.map(sourceSetsByName::getValue).forEach { dependsOnSourceSet ->
addAll(dependsOnSourceSet.allDependsOnSourceSets(sourceSetsByName))
}
}
}
@@ -50,29 +45,41 @@ class KotlinSourceSetImpl(
override val sourceDirs: Set<File>,
override val resourceDirs: Set<File>,
override val dependencies: Array<KotlinDependencyId>,
override val dependsOnSourceSets: Set<String>,
val defaultPlatform: KotlinPlatformContainerImpl = KotlinPlatformContainerImpl(),
override val declaredDependsOnSourceSets: Set<String>,
@Suppress("OverridingDeprecatedMember")
override val allDependsOnSourceSets: Set<String>,
defaultActualPlatforms: KotlinPlatformContainerImpl = KotlinPlatformContainerImpl(),
defaultIsTestModule: Boolean = false
) : KotlinSourceSet {
constructor(kotlinSourceSet: KotlinSourceSet, cloningCache: MutableMap<Any, Any>) : this(
kotlinSourceSet.name,
KotlinLanguageSettingsImpl(kotlinSourceSet.languageSettings),
HashSet(kotlinSourceSet.sourceDirs),
HashSet(kotlinSourceSet.resourceDirs),
kotlinSourceSet.dependencies,
HashSet(kotlinSourceSet.dependsOnSourceSets),
KotlinPlatformContainerImpl(kotlinSourceSet.actualPlatforms)
@Suppress("DEPRECATION")
constructor(kotlinSourceSet: KotlinSourceSet) : this(
name = kotlinSourceSet.name,
languageSettings = KotlinLanguageSettingsImpl(kotlinSourceSet.languageSettings),
sourceDirs = HashSet(kotlinSourceSet.sourceDirs),
resourceDirs = HashSet(kotlinSourceSet.resourceDirs),
dependencies = kotlinSourceSet.dependencies.clone(),
declaredDependsOnSourceSets = HashSet(kotlinSourceSet.declaredDependsOnSourceSets),
allDependsOnSourceSets = HashSet(kotlinSourceSet.allDependsOnSourceSets),
defaultActualPlatforms = KotlinPlatformContainerImpl(kotlinSourceSet.actualPlatforms)
) {
this.isTestModule = kotlinSourceSet.isTestModule
}
override var actualPlatforms: KotlinPlatformContainer = defaultPlatform
override var actualPlatforms: KotlinPlatformContainer = defaultActualPlatforms
internal set
override var isTestModule: Boolean = false
override var isTestModule: Boolean = defaultIsTestModule
internal set
override fun toString() = name
init {
@Suppress("DEPRECATION")
require(allDependsOnSourceSets.containsAll(declaredDependsOnSourceSets)) {
"Inconsistent source set dependencies: 'allDependsOnSourceSets' is expected to contain all 'declaredDependsOnSourceSets'"
}
}
}
data class KotlinLanguageSettingsImpl(
@@ -128,7 +135,7 @@ data class KotlinNativeCompilationExtensionsImpl(
data class KotlinCompilationImpl(
override val name: String,
override val allSourceSets: Collection<KotlinSourceSet>,
override val defaultSourceSets: Collection<KotlinSourceSet>,
override val declaredSourceSets: Collection<KotlinSourceSet>,
override val dependencies: Array<KotlinDependencyId>,
override val output: KotlinCompilationOutput,
override val arguments: KotlinCompilationArguments,
@@ -139,15 +146,15 @@ data class KotlinCompilationImpl(
// create deep copy
constructor(kotlinCompilation: KotlinCompilation, cloningCache: MutableMap<Any, Any>) : this(
kotlinCompilation.name,
cloneSourceSetsWithCaching(kotlinCompilation.allSourceSets, cloningCache),
cloneSourceSetsWithCaching(kotlinCompilation.defaultSourceSets, cloningCache),
kotlinCompilation.dependencies,
KotlinCompilationOutputImpl(kotlinCompilation.output),
KotlinCompilationArgumentsImpl(kotlinCompilation.arguments),
kotlinCompilation.dependencyClasspath,
KotlinTaskPropertiesImpl(kotlinCompilation.kotlinTaskProperties),
kotlinCompilation.nativeExtensions?.let(::KotlinNativeCompilationExtensionsImpl)
name = kotlinCompilation.name,
declaredSourceSets = cloneSourceSetsWithCaching(kotlinCompilation.declaredSourceSets, cloningCache),
allSourceSets = cloneSourceSetsWithCaching(kotlinCompilation.allSourceSets, cloningCache),
dependencies = kotlinCompilation.dependencies,
output = KotlinCompilationOutputImpl(kotlinCompilation.output),
arguments = KotlinCompilationArgumentsImpl(kotlinCompilation.arguments),
dependencyClasspath = kotlinCompilation.dependencyClasspath,
kotlinTaskProperties = KotlinTaskPropertiesImpl(kotlinCompilation.kotlinTaskProperties),
nativeExtensions = kotlinCompilation.nativeExtensions?.let(::KotlinNativeCompilationExtensionsImpl)
) {
disambiguationClassifier = kotlinCompilation.disambiguationClassifier
platform = kotlinCompilation.platform
@@ -158,7 +165,7 @@ data class KotlinCompilationImpl(
override lateinit var platform: KotlinPlatform
internal set
// TODO: Logic like this is duplicated *and different*
override val isTestModule: Boolean
get() = name == KotlinCompilation.TEST_COMPILATION_NAME
|| platform == KotlinPlatform.ANDROID && name.contains("Test")
@@ -171,7 +178,7 @@ data class KotlinCompilationImpl(
cloningCache: MutableMap<Any, Any>
): List<KotlinSourceSet> =
sourceSets.map { initialSourceSet ->
(cloningCache[initialSourceSet] as? KotlinSourceSet) ?: KotlinSourceSetImpl(initialSourceSet, cloningCache).also {
(cloningCache[initialSourceSet] as? KotlinSourceSet) ?: KotlinSourceSetImpl(initialSourceSet).also {
cloningCache[initialSourceSet] = it
}
}
@@ -250,7 +257,7 @@ data class ExtraFeaturesImpl(
) : ExtraFeatures
data class KotlinMPPGradleModelImpl(
override val sourceSets: Map<String, KotlinSourceSet>,
override val sourceSetsByName: Map<String, KotlinSourceSet>,
override val targets: Collection<KotlinTarget>,
override val extraFeatures: ExtraFeatures,
override val kotlinNativeHome: String,
@@ -259,11 +266,9 @@ data class KotlinMPPGradleModelImpl(
) : KotlinMPPGradleModel {
constructor(mppModel: KotlinMPPGradleModel, cloningCache: MutableMap<Any, Any>) : this(
mppModel.sourceSets.mapValues { initialSourceSet ->
(cloningCache[initialSourceSet] as? KotlinSourceSet) ?: KotlinSourceSetImpl(
initialSourceSet.value,
cloningCache
).also { cloningCache[initialSourceSet] = it }
mppModel.sourceSetsByName.mapValues { initialSourceSet ->
(cloningCache[initialSourceSet] as? KotlinSourceSet) ?: KotlinSourceSetImpl(initialSourceSet.value)
.also { cloningCache[initialSourceSet] = it }
},
mppModel.targets.map { initialTarget ->
(cloningCache[initialTarget] as? KotlinTarget) ?: KotlinTargetImpl(initialTarget, cloningCache).also {
@@ -289,20 +294,20 @@ class KotlinPlatformContainerImpl() : KotlinPlatformContainer {
get() = myPlatforms != null
constructor(platform: KotlinPlatformContainer) : this() {
myPlatforms = HashSet<KotlinPlatform>(platform.platforms)
myPlatforms = HashSet(platform.platforms)
}
override val platforms: Collection<KotlinPlatform>
override val platforms: Set<KotlinPlatform>
get() = myPlatforms ?: defaultCommonPlatform
override fun supports(simplePlatform: KotlinPlatform): Boolean = platforms.contains(simplePlatform)
override fun addSimplePlatforms(platforms: Collection<KotlinPlatform>) {
(myPlatforms ?: HashSet<KotlinPlatform>().apply { myPlatforms = this }).let {
it.addAll(platforms)
if (it.contains(KotlinPlatform.COMMON)) {
it.clear()
it.addAll(defaultCommonPlatform)
override fun pushPlatforms(platforms: Iterable<KotlinPlatform>) {
myPlatforms = (myPlatforms ?: LinkedHashSet()).apply {
addAll(platforms)
if (contains(KotlinPlatform.COMMON)) {
clear()
addAll(defaultCommonPlatform)
}
}
}
@@ -0,0 +1,307 @@
/*
* Copyright 2010-2021 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
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@OptIn(ExperimentalGradleToolingApi::class)
class KotlinMppGradleModelExtensionsTest {
@Test
fun resolveDeclaredDependsOnSourceSets() {
val commonMain = createKotlinSourceSet("commonMain")
val appleMain = createKotlinSourceSet("appleMain", declaredDependsOnSourceSets = setOf("commonMain"))
val macosMain = createKotlinSourceSet("macosMain", declaredDependsOnSourceSets = setOf("appleMain"))
val iosMain = createKotlinSourceSet("iosMain", declaredDependsOnSourceSets = setOf("appleMain", "commonMain"))
val model = createKotlinMPPGradleModel(
sourceSets = setOf(commonMain, appleMain, macosMain, iosMain)
)
assertEquals(
emptySet(), model.resolveDeclaredDependsOnSourceSets(commonMain),
"Expected no declared dependency source sets for commonMain"
)
assertEquals(
setOf(commonMain), model.resolveDeclaredDependsOnSourceSets(appleMain),
"Expected only declared dependency for 'appleMain'"
)
assertEquals(
setOf(appleMain, commonMain), model.resolveDeclaredDependsOnSourceSets(iosMain),
"Expected only declared dependency for 'iosMain'"
)
}
@Test
fun `resolveDeclaredDependsOnSourceSets is graceful for missing source sets`() {
val commonMain = createKotlinSourceSet("commonMain")
val macosMain = createKotlinSourceSet("macosMain", declaredDependsOnSourceSets = setOf("commonMain", "missing"))
val model = createKotlinMPPGradleModel(sourceSets = setOf(commonMain, macosMain))
assertEquals(
setOf(commonMain), model.resolveDeclaredDependsOnSourceSets(macosMain),
"Expected declaredDependencySourceSets to ignore missing dependency source set"
)
}
@Test
fun resolveAllDependsOnSourceSets() {
val commonMain = createKotlinSourceSet("commonMain")
val appleMain = createKotlinSourceSet("appleMain", declaredDependsOnSourceSets = setOf("commonMain"))
val x64Main = createKotlinSourceSet("x64Main", declaredDependsOnSourceSets = setOf("commonMain"))
val macosMain = createKotlinSourceSet("macosMain", declaredDependsOnSourceSets = setOf("appleMain"))
val macosX64Main = createKotlinSourceSet("macosX64Main", declaredDependsOnSourceSets = setOf("appleMain", "x64Main"))
val macosArm64Main = createKotlinSourceSet("macosArm64Main", declaredDependsOnSourceSets = setOf("appleMain"))
val model = createKotlinMPPGradleModel(sourceSets = setOf(commonMain, appleMain, x64Main, macosMain, macosX64Main, macosArm64Main))
assertEquals(
setOf(appleMain, x64Main, commonMain),
model.resolveAllDependsOnSourceSets(macosX64Main),
)
assertEquals(
setOf(appleMain, x64Main, commonMain).sortedBy { it.name },
model.resolveAllDependsOnSourceSets(macosX64Main).sortedBy { it.name }.toList(),
)
assertEquals(
setOf(appleMain, commonMain),
model.resolveAllDependsOnSourceSets(macosArm64Main).toSet(),
)
assertEquals(
setOf(commonMain),
model.resolveAllDependsOnSourceSets(appleMain).toSet(),
"Expected only 'commonMain' for 'appleMain'"
)
assertEquals(
emptySet(), model.resolveAllDependsOnSourceSets(commonMain).toSet(),
"Expected empty set for 'commonMain'"
)
}
@Test
fun `resolving source sets with self references or loops`() {
val a = createKotlinSourceSet("a", declaredDependsOnSourceSets = setOf(""))
val b = createKotlinSourceSet("b", declaredDependsOnSourceSets = setOf("a", "c")) // loop d -> c -> b -> c
val c = createKotlinSourceSet("c", declaredDependsOnSourceSets = setOf("b", "c")) // self reference c -> c
val d = createKotlinSourceSet("d", declaredDependsOnSourceSets = setOf("c"))
val model = createKotlinMPPGradleModel(sourceSets = setOf(a, b, c, d))
assertEquals(
setOf(a, b, c).sortedBy { it.name },
model.resolveAllDependsOnSourceSets(d).sortedBy { it.name },
"Expected dependency loop to be handled gracefully"
)
assertEquals(
setOf(a, b, c).sortedBy { it.name },
model.resolveAllDependsOnSourceSets(c).sortedBy { it.name },
"Expected self dependency to be resolved for 'resolveAllDependsOnSourceSets'"
)
assertEquals(
setOf(b, c).sortedBy { it.name },
model.resolveDeclaredDependsOnSourceSets(c).sortedBy { it.name },
"Expected self dependency to be resolved by 'resolveDeclaredDependsOnSourceSets'"
)
}
@Test
fun isDependsOn() {
val commonMain = createKotlinSourceSet("commonMain")
val appleMain = createKotlinSourceSet("appleMain", declaredDependsOnSourceSets = setOf("commonMain"))
val x64Main = createKotlinSourceSet("x64Main", declaredDependsOnSourceSets = setOf("commonMain"))
val macosX64Main = createKotlinSourceSet("macosX64Main", declaredDependsOnSourceSets = setOf("appleMain", "x64Main"))
val macosArm64Main = createKotlinSourceSet("macosArm64Main", declaredDependsOnSourceSets = setOf("appleMain"))
val model = createKotlinMPPGradleModel(sourceSets = setOf(commonMain, appleMain, x64Main, macosX64Main))
assertTrue(
model.isDependsOn(from = appleMain, to = commonMain),
"Expected isDependsOn from appleMain to commonMain"
)
assertTrue(
model.isDependsOn(from = x64Main, to = commonMain),
"Expected isDependsOn from x64Main to commonMain"
)
assertTrue(
model.isDependsOn(from = macosX64Main, to = commonMain),
"Expected isDependsOn from macosX64Main to commonMain"
)
assertTrue(
model.isDependsOn(from = macosX64Main, to = appleMain),
"Expected isDependsOn from macosX64Main to appleMain"
)
assertTrue(
model.isDependsOn(from = macosX64Main, to = commonMain),
"Expected isDependsOn from macosX64Main to commonMain"
)
assertTrue(
model.isDependsOn(from = macosX64Main, to = appleMain),
"Expected isDependsOn from macosX64Main to appleMain"
)
assertTrue(
model.isDependsOn(from = macosX64Main, to = x64Main),
"Expected isDependsOn from macosX64Main to x64Main"
)
assertTrue(
model.isDependsOn(from = macosArm64Main, to = commonMain),
"Expected isDependsOn from macosArm64Main to commonMain"
)
assertTrue(
model.isDependsOn(from = macosArm64Main, to = appleMain),
"Expected isDependsOn from macosArm64Main to appleMain"
)
assertFalse(
model.isDependsOn(from = macosArm64Main, to = x64Main),
"Expected false isDependsOn from macosArm64 to x64Main"
)
for (sourceSet in model.sourceSetsByName.values) {
assertFalse(
model.isDependsOn(from = commonMain, to = sourceSet),
"Expected false isDependsOn from commonMain to ${sourceSet.name}"
)
assertFalse(
model.isDependsOn(from = sourceSet, to = sourceSet),
"Expected false isDependsOn for same source set"
)
assertFalse(
sourceSet.isDependsOn(model, sourceSet),
"Expected false isDependsOn for same source set"
)
}
}
@Test
fun compilationDependsOnSourceSet() {
val commonMain = createKotlinSourceSet("commonMain")
val appleMain = createKotlinSourceSet("appleMain", declaredDependsOnSourceSets = setOf("commonMain"))
val macosMain = createKotlinSourceSet("macosMain", declaredDependsOnSourceSets = setOf("appleMain"))
val iosMain = createKotlinSourceSet("iosMain", declaredDependsOnSourceSets = setOf("appleMain"))
val metadataCompilation = createKotlinCompilation(defaultSourceSets = setOf(commonMain))
val macosMainCompilation = createKotlinCompilation(defaultSourceSets = setOf(macosMain))
val iosMainCompilation = createKotlinCompilation(defaultSourceSets = setOf(iosMain))
val model = createKotlinMPPGradleModel(sourceSets = setOf(commonMain, appleMain, macosMain, iosMain))
assertTrue(
model.compilationDependsOnSourceSet(iosMainCompilation, iosMain),
"Expected iosMainCompilation depending on iosMain"
)
assertTrue(
model.compilationDependsOnSourceSet(iosMainCompilation, appleMain),
"Expected iosMainCompilation depending on appleMain"
)
assertTrue(
model.compilationDependsOnSourceSet(iosMainCompilation, commonMain),
"Expected iosMainCompilation depending on commonMain"
)
assertFalse(
model.compilationDependsOnSourceSet(iosMainCompilation, macosMain),
"Expected iosMainCompilation *not* depending on macosMain"
)
assertTrue(
model.compilationDependsOnSourceSet(macosMainCompilation, macosMain),
"Expected macosMainCompilation depending on macosMain"
)
assertTrue(
model.compilationDependsOnSourceSet(macosMainCompilation, appleMain),
"Expected macosMainCompilation depending on appleMain"
)
assertTrue(
model.compilationDependsOnSourceSet(macosMainCompilation, commonMain),
"Expected macosMainCompilation depending on commonMain"
)
assertFalse(
model.compilationDependsOnSourceSet(macosMainCompilation, iosMain),
"Expected macosMainCompilation *not* depending on iosMain"
)
assertTrue(
model.compilationDependsOnSourceSet(metadataCompilation, commonMain),
"Expected metadataCompilation depending on commonMain"
)
assertFalse(
model.compilationDependsOnSourceSet(metadataCompilation, appleMain),
"Expected metadataCompilation *not* depending on appleMain"
)
assertFalse(
model.compilationDependsOnSourceSet(metadataCompilation, macosMain),
"Expected metadataCompilation *not* depending on macosMain"
)
assertFalse(
model.compilationDependsOnSourceSet(metadataCompilation, iosMain),
"Expected metadataCompilation *not* depending on iosMain"
)
}
@Test
fun getCompilations() {
val commonMain = createKotlinSourceSet("commonMain")
val appleMain = createKotlinSourceSet("appleMain", declaredDependsOnSourceSets = setOf("commonMain"))
val macosMain = createKotlinSourceSet("macosMain", declaredDependsOnSourceSets = setOf("appleMain"))
val metadataCompilation = createKotlinCompilation(defaultSourceSets = setOf(commonMain))
val macosMainCompilation = createKotlinCompilation(defaultSourceSets = setOf(macosMain))
val metadataTarget = createKotlinTarget("metadata", compilations = setOf(metadataCompilation))
val macosTarget = createKotlinTarget("macos", compilations = setOf(macosMainCompilation))
val model = createKotlinMPPGradleModel(
sourceSets = setOf(commonMain, appleMain, macosMain),
targets = setOf(metadataTarget, macosTarget)
)
assertEquals(
setOf(metadataCompilation, macosMainCompilation),
model.getCompilations(commonMain),
"Expected correct compilations for commonMain"
)
assertEquals(
setOf(macosMainCompilation), model.getCompilations(appleMain),
"Expected correct compilations for appleMain"
)
assertEquals(
setOf(macosMainCompilation), model.getCompilations(macosMain),
"Expected correct compilations for macosMain"
)
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2010-2021 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
internal fun createKotlinMPPGradleModel(
dependencyMap: Map<KotlinDependencyId, KotlinDependency> = emptyMap(),
sourceSets: Set<KotlinSourceSet> = emptySet(),
targets: Iterable<KotlinTarget> = emptyList(),
extraFeatures: ExtraFeatures = createExtraFeatures(),
kotlinNativeHome: String = ""
): KotlinMPPGradleModelImpl {
return KotlinMPPGradleModelImpl(
dependencyMap = dependencyMap,
sourceSetsByName = sourceSets.associateBy { it.name },
targets = targets.toList(),
extraFeatures = extraFeatures,
kotlinNativeHome = kotlinNativeHome
)
}
internal fun createExtraFeatures(
coroutinesState: String? = null,
isHmppEnabled: Boolean = false,
isNativeDependencyPropagationEnabled: Boolean = false
): ExtraFeaturesImpl {
return ExtraFeaturesImpl(
coroutinesState = coroutinesState,
isHMPPEnabled = isHmppEnabled,
isNativeDependencyPropagationEnabled = isNativeDependencyPropagationEnabled
)
}
internal fun createKotlinSourceSet(
name: String,
declaredDependsOnSourceSets: Set<String> = emptySet(),
allDependsOnSourceSets: Set<String> = declaredDependsOnSourceSets,
platforms: Set<KotlinPlatform> = emptySet(),
): KotlinSourceSetImpl = KotlinSourceSetImpl(
name = name,
languageSettings = KotlinLanguageSettingsImpl(
languageVersion = null,
apiVersion = null,
isProgressiveMode = false,
enabledLanguageFeatures = emptySet(),
experimentalAnnotationsInUse = emptySet(),
compilerPluginArguments = emptyArray(),
compilerPluginClasspath = emptySet(),
freeCompilerArgs = emptyArray()
),
sourceDirs = emptySet(),
resourceDirs = emptySet(),
dependencies = emptyArray(),
declaredDependsOnSourceSets = declaredDependsOnSourceSets,
allDependsOnSourceSets = allDependsOnSourceSets,
defaultActualPlatforms = KotlinPlatformContainerImpl().apply { pushPlatforms(platforms) },
)
internal fun createKotlinCompilation(
name: String = "main",
defaultSourceSets: Set<KotlinSourceSet> = emptySet(),
allSourceSets: Set<KotlinSourceSet> = emptySet(),
dependencies: Iterable<KotlinDependencyId> = emptyList(),
output: KotlinCompilationOutput = createKotlinCompilationOutput(),
arguments: KotlinCompilationArguments = createKotlinCompilationArguments(),
dependencyClasspath: Iterable<String> = emptyList(),
kotlinTaskProperties: KotlinTaskProperties = createKotlinTaskProperties(),
nativeExtensions: KotlinNativeCompilationExtensions? = null
): KotlinCompilationImpl {
return KotlinCompilationImpl(
name = name,
declaredSourceSets = defaultSourceSets,
allSourceSets = allSourceSets,
dependencies = dependencies.toList().toTypedArray(),
output = output,
arguments = arguments,
dependencyClasspath = dependencyClasspath.toList().toTypedArray(),
kotlinTaskProperties = kotlinTaskProperties,
nativeExtensions = nativeExtensions
)
}
internal fun createKotlinCompilationOutput(): KotlinCompilationOutputImpl {
return KotlinCompilationOutputImpl(
classesDirs = emptySet(),
effectiveClassesDir = null,
resourcesDir = null
)
}
internal fun createKotlinCompilationArguments(): KotlinCompilationArgumentsImpl {
return KotlinCompilationArgumentsImpl(
defaultArguments = emptyArray(),
currentArguments = emptyArray()
)
}
internal fun createKotlinTaskProperties(): KotlinTaskPropertiesImpl {
return KotlinTaskPropertiesImpl(
null, null, null, null
)
}
internal fun createKotlinTarget(
name: String,
platform: KotlinPlatform = KotlinPlatform.COMMON,
compilations: Iterable<KotlinCompilation> = emptyList()
): KotlinTargetImpl {
return KotlinTargetImpl(
name = name,
presetName = null,
disambiguationClassifier = null,
platform = platform,
compilations = compilations.toList(),
testRunTasks = emptyList(),
nativeMainRunTasks = emptyList(),
jar = null,
konanArtifacts = emptyList()
)
}