Re-structure KotlinMPPGradleProjectResolver.populateModuleDependencies
This commit is contained in:
+60
-325
@@ -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
-2
@@ -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)
|
||||
|
||||
+21
@@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -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))
|
||||
}
|
||||
}
|
||||
+86
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+234
@@ -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>
|
||||
}
|
||||
+22
@@ -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())
|
||||
+89
@@ -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)
|
||||
+42
@@ -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"
|
||||
}
|
||||
+167
@@ -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() }
|
||||
+21
@@ -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"))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user