Common klib support in the Kotlin Gradle plugin (KT-32677)

Refactor the Kotlin/Native compilations and tasks to support
Kotlin/Native-shared source sets compilation within the metadata target.

Provide the feature flag kotlin.mpp.enableCommonKlibs that enables
the following behavior:
* compilation of Kotlin/Native-shared source sets to a klib using
the Kotlin/Native compiler.
* compilation of common source sets (not just Kotlin/Native) into a
klib rather than *.kotlin_metadata

Issue #KT-32677 Fixed
This commit is contained in:
Sergey Igushkin
2020-01-20 14:28:46 +03:00
parent dd9ac39b65
commit 970ee4539b
35 changed files with 541 additions and 196 deletions
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="sergey.igushkin">
<words>
<w>klib</w>
</words>
</dictionary>
</component>
@@ -33,6 +33,8 @@ interface KotlinCompilation<out T : KotlinCommonOptions> : Named, HasAttributes,
val allKotlinSourceSets: Set<KotlinSourceSet>
val defaultSourceSetName: String
val defaultSourceSet: KotlinSourceSet
fun defaultSourceSet(configure: KotlinSourceSet.() -> Unit)
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.internals.MULTIPLATFORM_PROJECT_METADATA_FILE_NAME
import org.jetbrains.kotlin.gradle.internals.parseKotlinSourceSetMetadataFromXml
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.SourceSetMetadataLayout
import org.jetbrains.kotlin.gradle.plugin.mpp.ModuleDependencyIdentifier
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
import org.jetbrains.kotlin.gradle.util.checkedReplace
@@ -374,7 +375,8 @@ class HierarchicalMppIT : BaseGradleIT() {
pairs.map {
ModuleDependencyIdentifier(it.first, it.second)
}.toSet()
}
},
sourceSetBinaryLayout = sourceSetModuleDependencies.mapValues { SourceSetMetadataLayout.METADATA }
)
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.konan.target.HostManager
import org.junit.Assume
import kotlin.test.Ignore
import kotlin.test.Test
/** FIXME (sergey.igushkin): please enable these tests back as soon as the Kotlin/Native version that is bundled with the
* Kotlin distribution supports compilation to klib and targetless klibs.
*/
@Ignore
class KlibBasedMppIT : BaseGradleIT() {
override val defaultGradleVersion = GradleVersionRequired.AtLeast("6.0")
@Test
fun testBuildWithProjectDependency() = testBuildWithDependency {
gradleBuildScript().appendText("\n" + """
dependencies {
commonMainImplementation(project("$embeddedModuleName"))
}
""".trimIndent())
}
@Test
fun testBuildWithPublishedDependency() = testBuildWithDependency {
build(":$embeddedModuleName:publish") {
assertSuccessful()
}
gradleBuildScript().appendText("\n" + """
repositories {
maven("${'$'}rootDir/repo")
}
dependencies {
commonMainImplementation("com.example:$embeddedModuleName:1.0")
}
""".trimIndent())
// prevent Gradle from linking the above dependency to the project:
gradleBuildScript(embeddedModuleName).appendText("\ngroup = \"some.other.group\"")
}
private val embeddedModuleName = "project-dep"
private fun testBuildWithDependency(configureDependency: Project.() -> Unit) = with(Project("common-klib-lib-and-app")) {
Assume.assumeTrue(HostManager.hostIsMac)
embedProject(Project("common-klib-lib-and-app"), renameTo = embeddedModuleName)
gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl)
projectDir.resolve(embeddedModuleName + "/src").walkTopDown().filter { it.extension == "kt" }.forEach { file ->
file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep") }
}
configureDependency()
projectDir.resolve("src/commonMain/kotlin/LibUsage.kt").appendText("\n" + """
package com.h0tk3y.hmpp.klib.demo.test
import com.projectdep.LibCommonMainExpect as ProjectDepExpect
private fun useProjectDep() {
ProjectDepExpect()
}
""".trimIndent())
projectDir.resolve("src/iosMain/kotlin/LibIosMainUsage.kt").appendText("\n" + """
package com.h0tk3y.hmpp.klib.demo.test
import com.projectdep.libIosMainFun as libFun
private fun useProjectDep() {
libFun()
}
""".trimIndent())
val tasksToExecute = listOf(
":compileJvmAndJsMainKotlinMetadata",
":compileIosMainKotlinMetadata"
)
build("assemble") {
assertSuccessful()
assertTasksExecuted(*tasksToExecute.toTypedArray())
}
}
}
@@ -80,7 +80,10 @@ internal fun BaseGradleIT.transformProjectWithPluginsDsl(
}
result.projectDir.walkTopDown()
.filter { it.isFile && (it.name == "build.gradle" || it.name == "build.gradle.kts") }
.filter {
it.isFile && (it.name == "build.gradle" || it.name == "build.gradle.kts" ||
it.name == "settings.gradle" || it.name == "settings.gradle.kts")
}
.forEach { buildGradle ->
buildGradle.modify(::transformBuildScriptWithPluginsDsl)
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.createTempDir
import org.jetbrains.kotlin.gradle.util.isWindows
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.gradle.util.testResolveAllConfigurations
import org.junit.Test
@@ -292,15 +294,23 @@ class VariantAwareDependenciesIT : BaseGradleIT() {
}
internal fun BaseGradleIT.Project.embedProject(other: BaseGradleIT.Project) {
internal fun BaseGradleIT.Project.embedProject(other: BaseGradleIT.Project, renameTo: String? = null) {
setupWorkingDir()
other.setupWorkingDir()
other.testCase.apply {
val gradleBuildScript = other.gradleBuildScript()
if (gradleBuildScript.extension == "kts") {
gradleBuildScript.modify { it.replace(".version(\"$PLUGIN_MARKER_VERSION_PLACEHOLDER\")", "") }
val tempDir = createTempDir(if (isWindows) "" else "BaseGradleIT")
val embeddedModuleName = renameTo ?: other.projectName
try {
other.projectDir.copyRecursively(tempDir)
tempDir.copyRecursively(projectDir.resolve(embeddedModuleName))
} finally {
check(tempDir.deleteRecursively())
}
testCase.apply {
gradleSettingsScript().appendText("\ninclude(\"$embeddedModuleName\")")
val embeddedBuildScript = gradleBuildScript(embeddedModuleName)
if (embeddedBuildScript.extension == "kts") {
embeddedBuildScript.modify { it.replace(".version(\"$PLUGIN_MARKER_VERSION_PLACEHOLDER\")", "") }
}
}
other.projectDir.copyRecursively(projectDir.resolve(other.projectName))
projectDir.resolve("settings.gradle").appendText("\ninclude '${other.projectName}'")
}
@@ -0,0 +1,70 @@
plugins {
kotlin("multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "com.example"
version = "1.0"
repositories {
mavenLocal()
jcenter()
}
kotlin {
jvm()
js()
ios()
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val jvmAndJsMain by creating {
dependsOn(commonMain)
}
val jvmMain by getting {
dependsOn(jvmAndJsMain)
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
val jsMain by getting {
dependsOn(jvmAndJsMain)
dependencies {
implementation(kotlin("stdlib-js"))
}
}
}
}
publishing {
repositories {
maven("$rootDir/repo")
}
}
tasks {
val skipCompilationOfTargets = setOf(
"iosArm64",
"iosX64"
)
all {
val target = name.removePrefix("compileKotlin").decapitalize()
if (target in skipCompilationOfTargets) {
actions.clear()
doLast {
val destinationFile = project.buildDir.resolve("classes/kotlin/$target/main/${project.name}.klib")
destinationFile.parentFile.mkdirs()
println("Writing a dummy klib to $destinationFile")
destinationFile.createNewFile()
}
}
}
}
@@ -0,0 +1,2 @@
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCommonKlibs=true
@@ -0,0 +1,6 @@
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
}
}
@@ -0,0 +1,16 @@
package com.h0tk3y.hmpp.klib.demo
interface LibCommonMainIface
expect class LibCommonMainExpect() : LibCommonMainIface {
fun libCommonMainExpectFun(): Unit
}
fun libCommonMainTopLevelFun(): Int {
println("commonMainTopLevelFun")
return 2
}
fun main() {
}
@@ -0,0 +1,17 @@
package com.h0tk3y.hmpp.klib.demo
import kotlinx.cinterop.CArrayPointer
actual class LibCommonMainExpect : LibCommonMainIface {
actual fun libCommonMainExpectFun(): Unit {
println("actualized in iosMain")
libCommonMainTopLevelFun()
println(CArrayPointer::class)
}
fun additionalFunInIosActual() {
println("additional fun in lib iosMain")
}
}
fun libIosMainFun(): LibCommonMainIface = LibCommonMainExpect()
@@ -0,0 +1,12 @@
package com.h0tk3y.hmpp.klib.demo
actual class LibCommonMainExpect : LibCommonMainIface {
actual fun libCommonMainExpectFun(): Unit {
println("actualized in jvmAndJsMain")
libCommonMainTopLevelFun()
}
fun additionalFunInJvmAndJsActual() {
println("additional fun from jvmAndJsMain")
}
}
@@ -0,0 +1,5 @@
package com.h0tk3y.hmpp.klib.demo
fun main() {
libCommonMainTopLevelFun()
}
@@ -52,29 +52,16 @@ val KOTLIN_DSL_NAME = "kotlin"
val KOTLIN_JS_DSL_NAME = "kotlin2js"
val KOTLIN_OPTIONS_DSL_NAME = "kotlinOptions"
internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
val project: Project,
val tasksProvider: KotlinTasksProvider,
val taskDescription: String,
val kotlinCompilation: AbstractKotlinCompilation<*>
abstract class KotlinCompilationProcessor<out T : AbstractCompile>(
open val kotlinCompilation: AbstractKotlinCompilation<*>
) {
protected abstract fun doTargetSpecificProcessing()
protected val logger = Logging.getLogger(this.javaClass)!!
abstract val kotlinTask: TaskProvider<out T>
abstract fun run()
protected val sourceSetName: String = kotlinCompilation.compilationName
protected val project: Project
get() = kotlinCompilation.target.project
protected val kotlinTask: TaskProvider<out T> = registerKotlinCompileTask()
protected val javaSourceSet: SourceSet?
get() =
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet
?: kotlinCompilation.target.let {
if (it is KotlinJvmTarget && it.withJavaEnabled)
project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets.maybeCreate(kotlinCompilation.name)
else null
}
private val defaultKotlinDestinationDir: File
protected val defaultKotlinDestinationDir: File
get() {
val kotlinExt = project.kotlinExtension
val targetSubDirectory =
@@ -84,6 +71,28 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
return File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
}
}
internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
val tasksProvider: KotlinTasksProvider,
val taskDescription: String,
kotlinCompilation: AbstractKotlinCompilation<*>
) : KotlinCompilationProcessor<T>(kotlinCompilation) {
protected abstract fun doTargetSpecificProcessing()
protected val logger = Logging.getLogger(this.javaClass)!!
protected val sourceSetName: String = kotlinCompilation.compilationName
override val kotlinTask: TaskProvider<out T> = registerKotlinCompileTask()
protected val javaSourceSet: SourceSet?
get() =
(kotlinCompilation as? KotlinWithJavaCompilation<*>)?.javaSourceSet
?: kotlinCompilation.target.let {
if (it is KotlinJvmTarget && it.withJavaEnabled)
project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets.maybeCreate(kotlinCompilation.name)
else null
}
private fun registerKotlinCompileTask(): TaskProvider<out T> {
val name = kotlinCompilation.compileKotlinTaskName
@@ -103,7 +112,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
return result
}
open fun run() {
override fun run() {
addKotlinDirectoriesToJavaSourceSet()
doTargetSpecificProcessing()
@@ -165,12 +174,11 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
}
internal class Kotlin2JvmSourceSetProcessor(
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: AbstractKotlinCompilation<*>,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<KotlinCompile>(
project, tasksProvider, "Compiles the $kotlinCompilation.", kotlinCompilation
tasksProvider, "Compiles the $kotlinCompilation.", kotlinCompilation
) {
override fun doRegisterTask(
project: Project,
@@ -245,13 +253,11 @@ internal fun KotlinCompilationOutput.addClassesDir(classesDirProvider: () -> Fil
}
internal class Kotlin2JsSourceSetProcessor(
project: Project,
tasksProvider: KotlinTasksProvider,
kotlinCompilation: AbstractKotlinCompilation<*>,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<Kotlin2JsCompile>(
project, tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.",
kotlinCompilation = kotlinCompilation
tasksProvider, taskDescription = "Compiles the Kotlin sources in $kotlinCompilation to JavaScript.", kotlinCompilation = kotlinCompilation
) {
override fun doRegisterTask(
project: Project,
@@ -311,13 +317,11 @@ internal class Kotlin2JsSourceSetProcessor(
}
internal class KotlinCommonSourceSetProcessor(
project: Project,
compilation: AbstractKotlinCompilation<*>,
tasksProvider: KotlinTasksProvider,
private val kotlinPluginVersion: String
) : KotlinSourceSetProcessor<KotlinCompileCommon>(
project, tasksProvider, taskDescription = "Compiles the kotlin sources in $compilation to Metadata.",
kotlinCompilation = compilation
tasksProvider, taskDescription = "Compiles the kotlin sources in $compilation to Metadata.", kotlinCompilation = compilation
) {
override fun doTargetSpecificProcessing() {
project.tasks.findByName(kotlinCompilation.compileAllTaskName)!!.dependsOn(kotlinTask)
@@ -581,7 +585,7 @@ internal open class KotlinPlugin(
}
override fun buildSourceSetProcessor(project: Project, compilation: AbstractKotlinCompilation<*>, kotlinPluginVersion: String) =
Kotlin2JvmSourceSetProcessor(project, tasksProvider, compilation, kotlinPluginVersion)
Kotlin2JvmSourceSetProcessor(tasksProvider, compilation, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget<KotlinJvmOptions>(project, KotlinPlatformType.jvm, targetName).apply {
@@ -609,7 +613,7 @@ internal open class KotlinCommonPlugin(
compilation: AbstractKotlinCompilation<*>,
kotlinPluginVersion: String
): KotlinSourceSetProcessor<*> =
KotlinCommonSourceSetProcessor(project, compilation, tasksProvider, kotlinPluginVersion)
KotlinCommonSourceSetProcessor(compilation, tasksProvider, kotlinPluginVersion)
override fun apply(project: Project) {
val target = KotlinWithJavaTarget<KotlinMultiplatformCommonOptions>(project, KotlinPlatformType.common, targetName)
@@ -634,7 +638,7 @@ internal open class Kotlin2JsPlugin(
kotlinPluginVersion: String
): KotlinSourceSetProcessor<*> =
Kotlin2JsSourceSetProcessor(
project, tasksProvider, compilation, kotlinPluginVersion
tasksProvider, compilation, kotlinPluginVersion
)
override fun apply(project: Project) {
@@ -94,6 +94,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
val enableGranularSourceSetsMetadata: Boolean?
get() = booleanProperty("kotlin.mpp.enableGranularSourceSetsMetadata")
val enableCommonKlibs: Boolean?
get() = booleanProperty("kotlin.mpp.enableCommonKlibs")
val ignoreDisabledNativeTargets: Boolean?
get() = booleanProperty(DisabledNativeTargetsReporter.DISABLE_WARNING_PROPERTY_NAME)
@@ -63,7 +63,9 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
cleanTask.delete(kotlinCompilation.output.allOutputs)
}
protected open fun setupCompilationDependencyFiles(project: Project, compilation: KotlinCompilation<KotlinCommonOptions>) {
protected open fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
val project = compilation.target.project
compilation.compileDependencyFiles = project.configurations.maybeCreate(compilation.compileDependencyConfigurationName)
if (compilation is KotlinCompilationToRunnableFiles) {
compilation.runtimeDependencyFiles = project.configurations.maybeCreate(compilation.runtimeDependencyConfigurationName)
@@ -76,7 +78,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
target.compilations.all {
project.registerOutputsForStaleOutputCleanup(it)
setupCompilationDependencyFiles(project, it)
setupCompilationDependencyFiles(it)
}
if (createTestCompilation) {
@@ -322,7 +324,7 @@ abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompil
createDefaultSourceSets,
createTestCompilation
) {
internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinSourceSetProcessor<*>
internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinCompilationProcessor<*>
override fun configureCompilations(target: KotlinTargetType) {
super.configureCompilations(target)
@@ -336,7 +338,7 @@ abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompil
}
/** The implementations are expected to create a [Jar] task under the name [KotlinTarget.artifactsTaskName] of the [target]. */
protected open fun createJarTasks(target: KotlinOnlyTarget<KotlinCompilationType>) {
protected open fun createJarTasks(target: KotlinTargetType) {
val result = target.project.tasks.create(target.artifactsTaskName, Jar::class.java)
result.description = "Assembles a jar archive containing the main classes."
result.group = BasePlugin.BUILD_GROUP
@@ -8,13 +8,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_SOURCE_SET_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME
import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope
import java.io.File
import java.util.*
@@ -372,15 +368,22 @@ private class JarArtifactMppDependencyMetadataExtractor(
val resultFiles = mutableMapOf<String, FileCollection>()
val projectStructureMetadata = checkNotNull(getProjectStructureMetadata()) {
"can't extract metadata from a module without project structure metadata"
}
ZipFile(artifactJar).use { zip ->
val entriesBySourceSet = zip.entries().asSequence()
.groupBy { it.name.substringBefore("/") }
.filterKeys { it in chooseSourceSetsByNames }
entriesBySourceSet.forEach { (sourceSetName, entries) ->
// TODO: once IJ supports non-JAR metadata dependencies, extraact to a directory, not a JAR
// TODO: once IJ supports non-JAR metadata dependencies, extract to a directory, not a JAR
// Also, if both IJ and the CLI compiler can read metadata from a path inside a JAR, then no operations will be needed
val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.jar")
val extension =
projectStructureMetadata.sourceSetBinaryLayout[sourceSetName]?.archiveExtension
?: SourceSetMetadataLayout.METADATA.archiveExtension
val extractToJarFile = transformedModuleRoot.resolve("$moduleString-$sourceSetName.$extension")
resultFiles[sourceSetName] = project.files(extractToJarFile)
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
@@ -12,22 +13,12 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon
interface KotlinMetadataCompilation<T : KotlinCommonOptions> : KotlinCompilation<T>
class KotlinCommonCompilation(
target: KotlinTarget,
name: String
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name) {
) : AbstractKotlinCompilation<KotlinMultiplatformCommonOptions>(target, name), KotlinMetadataCompilation<KotlinMultiplatformCommonOptions> {
override val compileKotlinTask: KotlinCompileCommon
get() = super.compileKotlinTask as KotlinCompileCommon
private val commonSourceSetName by lazy {
when (compilationName) {
// Historically, a metadata target has a main compilation. We keep using it to compile just the commonMain source set:
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME
// All other common source sets are compiled by compilations named according to the source sets:
else -> compilationName
}
}
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(commonSourceSetName)
}
@@ -14,7 +14,7 @@ interface KotlinCompilationFactory<T : KotlinCompilation<*>> : NamedDomainObject
}
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
val target: KotlinOnlyTarget<*>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
override val itemClass: Class<KotlinCommonCompilation>
get() = KotlinCommonCompilation::class.java
@@ -22,7 +22,8 @@ import javax.inject.Inject
internal const val COMMON_MAIN_ELEMENTS_CONFIGURATION_NAME = "commonMainMetadataElements"
open class KotlinMetadataTarget @Inject constructor(project: Project) :
KotlinOnlyTarget<KotlinCommonCompilation>(project, KotlinPlatformType.common) {
KotlinOnlyTarget<AbstractKotlinCompilation<*>>(project, KotlinPlatformType.common) {
override val kotlinComponents: Set<KotlinTargetComponent> by lazy {
if (!project.isKotlinGranularMetadataEnabled)
super.kotlinComponents
@@ -257,11 +257,11 @@ class KotlinMultiplatformPlugin(
targets.all { target ->
target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)?.let { mainCompilation ->
sourceSets.findByName(mainCompilation.defaultSourceSetName)?.dependsOn(production)
mainCompilation.defaultSourceSet.takeIf { it != production }?.dependsOn(production)
}
target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME)?.let { testCompilation ->
sourceSets.findByName(testCompilation.defaultSourceSetName)?.dependsOn(test)
testCompilation.defaultSourceSet.takeIf { it != test }?.dependsOn(test)
}
KotlinBuildStatsService.getInstance()?.report(StringMetrics.MPP_PLATFORMS, target.targetName)
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.targets.metadata.getPublishedPlatformCompilations
import org.w3c.dom.Document
import org.w3c.dom.Element
@@ -21,6 +23,28 @@ data class ModuleDependencyIdentifier(
val moduleId: String
)
sealed class SourceSetMetadataLayout(
@get:Input
val name: String,
@get:Internal
val archiveExtension: String
) {
object METADATA : SourceSetMetadataLayout("metadata", "jar")
object KLIB : SourceSetMetadataLayout("klib", "klib")
companion object {
private val values = listOf(METADATA, KLIB)
fun byName(name: String): SourceSetMetadataLayout? = values.firstOrNull { it.name == name }
fun chooseForProducingProject(project: Project) =
if (PropertiesProvider(project.rootProject).enableCommonKlibs == true)
KLIB
else
METADATA
}
}
data class KotlinProjectStructureMetadata(
@Input
val sourceSetNamesByVariantName: Map<String, Set<String>>,
@@ -28,11 +52,14 @@ data class KotlinProjectStructureMetadata(
@Input
val sourceSetsDependsOnRelation: Map<String, Set<String>>,
@Nested
val sourceSetBinaryLayout: Map<String, SourceSetMetadataLayout>,
@Internal
val sourceSetModuleDependencies: Map<String, Set<ModuleDependencyIdentifier>>,
@Input
val formatVersion: String = FORMAT_VERSION_0_1
val formatVersion: String = FORMAT_VERSION_0_2
) {
@Suppress("UNUSED") // Gradle input
@get:Input
@@ -41,6 +68,7 @@ data class KotlinProjectStructureMetadata(
companion object {
internal const val FORMAT_VERSION_0_1 = "0.1"
internal const val FORMAT_VERSION_0_2 = "0.2"
}
}
@@ -63,6 +91,9 @@ internal fun buildKotlinProjectStructureMetadata(project: Project): KotlinProjec
sourceSet.name to project.configurations.getByName(sourceSet.apiConfigurationName).allDependencies.map {
ModuleDependencyIdentifier(it.group.orEmpty(), it.name)
}.toSet()
},
sourceSetBinaryLayout = sourceSetsWithMetadataCompilations.keys.associate { sourceSet ->
sourceSet.name to SourceSetMetadataLayout.chooseForProducingProject(project)
}
)
}
@@ -96,6 +127,9 @@ internal fun KotlinProjectStructureMetadata.toXmlDocument(): Document {
sourceSetModuleDependencies[sourceSet].orEmpty().forEach { moduleDependency ->
textNode("moduleDependency", moduleDependency.groupId + ":" + moduleDependency.moduleId)
}
sourceSetBinaryLayout[sourceSet]?.let { binaryLayout ->
textNode("binaryLayout", binaryLayout.name)
}
}
}
}
@@ -123,6 +157,7 @@ internal fun parseKotlinSourceSetMetadataFromXml(document: Document): KotlinProj
val sourceSetDependsOnRelation = mutableMapOf<String, Set<String>>()
val sourceSetModuleDependencies = mutableMapOf<String, Set<ModuleDependencyIdentifier>>()
val sourceSetBinaryLayout = mutableMapOf<String, SourceSetMetadataLayout>()
val sourceSetsNode = projectStructureNode.getElementsByTagName("sourceSets").item(0) ?: return null
@@ -139,6 +174,11 @@ internal fun parseKotlinSourceSetMetadataFromXml(document: Document): KotlinProj
val (groupId, moduleId) = node.textContent.split(":")
moduleDependencies.add(ModuleDependencyIdentifier(groupId, moduleId))
}
"binaryLayout" -> {
SourceSetMetadataLayout.byName(node.textContent)?.let { binaryLayout ->
sourceSetBinaryLayout[sourceSetName] = binaryLayout
}
}
}
}
@@ -149,6 +189,7 @@ internal fun parseKotlinSourceSetMetadataFromXml(document: Document): KotlinProj
return KotlinProjectStructureMetadata(
sourceSetsByVariant,
sourceSetDependsOnRelation,
sourceSetBinaryLayout,
sourceSetModuleDependencies,
formatVersion
)
@@ -36,9 +36,6 @@ internal fun KotlinCompilation<*>.composeName(prefix: String? = null, suffix: St
return lowerCamelCaseName(prefix, targetNamePart, compilationNamePart, suffix)
}
internal val KotlinCompilation<*>.defaultSourceSetName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName)
abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
target: KotlinTarget,
override val compilationName: String
@@ -70,6 +67,16 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
override val allKotlinSourceSets: Set<KotlinSourceSet>
get() = kotlinSourceSets.flatMapTo(mutableSetOf()) { it.getSourceSetHierarchy() }
override val defaultSourceSetName: String
get() = lowerCamelCaseName(
target.disambiguationClassifier.takeIf { target !is KotlinMetadataTarget },
when {
compilationName == KotlinCompilation.MAIN_COMPILATION_NAME && target is KotlinMetadataTarget ->
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME // corner case: main compilation of the metadata target compiles commonMain
else -> compilationName
}
)
override val defaultSourceSet: KotlinSourceSet
get() = target.project.kotlinExtension.sourceSets.getByName(defaultSourceSetName)
@@ -5,14 +5,12 @@
package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinOnlyTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTestsConfigurator
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.testing.testTaskName
@@ -47,7 +45,7 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JsSourceSetProcessor(compilation.target.project, tasksProvider, compilation, kotlinPluginVersion)
return Kotlin2JsSourceSetProcessor(tasksProvider, compilation, kotlinPluginVersion)
}
override fun configureCompilations(target: KotlinJsTarget) {
@@ -71,6 +71,6 @@ class KotlinJvmTargetConfigurator(kotlinPluginVersion: String) :
override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return Kotlin2JvmSourceSetProcessor(compilation.target.project, tasksProvider, compilation, kotlinPluginVersion)
return Kotlin2JvmSourceSetProcessor(tasksProvider, compilation, kotlinPluginVersion)
}
}
@@ -35,7 +35,7 @@ class KotlinJvmWithJavaTargetPreset(
}
AbstractKotlinPlugin.configureTarget(target) { compilation ->
Kotlin2JvmSourceSetProcessor(project, KotlinTasksProvider(name), compilation, kotlinPluginVersion)
Kotlin2JvmSourceSetProcessor(KotlinTasksProvider(name), compilation, kotlinPluginVersion)
}
target.compilations.getByName("test").run {
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.sources.*
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.tasks.locateTask
@@ -38,7 +39,7 @@ internal val Project.isKotlinGranularMetadataEnabled: Boolean
get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget>(
KotlinOnlyTargetConfigurator<AbstractKotlinCompilation<*>, KotlinMetadataTarget>(
createDefaultSourceSets = false,
createTestCompilation = false,
kotlinPluginVersion = kotlinPluginVersion
@@ -59,7 +60,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
}
}
private val KotlinOnlyTarget<KotlinCommonCompilation>.apiElementsConfiguration: Configuration
private val KotlinOnlyTarget<*>.apiElementsConfiguration: Configuration
get() = project.configurations.getByName(apiElementsConfigurationName)
override fun configureTarget(target: KotlinMetadataTarget) {
@@ -67,7 +68,6 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
if (target.project.isKotlinGranularMetadataEnabled) {
KotlinBuildStatsService.getInstance()?.report(BooleanMetrics.ENABLED_HMPP, true)
target as KotlinMetadataTarget
createMergedAllSourceSetsConfigurations(target)
@@ -81,20 +81,40 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
}
}
override fun setupCompilationDependencyFiles(project: Project, compilation: KotlinCompilation<KotlinCommonOptions>) {
override fun setupCompilationDependencyFiles(compilation: KotlinCompilation<KotlinCommonOptions>) {
val project = compilation.target.project
/** See [createTransformedMetadataClasspath] and its usage. */
if (project.isKotlinGranularMetadataEnabled)
compilation.compileDependencyFiles = project.files()
else
super.setupCompilationDependencyFiles(project, compilation)
super.setupCompilationDependencyFiles(compilation)
}
override fun buildCompilationProcessor(compilation: KotlinCommonCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
return KotlinCommonSourceSetProcessor(compilation.target.project, compilation, tasksProvider, kotlinPluginVersion)
override fun buildCompilationProcessor(compilation: AbstractKotlinCompilation<*>): KotlinCompilationProcessor<*> = when (compilation) {
is KotlinCommonCompilation -> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
KotlinCommonSourceSetProcessor(compilation, tasksProvider, kotlinPluginVersion)
}
is KotlinSharedNativeCompilation -> NativeSharedCompilationProcessor(compilation)
else -> error("unsupported compilation type ${compilation::class.qualifiedName}")
}
override fun createJarTasks(target: KotlinOnlyTarget<KotlinCommonCompilation>) {
private inner class NativeSharedCompilationProcessor(
override val kotlinCompilation: KotlinSharedNativeCompilation
) : KotlinCompilationProcessor<KotlinNativeCompile>(kotlinCompilation) {
private val nativeTargetConfigurator = KotlinNativeTargetConfigurator<KotlinNativeTarget>(kotlinPluginVersion)
override val kotlinTask: TaskProvider<out KotlinNativeCompile> =
with(nativeTargetConfigurator) {
project.createKlibCompilationTask(kotlinCompilation)
}
override fun run() = Unit
}
override fun createJarTasks(target: KotlinMetadataTarget) {
super.createJarTasks(target)
if (target.project.isKotlinGranularMetadataEnabled) {
@@ -130,7 +150,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
val publishedCommonSourceSets: Set<KotlinSourceSet> = getPublishedCommonSourceSets(project)
val sourceSetsWithMetadataCompilations: Map<KotlinSourceSet, KotlinCommonCompilation> =
val sourceSetsWithMetadataCompilations: Map<KotlinSourceSet, AbstractKotlinCompilation<*>> =
publishedCommonSourceSets.associate { sourceSet ->
sourceSet to configureMetadataCompilation(target, sourceSet, allMetadataJar)
}
@@ -182,25 +202,41 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
target: KotlinMetadataTarget,
sourceSet: KotlinSourceSet,
allMetadataJar: Jar
): KotlinCommonCompilation {
): AbstractKotlinCompilation<*> {
val project = target.project
// With the metadata target, we publish all API dependencies of all the published source sets together:
target.apiElementsConfiguration.extendsFrom(project.configurations.getByName(sourceSet.apiConfigurationName))
val metadataCompilation = when (sourceSet.name) {
// Historically, we already had a 'main' compilation in metadata targets; TODO consider removing it instead
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME -> target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
else -> target.compilations.create(sourceSet.name) { compilation ->
compilation.addExactSourceSetsEagerly(setOf(sourceSet))
val compilationName = when (sourceSet.name) {
KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME -> KotlinCompilation.MAIN_COMPILATION_NAME
else -> sourceSet.name
}
val metadataCompilation = run {
if (compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
// It already exists. TODO Create it here?
return@run target.compilations.getByName(compilationName)
}
val isNativeSourceSet = CompilationSourceSetUtil.compilationsBySourceSets(project).getValue(sourceSet)
.all { compilation -> compilation.target is KotlinNativeTarget }
val compilationFactory: KotlinCompilationFactory<out AbstractKotlinCompilation<*>> = when {
isNativeSourceSet -> KotlinSharedNativeCompilationFactory(target)
else -> KotlinCommonCompilationFactory(target)
}
compilationFactory.create(compilationName).apply {
target.compilations.add(this@apply)
addExactSourceSetsEagerly(setOf(sourceSet))
}
}
project.addExtendsFromRelation(metadataCompilation.compileDependencyConfigurationName, ALL_COMPILE_METADATA_CONFIGURATION_NAME)
allMetadataJar.from(metadataCompilation.output.allOutputs) { spec ->
spec.into(metadataCompilation.defaultSourceSet.name)
}
val metadataContent = project.filesWithUnpackedArchives(metadataCompilation.output.allOutputs, setOf("klib"))
allMetadataJar.from(metadataContent) { spec -> spec.into(metadataCompilation.defaultSourceSet.name) }
project.registerTask<TransformKotlinGranularMetadata>(transformGranularMetadataTaskName(sourceSet), listOf(sourceSet)) { }
@@ -338,7 +374,14 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
// For now, we will only compile metadata from source sets used by multiple platforms
// TODO once the compiler is able to analyze common code with platform-specific features and dependencies, lift this restriction
val sourceSetsUsedInMultipleTargets = compilationsBySourceSet.filterValues { compilations ->
compilations.map { it.target.platformType }.distinct().size > 1
compilations.map { it.target.platformType }.distinct().run {
size > 1 || (
PropertiesProvider(project.rootProject).enableCommonKlibs == true &&
singleOrNull() == KotlinPlatformType.native &&
compilations.map { it.target }.distinct().size > 1 &&
compilations.all { (it.target as? KotlinNativeTarget)?.konanTarget?.enabledOnCurrentHost == true }
)
}
}
// We don't want to publish source set metadata from source sets that don't participate in any compilation that is published,
@@ -373,4 +416,13 @@ internal fun getPublishedPlatformCompilations(project: Project): Map<KotlinUsage
}
return result
}
}
private fun Project.filesWithUnpackedArchives(from: FileCollection, extensions: Set<String>): FileCollection =
project.files(project.provider {
from.map {
if (it.extension in extensions)
project.zipTree(it)
else it
}
}).builtBy(from)
@@ -7,7 +7,6 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask
@@ -16,16 +15,24 @@ import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigur
class KotlinMetadataTargetPreset(
project: Project,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinMetadataTarget, KotlinCommonCompilation>(
) : KotlinOnlyTargetPreset<KotlinMetadataTarget, AbstractKotlinCompilation<*>>(
project,
kotlinPluginVersion
) {
override fun getName(): String = PRESET_NAME
override fun createCompilationFactory(
forTarget: KotlinOnlyTarget<KotlinCommonCompilation>
): KotlinCompilationFactory<KotlinCommonCompilation> =
KotlinCommonCompilationFactory(forTarget)
forTarget: KotlinOnlyTarget<AbstractKotlinCompilation<*>>
): KotlinCompilationFactory<AbstractKotlinCompilation<*>> =
object : KotlinCompilationFactory<AbstractKotlinCompilation<*>> {
override val itemClass: Class<AbstractKotlinCompilation<*>>
get() = AbstractKotlinCompilation::class.java
override fun create(name: String): AbstractKotlinCompilation<*> = when (name) {
KotlinCompilation.MAIN_COMPILATION_NAME -> KotlinCommonCompilationFactory(forTarget).create(name)
else -> error("Can't create custom metadata compilations by name")
}
}
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.common
@@ -34,7 +41,7 @@ class KotlinMetadataTargetPreset(
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget> =
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<AbstractKotlinCompilation<*>, KotlinMetadataTarget> =
KotlinMetadataTargetConfigurator(kotlinPluginVersion)
override fun instantiateTarget(): KotlinMetadataTarget {
@@ -17,17 +17,19 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationWithResources
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.util.concurrent.Callable
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
abstract class AbstractKotlinNativeCompilation(
target: KotlinTarget,
val konanTarget: KonanTarget,
name: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, name), KotlinCompilationWithResources<KotlinCommonOptions> {
compilationName: String
) : AbstractKotlinCompilation<KotlinCommonOptions>(target, compilationName) {
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
@@ -35,9 +37,6 @@ class KotlinNativeCompilation(
override val compileKotlinTask: KotlinNativeCompile
get() = super.compileKotlinTask as KotlinNativeCompile
private val project: Project
get() = target.project
// A collection containing all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks. Unlike kotlinSourceSets, includes dependency source sets.
@@ -45,65 +44,32 @@ class KotlinNativeCompilation(
internal val allSources: MutableSet<SourceDirectorySet> = mutableSetOf()
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: ConfigurableFileCollection = project.files()
internal val commonSources: ConfigurableFileCollection = target.project.files()
@Deprecated("Use associateWith(...) to add a friend compilation and associateWith to get all of them.")
var friendCompilationName: String? = null
set(value) {
SingleWarningPerBuild.show(
project,
"Property `friendCompilationName` of `KotlinNativeCompilation` has been deprecated and will be removed. " +
"Use `associateWith(...)` instead."
)
field = value
}
internal val friendCompilations: List<KotlinNativeCompilation>
get() = mutableListOf<KotlinNativeCompilation>().apply {
@Suppress("DEPRECATION")
friendCompilationName?.let {
add(target.compilations.getByName(it))
}
addAll(associateWithTransitiveClosure.filterIsInstance<KotlinNativeCompilation>())
}
// Native-specific DSL.
private fun showDeprecationWarning() = SingleWarningPerBuild.show(
project,
"The compilation.extraOpts method used in this build is deprecated. Use compilation.kotlinOptions.freeCompilerArgs instead."
)
internal var extraOptsNoWarn: MutableList<String> = mutableListOf()
@Deprecated("Use kotlinOptions.freeCompilerArgs instead", ReplaceWith("kotlinOptions.freeCompilerArgs"))
var extraOpts: MutableList<String>
get() {
showDeprecationWarning()
return extraOptsNoWarn
}
set(value) {
showDeprecationWarning()
extraOptsNoWarn = value
}
@Deprecated("Use kotlinOptions.freeCompilerArgs instead", ReplaceWith("kotlinOptions.freeCompilerArgs += values as Array<String>"))
@Suppress("Deprecation")
fun extraOpts(vararg values: Any) = extraOpts(values.toList())
@Deprecated("Use kotlinOptions.freeCompilerArgs instead", ReplaceWith("kotlinOptions.freeCompilerArgs += values as List<String>"))
@Suppress("Deprecation")
fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Lazy<Boolean>) {
allSources.add(sourceSet.kotlin)
commonSources.from(target.project.files(Callable { if (addAsCommonSources.value) sourceSet.kotlin else emptyList<Any>() }))
}
// Endorsed library controller.
var enableEndorsedLibs: Boolean = false
}
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
konanTarget: KonanTarget,
name: String
) : AbstractKotlinNativeCompilation(target, konanTarget, name),
KotlinCompilationWithResources<KotlinCommonOptions> {
private val project: Project
get() = target.project
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName, this)
}
// Endorsed library controller.
var enableEndorsedLibs = false
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
@@ -124,8 +90,10 @@ class KotlinNativeCompilation(
val binariesTaskName: String
get() = lowerCamelCaseName(target.disambiguationClassifier, compilationName, "binaries")
override fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Lazy<Boolean>) {
allSources.add(sourceSet.kotlin)
commonSources.from(project.files(Callable { if (addAsCommonSources.value) sourceSet.kotlin else emptyList<Any>() }))
}
}
override val kotlinOptions: KotlinCommonOptions
get() = compileKotlinTask.kotlinOptions
}
class KotlinSharedNativeCompilation(override val target: KotlinMetadataTarget, name: String) :
AbstractKotlinNativeCompilation(target, HostManager.host, name),
KotlinMetadataCompilation<KotlinCommonOptions>
@@ -6,10 +6,9 @@
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.jetbrains.kotlin.konan.target.HostManager
class KotlinNativeCompilationFactory(
val project: Project,
val target: KotlinNativeTarget
) : KotlinCompilationFactory<KotlinNativeCompilation> {
@@ -22,4 +21,14 @@ class KotlinNativeCompilationFactory(
// Note: such validation should be done in the whenEvaluate block because
// a user can change args during project configuration.
KotlinNativeCompilation(target, target.konanTarget, name)
}
class KotlinSharedNativeCompilationFactory(
val target: KotlinMetadataTarget
): KotlinCompilationFactory<KotlinSharedNativeCompilation> {
override val itemClass: Class<KotlinSharedNativeCompilation>
get() = KotlinSharedNativeCompilation::class.java
override fun create(name: String): KotlinSharedNativeCompilation =
KotlinSharedNativeCompilation(target, name)
}
@@ -17,6 +17,7 @@ import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
@@ -41,7 +42,7 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
createTestCompilation = true
) {
private fun Project.klibOutputDirectory(
compilation: KotlinNativeCompilation
compilation: AbstractKotlinNativeCompilation
): File {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.name}")
@@ -148,35 +149,39 @@ open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
}
}
private fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilation) {
val compileTask = tasks.create(
internal fun Project.createKlibCompilationTask(compilation: AbstractKotlinNativeCompilation): TaskProvider<out KotlinNativeCompile> {
val compileTask = project.registerTask<KotlinNativeCompile>(
compilation.compileKotlinTaskName,
KotlinNativeCompile::class.java
).apply {
this.compilation = compilation
group = BasePlugin.BUILD_GROUP
description = "Compiles a klibrary from the '${compilation.name}' " +
) { task ->
task.compilation = compilation
task.group = BasePlugin.BUILD_GROUP
task.description = "Compiles a klibrary from the '${compilation.name}' " +
"compilation for target '${compilation.platformType.name}'."
enabled = compilation.konanTarget.enabledOnCurrentHost
task.enabled = compilation.konanTarget.enabledOnCurrentHost
destinationDir = klibOutputDirectory(compilation)
addCompilerPlugins()
task.destinationDir = klibOutputDirectory(compilation)
task.addCompilerPlugins()
compilation.output.addClassesDir {
project.files(this.outputFile).builtBy(this)
project.files(task.outputFile).builtBy(task)
}
}
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
if (compilation.compilationName == MAIN_COMPILATION_NAME) {
compilation as? KotlinNativeCompilation ?: error("Main shared-Native compilation is not yet supported!")
project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTask)
}
project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).apply {
dependsOn(compileTask)
}
createRegularKlibArtifact(compilation, compileTask)
createRegularKlibArtifact(compilation, compileTask.get() /*TODO don't instantiate the task eagerly*/)
}
return compileTask
}
private fun Project.createCInteropTasks(compilation: KotlinNativeCompilation) {
@@ -87,7 +87,7 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
disambiguationClassifier = name
preset = this@AbstractKotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this)
val compilationFactory = KotlinNativeCompilationFactory(this)
compilations = project.container(compilationFactory.itemClass, compilationFactory)
}
@@ -209,5 +209,5 @@ internal val KonanTarget.isCurrentHost: Boolean
internal val KonanTarget.enabledOnCurrentHost
get() = HostManager().isEnabled(this)
internal val KotlinNativeCompilation.isMainCompilation: Boolean
internal val AbstractKotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -90,7 +90,8 @@ private fun FileCollection.filterOutPublishableInteropLibs(project: Project): Fi
/**
* We pass to the compiler:
*
* - Only *.klib files. A dependency configuration may contain jar files
* - Only *.klib files and directories (normally containing an unpacked klib).
* A dependency configuration may contain jar files
* (e.g. when a common artifact was directly added to commonMain source set).
* So, we need to filter out such artifacts.
*
@@ -103,7 +104,7 @@ private fun FileCollection.filterOutPublishableInteropLibs(project: Project): Fi
* uses them by default so we don't pass them to the compiler explicitly.
*/
private fun Collection<File>.filterKlibsPassedToCompiler(project: Project) = filter {
it.extension == "klib" && it.exists() && !it.providedByCompiler(project)
(it.extension == "klib" || it.isDirectory) && it.exists() && !it.providedByCompiler(project)
}
// endregion
@@ -115,7 +116,7 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
}
@get:Internal
abstract val compilation: KotlinNativeCompilation
abstract val compilation: AbstractKotlinNativeCompilation
// region inputs/outputs
@get:Input
@@ -245,6 +246,11 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
addArg("-target", target)
addArg("-p", outputKind.name.toLowerCase())
if (compilation is KotlinSharedNativeCompilation) {
add("-Xklib-mpp")
add("-Xmetadata-klib")
}
addArg("-o", outputFile.get().absolutePath)
// Libraries.
@@ -273,7 +279,7 @@ abstract class AbstractKotlinNativeCompile<T : KotlinCommonToolOptions> : Abstra
*/
open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions>(), KotlinCompile<KotlinCommonOptions> {
@Internal
override lateinit var compilation: KotlinNativeCompilation
final override lateinit var compilation: AbstractKotlinNativeCompilation
@get:Input
override val outputKind = LIBRARY
@@ -300,7 +306,7 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
private val friendModule: FileCollection?
get() = project.files(
project.provider { compilation.friendCompilations.map { it.output.allOutputs } + compilation.friendArtifacts }
project.provider { compilation.associateWithTransitiveClosure.map { it.output.allOutputs } + compilation.friendArtifacts }
)
// endregion.
@@ -332,13 +338,7 @@ open class KotlinNativeCompile : AbstractKotlinNativeCompile<KotlinCommonOptions
override var suppressWarnings: Boolean = false
override var verbose: Boolean = false
// TODO: Drop extraOpts in 1.3.70 and create a list here directly
// Delegate for compilations's extra options.
override var freeCompilerArgs: List<String>
get() = compilation.extraOptsNoWarn
set(value) {
compilation.extraOptsNoWarn = value.toMutableList()
}
override var freeCompilerArgs: List<String> = listOf()
}
@get:Input
@@ -582,7 +582,7 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile<KotlinCommonToolOption
// Allow a user to force the old behaviour of a link task.
// TODO: Remove in 1.3.70.
mutableListOf<String>().apply {
val friendCompilations = compilation.friendCompilations
val friendCompilations = compilation.associateWithTransitiveClosure.toList()
val friendFiles = if (friendCompilations.isNotEmpty())
project.files(
project.provider { friendCompilations.map { it.output.allOutputs } + compilation.friendArtifacts }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptionsImpl
import org.jetbrains.kotlin.gradle.dsl.fillDefaultValues
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.incremental.ChangedFiles
import java.io.File
@@ -52,6 +53,10 @@ open class KotlinCompileCommon : AbstractKotlinCompile<K2MetadataCompilerArgumen
args.moduleName = this@KotlinCompileCommon.moduleName
if (PropertiesProvider(project.rootProject).enableCommonKlibs == true) {
args.klibBasedMpp = true
}
if (defaultsOnly) return
val classpathList = classpath.files.toMutableList()
@@ -284,6 +284,9 @@ class MockKotlinCompilation(
override fun source(sourceSet: KotlinSourceSet) = defaultSourceSet.dependsOn(sourceSet)
override val defaultSourceSetName: String
get() = defaultSourceSet.name
override fun associateWith(other: KotlinCompilation<*>) {
associateWith += other
}