Group associated compilations into modules with a single module name
As associated compilations will see internals of each other, they now need to share the module name for the compiler to be able to correctly generate calls to the internals. Move `moduleName` to `KotlinCompilation`. Group associate compilations into modules in a DSU-like way, use a single module name for all of the compilations grouped into a module.
This commit is contained in:
+2
@@ -75,6 +75,8 @@ interface KotlinCompilation<out T : KotlinCommonOptions> : Named, HasAttributes,
|
||||
|
||||
override val relatedConfigurationNames: List<String>
|
||||
get() = super.relatedConfigurationNames + compileDependencyConfigurationName
|
||||
|
||||
val moduleName: String
|
||||
}
|
||||
|
||||
interface KotlinCompilationToRunnableFiles<T : KotlinCommonOptions> : KotlinCompilation<T> {
|
||||
|
||||
+2
-1
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileTaskData
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
|
||||
import org.jetbrains.kotlin.gradle.utils.archivePathCompatible
|
||||
import org.jetbrains.kotlin.gradle.utils.newTmpFile
|
||||
import org.jetbrains.kotlin.gradle.utils.relativeToRoot
|
||||
@@ -192,7 +193,7 @@ internal open class GradleCompilerRunner(protected val task: Task) {
|
||||
val target = taskData.compilation.target
|
||||
val module = IncrementalModuleEntry(
|
||||
project.path,
|
||||
compilation.moduleName,
|
||||
compilation.ownModuleName,
|
||||
project.buildDir,
|
||||
taskData.buildHistoryFile
|
||||
)
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.plugin.mpp.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
|
||||
/** This is a disjoint-set union-like approach to having a module name that is equal across associated compilations, as the compiler
|
||||
* now requires that to properly compile internal calls to friend classes. Associating a compilation with another one leads to their
|
||||
* disjoint sets being united, so their module names become equal, taken from the new leader compilation.
|
||||
*
|
||||
* TODO: once the compiler is able to correctly generate calls to internals in other modules, remove this logic.
|
||||
*/
|
||||
internal class KotlinCompilationsModuleGroups {
|
||||
private val moduleLeaderCompilationMap: MutableMap<KotlinCompilation<*>, KotlinCompilation<*>> =
|
||||
mutableMapOf()
|
||||
|
||||
fun getModuleLeader(compilation: KotlinCompilation<*>): KotlinCompilation<*> {
|
||||
if (compilation !in moduleLeaderCompilationMap) {
|
||||
moduleLeaderCompilationMap[compilation] = compilation
|
||||
}
|
||||
val leader = moduleLeaderCompilationMap.getValue(compilation)
|
||||
return when {
|
||||
leader == compilation -> leader
|
||||
else -> getModuleLeader(leader).also { moduleLeaderCompilationMap[compilation] = it }
|
||||
}
|
||||
}
|
||||
|
||||
fun unionModules(compilationA: KotlinCompilation<*>, compilationB: KotlinCompilation<*>) {
|
||||
val aLeader = getModuleLeader(compilationA)
|
||||
val bLeader = getModuleLeader(compilationB)
|
||||
|
||||
if (aLeader == bLeader)
|
||||
return
|
||||
|
||||
listOf(aLeader, bLeader).run {
|
||||
/** heuristically choose the new leader: choose `main` when possible, don't choose `*test*` when there's an alternative,
|
||||
* if that didn't work, choose the first name lexicographically */
|
||||
val newLeader = singleOrNull { it.name == KotlinCompilation.MAIN_COMPILATION_NAME }
|
||||
?: singleOrNull { it.name.contains("main", true) }
|
||||
?: singleOrNull { !it.name.contains("test", true) }
|
||||
?: minBy { it.name }!!
|
||||
|
||||
forEach { moduleLeaderCompilationMap[it] = newLeader }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXT_NAME = "kotlin.compilations.moduleGroups"
|
||||
|
||||
fun getModuleLeaderCompilation(compilation: KotlinCompilation<*>): KotlinCompilation<*> =
|
||||
getInstance(compilation.target.project).getModuleLeader(compilation)
|
||||
|
||||
fun unionModules(compilationA: KotlinCompilation<*>, compilationB: KotlinCompilation<*>) {
|
||||
getInstance(compilationA.target.project).unionModules(compilationA, compilationB)
|
||||
}
|
||||
|
||||
private fun getInstance(project: Project): KotlinCompilationsModuleGroups {
|
||||
val ext = project.extensions.getByType(ExtraPropertiesExtension::class.java)
|
||||
if (!ext.has(EXT_NAME)) {
|
||||
ext.set(EXT_NAME, KotlinCompilationsModuleGroups())
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return ext.get(EXT_NAME) as KotlinCompilationsModuleGroups
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-8
@@ -19,6 +19,7 @@ import org.gradle.api.tasks.TaskState
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.internal.KotlinCompilationsModuleGroups
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
@@ -191,14 +192,8 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
|
||||
override fun toString(): String = "compilation '$compilationName' ($target)"
|
||||
|
||||
internal val moduleName: String
|
||||
get() {
|
||||
val project = target.project
|
||||
val baseName = project.convention.findPlugin(BasePluginConvention::class.java)?.archivesBaseName
|
||||
?: project.name
|
||||
val suffix = if (compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) "" else "_$compilationName"
|
||||
return filterModuleName("$baseName$suffix")
|
||||
}
|
||||
override val moduleName: String
|
||||
get() = KotlinCompilationsModuleGroups.getModuleLeaderCompilation(this).takeIf { it != this }?.ownModuleName ?: ownModuleName
|
||||
|
||||
override fun associateWith(other: KotlinCompilation<*>) {
|
||||
require(other.target == target) { "Only associations between compilations of a single target are supported" }
|
||||
@@ -207,6 +202,7 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
_associateWith += other
|
||||
|
||||
addAssociateCompilationDependencies(other)
|
||||
KotlinCompilationsModuleGroups.unionModules(this, other)
|
||||
}
|
||||
|
||||
protected open fun addAssociateCompilationDependencies(other: KotlinCompilation<*>) {
|
||||
@@ -233,6 +229,15 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
get() = Collections.unmodifiableSet(_associateWith)
|
||||
}
|
||||
|
||||
internal val KotlinCompilation<*>.ownModuleName: String
|
||||
get() {
|
||||
val project = target.project
|
||||
val baseName = project.convention.findPlugin(BasePluginConvention::class.java)?.archivesBaseName
|
||||
?: project.name
|
||||
val suffix = if (compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) "" else "_$compilationName"
|
||||
return filterModuleName("$baseName$suffix")
|
||||
}
|
||||
|
||||
internal val KotlinCompilation<*>.associateWithTransitiveClosure: Iterable<KotlinCompilation<*>>
|
||||
get() = mutableSetOf<KotlinCompilation<*>>().apply {
|
||||
fun visit(other: KotlinCompilation<*>) {
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
|
||||
args.apply { fillDefaultValues() }
|
||||
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
|
||||
|
||||
args.moduleName = friendTask?.moduleName ?: this@KotlinCompileCommon.moduleName
|
||||
args.moduleName = this@KotlinCompileCommon.moduleName
|
||||
|
||||
if (defaultsOnly) return
|
||||
|
||||
|
||||
+4
-2
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
|
||||
import org.jetbrains.kotlin.gradle.logging.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName
|
||||
import org.jetbrains.kotlin.gradle.report.BuildReportMode
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import org.jetbrains.kotlin.gradle.utils.isParentOf
|
||||
@@ -399,7 +401,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
||||
args.apply { fillDefaultValues() }
|
||||
super.setupCompilerArgs(args, defaultsOnly = defaultsOnly, ignoreClasspathResolutionErrors = ignoreClasspathResolutionErrors)
|
||||
|
||||
args.moduleName = friendTask?.moduleName ?: moduleName
|
||||
args.moduleName = taskData.compilation.moduleName
|
||||
logger.kotlinDebug { "args.moduleName = ${args.moduleName}" }
|
||||
|
||||
args.friendPaths = friendPaths.value
|
||||
@@ -540,7 +542,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
|
||||
get() = kotlinOptionsImpl
|
||||
|
||||
private val defaultOutputFile: File
|
||||
get() = File(destinationDir, "$moduleName.js")
|
||||
get() = File(destinationDir, "${taskData.compilation.ownModuleName}.js")
|
||||
|
||||
@Suppress("unused")
|
||||
@get:OutputFile
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.plugin.mpp.internal
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.MockKotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.MockKotlinSourceSet
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class KotlinCompilationsModuleGroupsTest {
|
||||
private lateinit var instance: KotlinCompilationsModuleGroups
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
instance = KotlinCompilationsModuleGroups()
|
||||
}
|
||||
|
||||
private fun compilation(name: String) = MockKotlinCompilation(name, MockKotlinSourceSet(name))
|
||||
|
||||
@Test
|
||||
fun testIdentityAsModuleLeaderForNewCompilation() {
|
||||
val a = compilation("a")
|
||||
assertEquals(a, instance.getModuleLeader(a))
|
||||
}
|
||||
|
||||
private fun assertLeader(leader: KotlinCompilation<*>, vararg ofCompilations: KotlinCompilation<*>) {
|
||||
val leadersForModules = ofCompilations.map { instance.getModuleLeader(it) }
|
||||
assertEquals(leadersForModules.map { leader }, leadersForModules)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testModulesMerged() {
|
||||
val (a, b, c, d) = listOf("a", "b", "c", "d").map(this::compilation)
|
||||
|
||||
// Union all but one:
|
||||
instance.unionModules(b, c)
|
||||
instance.unionModules(a, b)
|
||||
assertLeader(a, a, b, c)
|
||||
assertLeader(d, d)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMainPreferredAsLeader() {
|
||||
val (aMain, aTest) = listOf("aMain", "aTest").map(this::compilation)
|
||||
val (zMain, yTest) = listOf("zMain", "yTest").map(this::compilation)
|
||||
val (xMain, xBenchmark, xTest) = listOf("xMain", "xBenchmark", "xTest").map(this::compilation)
|
||||
|
||||
instance.unionModules(aMain, aTest)
|
||||
assertLeader(aMain, aMain, aTest)
|
||||
|
||||
instance.unionModules(yTest, zMain)
|
||||
assertLeader(zMain, zMain, yTest)
|
||||
|
||||
instance.unionModules(xBenchmark, xTest)
|
||||
instance.unionModules(xBenchmark, xMain)
|
||||
assertLeader(xMain, xMain, xBenchmark, xTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonTestPreferredAsLeader() {
|
||||
val (aBenchmark, aPerformanceTest, aTest) = listOf("aBenchmark", "aPerformanceTest", "aTest").map(this::compilation)
|
||||
instance.unionModules(aPerformanceTest, aTest)
|
||||
instance.unionModules(aTest, aBenchmark)
|
||||
assertLeader(aBenchmark, aBenchmark, aPerformanceTest, aTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unionModuleWithSelf() {
|
||||
val compilation = compilation("compilation")
|
||||
instance.unionModules(compilation, compilation)
|
||||
assertLeader(compilation, compilation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnionModulesTwice() {
|
||||
val (a, b) = listOf("a", "b").map(this::compilation)
|
||||
instance.unionModules(a, b)
|
||||
instance.unionModules(a, b)
|
||||
assertLeader(a, a, b)
|
||||
}
|
||||
}
|
||||
+1
@@ -316,6 +316,7 @@ class MockKotlinCompilation(
|
||||
override fun attributes(configure: AttributeContainer.() -> Unit) = throw UnsupportedOperationException()
|
||||
override fun attributes(configure: Closure<*>) = throw UnsupportedOperationException()
|
||||
override val compileAllTaskName: String get() = throw UnsupportedOperationException()
|
||||
override val moduleName: String get() = throw UnsupportedOperationException()
|
||||
//endregion
|
||||
|
||||
override fun toString(): String = "compilation '${name}'"
|
||||
|
||||
Reference in New Issue
Block a user