Enable metadata publishing for new MPP and consumption from source sets
* Add configurations for consuming source sets metadata (extending the source set's dependency configurations). These configurations will be used by the IDE to pick up the dependencies metadata. * Remove the universal preset from the default presets, create a metadata target by default for compiling and publishing the metadata of `commonMain` (in the future, we will publish and consume the metadata of the other source sets as well). * Make the classpath configurations of common modules consume the metadata and thus ensure compatibility of 1.2.x MPP with the new MPP model.
This commit is contained in:
+14
@@ -23,6 +23,20 @@ interface KotlinSourceSet : Named, HasKotlinDependencies {
|
||||
fun dependsOn(other: KotlinSourceSet)
|
||||
val dependsOn: Set<KotlinSourceSet>
|
||||
|
||||
val apiMetadataConfigurationName: String
|
||||
val implementationMetadataConfigurationName: String
|
||||
val compileOnlyMetadataConfigurationName: String
|
||||
val runtimeOnlyMetadataConfigurationName: String
|
||||
|
||||
override val relatedConfigurationNames: List<String>
|
||||
get() = super.relatedConfigurationNames +
|
||||
listOf(
|
||||
apiMetadataConfigurationName,
|
||||
implementationMetadataConfigurationName,
|
||||
compileOnlyMetadataConfigurationName,
|
||||
runtimeOnlyMetadataConfigurationName
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val COMMON_MAIN_SOURCE_SET_NAME = "commonMain"
|
||||
const val COMMON_TEST_SOURCE_SET_NAME = "commonTest"
|
||||
|
||||
+2
@@ -30,10 +30,12 @@ class KotlinAndroid32GradleIT : KotlinAndroid3GradleIT(androidGradlePluginVersio
|
||||
":lib:compileReleaseKotlinAndroidLib",
|
||||
":lib:compileKotlinJvmLib",
|
||||
":lib:compileKotlinJsLib",
|
||||
":lib:compileKotlinMetadata",
|
||||
":app:compileDebugKotlinAndroidApp",
|
||||
":app:compileReleaseKotlinAndroidApp",
|
||||
":app:compileKotlinJvmApp",
|
||||
":app:compileKotlinJsApp",
|
||||
":app:compileKotlinMetadata",
|
||||
":lib:compileDebugUnitTestJavaWithJavac",
|
||||
":app:compileDebugUnitTestJavaWithJavac"
|
||||
)
|
||||
|
||||
+84
-7
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMultiplatformPlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.METADATA_CONFIGURATION_NAME_SUFFIX
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.SourceSetConsistencyChecks
|
||||
import org.jetbrains.kotlin.gradle.util.checkBytecodeContains
|
||||
import org.jetbrains.kotlin.gradle.util.modify
|
||||
@@ -27,12 +29,16 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
with(libProject) {
|
||||
build("publish") {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":compileKotlinJvm6", ":compileKotlinNodeJs", ":jvm6Jar", ":nodeJsJar")
|
||||
val repoDir = projectDir.resolve("repo")
|
||||
val moduleDir = repoDir.resolve("com/example/sample-lib/1.0")
|
||||
assertTasksExecuted(
|
||||
":compileKotlinJvm6", ":compileKotlinNodeJs", ":compileKotlinMetadata",
|
||||
":jvm6Jar", ":nodeJsJar", ":metadataJar"
|
||||
)
|
||||
val moduleDir = projectDir.resolve("repo/com/example/sample-lib/1.0")
|
||||
val jvmJarName = "sample-lib-1.0-jvm6.jar"
|
||||
val jsJarName = "sample-lib-1.0-nodeJs.jar"
|
||||
listOf(jvmJarName, jsJarName, "sample-lib-1.0.module").forEach {
|
||||
val metadataJarName = "sample-lib-1.0-metadata.jar"
|
||||
|
||||
listOf(jvmJarName, jsJarName, metadataJarName, "sample-lib-1.0.module").forEach {
|
||||
Assert.assertTrue(moduleDir.resolve(it).exists())
|
||||
}
|
||||
|
||||
@@ -46,6 +52,9 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
Assert.assertTrue("function idUsage(" in compiledJs)
|
||||
Assert.assertTrue("function expectedFun(" in compiledJs)
|
||||
Assert.assertTrue("function main(" in compiledJs)
|
||||
|
||||
val metadataJarEntries = ZipFile(moduleDir.resolve(metadataJarName)).entries().asSequence().map { it.name }.toSet()
|
||||
Assert.assertTrue("com/example/lib/CommonKt.kotlin_metadata" in metadataJarEntries)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +66,11 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
|
||||
fun CompiledProject.checkAppBuild() {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":compileKotlinJvm6", ":compileKotlinJvm8", ":compileKotlinNodeJs")
|
||||
assertTasksExecuted(":compileKotlinJvm6", ":compileKotlinJvm8", ":compileKotlinNodeJs", ":compileKotlinMetadata")
|
||||
|
||||
projectDir.resolve(targetClassesDir("jvm6")).run {
|
||||
Assert.assertTrue(resolve("com/example/app/AKt.class").exists())
|
||||
Assert.assertTrue(this.resolve("com/example/app/UseBothIdsKt.class").exists())
|
||||
Assert.assertTrue(resolve("com/example/app/UseBothIdsKt.class").exists())
|
||||
}
|
||||
|
||||
projectDir.resolve(targetClassesDir("jvm8")).run {
|
||||
@@ -70,6 +79,10 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
Assert.assertTrue(resolve("com/example/app/Jdk8ApiUsageKt.class").exists())
|
||||
}
|
||||
|
||||
projectDir.resolve(targetClassesDir("metadata")).run {
|
||||
Assert.assertTrue(resolve("com/example/app/AKt.kotlin_metadata").exists())
|
||||
}
|
||||
|
||||
projectDir.resolve(targetClassesDir("nodeJs")).resolve("sample-app.js").readText().run {
|
||||
Assert.assertTrue(contains("console.info"))
|
||||
Assert.assertTrue(contains("function nodeJsMain("))
|
||||
@@ -96,7 +109,9 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
|
||||
build("assemble") {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":app-js:compileKotlin2Js", ":app-jvm:compileKotlin")
|
||||
assertTasksExecuted(":app-js:compileKotlin2Js", ":app-jvm:compileKotlin", ":app-common:compileKotlinCommon")
|
||||
|
||||
assertFileExists(kotlinClassesDir("app-common") + "com/example/app/CommonAppKt.kotlin_metadata")
|
||||
|
||||
val jvmClassFile = projectDir.resolve(kotlinClassesDir("app-jvm") + "com/example/app/JvmAppKt.class")
|
||||
checkBytecodeContains(jvmClassFile, "CommonKt.id", "MainKt.expectedFun")
|
||||
@@ -326,4 +341,66 @@ class NewMultiplatformIT : BaseGradleIT() {
|
||||
assertContains("allJava:foo.java")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testResolveMppLibDependencyToMetadata() {
|
||||
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
|
||||
val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app")
|
||||
|
||||
libProject.build("publish") { assertSuccessful() }
|
||||
val localRepo = libProject.projectDir.resolve("repo")
|
||||
val localRepoUri = localRepo.toURI()
|
||||
|
||||
with(appProject) {
|
||||
setupWorkingDir()
|
||||
|
||||
val pathPrefix = "metadataDependency: "
|
||||
|
||||
gradleBuildScript().appendText(
|
||||
"\n" + """
|
||||
repositories { maven { url '$localRepoUri' } }
|
||||
|
||||
kotlin.sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
// add these dependencies to check that they are resolved to metadata
|
||||
api 'com.example:sample-lib:1.0'
|
||||
compileOnly 'com.example:sample-lib:1.0'
|
||||
runtimeOnly 'com.example:sample-lib:1.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task('printMetadataFiles') {
|
||||
doFirst {
|
||||
['Api', 'Implementation', 'CompileOnly', 'RuntimeOnly'].each { kind ->
|
||||
def configuration = configurations.getByName("commonMain${'$'}kind" + '$METADATA_CONFIGURATION_NAME_SUFFIX')
|
||||
configuration.files.each { println '$pathPrefix' + configuration.name + '->' + it.absolutePath }
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
val metadataDependencyRegex = "$pathPrefix(.*?)->(.*)".toRegex()
|
||||
|
||||
build("printMetadataFiles") {
|
||||
assertSuccessful()
|
||||
|
||||
val expectedPath =
|
||||
localRepo.resolve(
|
||||
"com/example/sample-lib/1.0/sample-lib-1.0-${KotlinMultiplatformPlugin.METADATA_TARGET_NAME}.jar"
|
||||
).absolutePath
|
||||
|
||||
val paths = metadataDependencyRegex.findAll(output).map { it.groupValues[1] to it.groupValues[2] }.toSet()
|
||||
|
||||
Assert.assertEquals(
|
||||
listOf("Api", "Implementation", "CompileOnly", "RuntimeOnly").map {
|
||||
"commonMain$it$METADATA_CONFIGURATION_NAME_SUFFIX" to expectedPath
|
||||
}.toSet(),
|
||||
paths
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,4 +2,4 @@ package com.example.lib
|
||||
|
||||
class CommonLibClass
|
||||
|
||||
expect class ExpectedLibClass
|
||||
expect class ExpectedLibClass()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
apply plugin: 'kotlin-platform-common'
|
||||
|
||||
dependencies {
|
||||
compile 'com.example:sample-lib:1.0'
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.example.app
|
||||
|
||||
import com.example.lib.id
|
||||
import com.example.lib.expectedFun
|
||||
|
||||
fun idUsage(x: Int): Int = id(x)
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
expectedFun()
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
enableFeaturePreview('GRADLE_METADATA')
|
||||
|
||||
include 'app-jvm', 'app-js'
|
||||
include 'app-common', 'app-jvm', 'app-js'
|
||||
+7
-4
@@ -433,15 +433,18 @@ internal abstract class AbstractKotlinPlugin(
|
||||
) {
|
||||
val project = kotlinTarget.project
|
||||
|
||||
// Setup the consuming configurations:
|
||||
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
|
||||
kotlinTarget.compilations.all { compilation ->
|
||||
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, kotlinTarget, project.configurations)
|
||||
}
|
||||
|
||||
// Setup the published configurations:
|
||||
// Don't set the attributes for common module; otherwise their 'common' platform won't be compatible with the one in
|
||||
// platform-specific modules
|
||||
if (kotlinTarget.platformType != KotlinPlatformType.common) {
|
||||
project.dependencies.attributesSchema.attribute(KotlinPlatformType.attribute)
|
||||
project.configurations.getByName(kotlinTarget.apiElementsConfigurationName).usesPlatformOf(kotlinTarget)
|
||||
project.configurations.getByName(kotlinTarget.runtimeElementsConfigurationName).usesPlatformOf(kotlinTarget)
|
||||
kotlinTarget.compilations.all { compilation ->
|
||||
KotlinTargetConfigurator.defineConfigurationsForCompilation(compilation, kotlinTarget, project.configurations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
|
||||
internal val Project.multiplatformExtension get(): KotlinMultiplatformExtension? =
|
||||
project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
|
||||
|
||||
internal class KotlinMultiplatformPlugin(
|
||||
class KotlinMultiplatformPlugin(
|
||||
private val buildOutputCleanupRegistry: BuildOutputCleanupRegistry,
|
||||
private val fileResolver: FileResolver,
|
||||
private val instantiator: Instantiator,
|
||||
@@ -72,11 +72,16 @@ internal class KotlinMultiplatformPlugin(
|
||||
|
||||
setUpConfigurationAttributes(project)
|
||||
configurePublishingWithMavenPublish(project)
|
||||
|
||||
// set up metadata publishing
|
||||
targetsFromPreset.fromPreset(
|
||||
KotlinMetadataTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion),
|
||||
METADATA_TARGET_NAME
|
||||
)
|
||||
}
|
||||
|
||||
fun setupDefaultPresets(project: Project) {
|
||||
with((project.kotlinExtension as KotlinMultiplatformExtension).presets) {
|
||||
add(KotlinUniversalTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
|
||||
add(KotlinJvmTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
|
||||
add(KotlinJsTargetPreset(project, instantiator, fileResolver, buildOutputCleanupRegistry, kotlinPluginVersion))
|
||||
add(KotlinAndroidTargetPreset(project, kotlinPluginVersion))
|
||||
@@ -155,4 +160,8 @@ internal class KotlinMultiplatformPlugin(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val METADATA_TARGET_NAME = "metadata"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -45,7 +45,7 @@ abstract class KotlinOnlyTargetPreset<T : KotlinCompilation>(
|
||||
internal abstract fun buildCompilationProcessor(compilation: T): KotlinSourceSetProcessor<*>
|
||||
}
|
||||
|
||||
class KotlinUniversalTargetPreset(
|
||||
class KotlinMetadataTargetPreset(
|
||||
project: Project,
|
||||
instantiator: Instantiator,
|
||||
fileResolver: FileResolver,
|
||||
@@ -77,7 +77,7 @@ class KotlinUniversalTargetPreset(
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val PRESET_NAME = "universal"
|
||||
const val PRESET_NAME = "metadata"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.lang.reflect.Constructor
|
||||
import java.util.*
|
||||
|
||||
const val METADATA_CONFIGURATION_NAME_SUFFIX = "DependenciesMetadata"
|
||||
|
||||
class DefaultKotlinSourceSet(
|
||||
private val project: Project,
|
||||
val displayName: String,
|
||||
@@ -38,6 +40,18 @@ class DefaultKotlinSourceSet(
|
||||
override val runtimeOnlyConfigurationName: String
|
||||
get() = disambiguateName("runtimeOnly")
|
||||
|
||||
override val apiMetadataConfigurationName: String
|
||||
get() = lowerCamelCaseName(apiConfigurationName, METADATA_CONFIGURATION_NAME_SUFFIX)
|
||||
|
||||
override val implementationMetadataConfigurationName: String
|
||||
get() = lowerCamelCaseName(implementationConfigurationName, METADATA_CONFIGURATION_NAME_SUFFIX)
|
||||
|
||||
override val compileOnlyMetadataConfigurationName: String
|
||||
get() = lowerCamelCaseName(compileOnlyConfigurationName, METADATA_CONFIGURATION_NAME_SUFFIX)
|
||||
|
||||
override val runtimeOnlyMetadataConfigurationName: String
|
||||
get() = lowerCamelCaseName(runtimeOnlyConfigurationName, METADATA_CONFIGURATION_NAME_SUFFIX)
|
||||
|
||||
override val kotlin: SourceDirectorySet = createDefaultSourceDirectorySet(name + " Kotlin source", fileResolver).apply {
|
||||
filter.include("**/*.java", "**/*.kt", "**/*.kts")
|
||||
}
|
||||
|
||||
+21
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.gradle.plugin.sources
|
||||
|
||||
import org.gradle.api.NamedDomainObjectFactory
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.internal.file.FileResolver
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.source.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.usageByName
|
||||
import java.io.File
|
||||
|
||||
internal abstract class KotlinSourceSetFactory<T : KotlinSourceSet> internal constructor(
|
||||
@@ -52,6 +55,24 @@ internal class DefaultKotlinSourceSetFactory(
|
||||
override fun setUpSourceSetDefaults(sourceSet: DefaultKotlinSourceSet) {
|
||||
super.setUpSourceSetDefaults(sourceSet)
|
||||
sourceSet.resources.srcDir(File(defaultSourceLocation(sourceSet.name), "resources"))
|
||||
|
||||
val dependencyConfigurationWithMetadata = with(sourceSet) {
|
||||
listOf(
|
||||
apiConfigurationName to apiMetadataConfigurationName,
|
||||
implementationConfigurationName to implementationMetadataConfigurationName,
|
||||
compileOnlyConfigurationName to compileOnlyMetadataConfigurationName,
|
||||
runtimeOnlyConfigurationName to runtimeOnlyMetadataConfigurationName
|
||||
)
|
||||
}
|
||||
|
||||
dependencyConfigurationWithMetadata.forEach { (configurationName, metadataName) ->
|
||||
project.configurations.maybeCreate(metadataName).apply {
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common)
|
||||
isVisible = false
|
||||
isCanBeConsumed = false
|
||||
extendsFrom(project.configurations.maybeCreate(configurationName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doCreateSourceSet(name: String): DefaultKotlinSourceSet {
|
||||
|
||||
Reference in New Issue
Block a user