[gradle-plugin] Support Konan tooling model in the exp plugin (#2008)

This commit is contained in:
Ilya Matveev
2018-09-06 19:15:39 +07:00
committed by GitHub
parent 6d539e5e40
commit 710c691aa8
3 changed files with 156 additions and 18 deletions
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
// TODO: implement ComponentWithObjectFiles when we build klibs as objects // TODO: implement ComponentWithObjectFiles when we build klibs as objects
interface KotlinNativeBinary: ComponentWithDependencies, BuildableComponent { interface KotlinNativeBinary : ComponentWithDependencies, BuildableComponent, ComponentWithOutputs {
/** A component this binary belongs to. */ /** A component this binary belongs to. */
val component: KotlinNativeComponent val component: KotlinNativeComponent
@@ -86,7 +86,6 @@ interface KotlinNativeBinary: ComponentWithDependencies, BuildableComponent {
*/ */
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation. // TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeExecutable : KotlinNativeBinary, interface KotlinNativeExecutable : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent, PublishableComponent,
ConfigurableComponentWithRuntimeUsage ConfigurableComponentWithRuntimeUsage
@@ -95,29 +94,26 @@ interface KotlinNativeExecutable : KotlinNativeBinary,
*/ */
// TODO: Consider implementing ComponentWithLinkFile. // TODO: Consider implementing ComponentWithLinkFile.
interface KotlinNativeLibrary : KotlinNativeBinary, interface KotlinNativeLibrary : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent, PublishableComponent,
ConfigurableComponentWithLinkUsage ConfigurableComponentWithLinkUsage
/** /**
* Represents an Objective C framework compiled from Kotlin/Native sources. * Represents an Objective C framework compiled from Kotlin/Native sources.
*/ */
interface KotlinNativeFramework : KotlinNativeBinary, ComponentWithOutputs interface KotlinNativeFramework : KotlinNativeBinary
/** /**
* A shared library compiled from Kotlin/Native sources. * A shared library compiled from Kotlin/Native sources.
*/ */
interface KotlinNativeDynamic : KotlinNativeBinary, ComponentWithOutputs interface KotlinNativeDynamic : KotlinNativeBinary
/** /**
* A static library compiled from Kotlin/Native sources. * A static library compiled from Kotlin/Native sources.
*/ */
interface KotlinNativeStatic : KotlinNativeBinary, ComponentWithOutputs interface KotlinNativeStatic : KotlinNativeBinary
/** /**
* Represents a test executable. * Represents a test executable.
*/ */
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation. // TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeTestExecutable : KotlinNativeBinary, interface KotlinNativeTestExecutable : KotlinNativeBinary, TestComponent
ComponentWithOutputs,
TestComponent
@@ -18,29 +18,84 @@ package org.jetbrains.kotlin.gradle.plugin.model
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.tooling.provider.model.ToolingModelBuilder import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.konanArtifactsContainer import org.jetbrains.kotlin.gradle.plugin.konanArtifactsContainer
import org.jetbrains.kotlin.gradle.plugin.konanExtension import org.jetbrains.kotlin.gradle.plugin.konanExtension
import org.jetbrains.kotlin.gradle.plugin.konanHome import org.jetbrains.kotlin.gradle.plugin.konanHome
import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import java.io.File import java.io.File
import java.lang.IllegalStateException
object KonanToolingModelBuilder : ToolingModelBuilder { object KonanToolingModelBuilder : ToolingModelBuilder {
override fun canBuild(modelName: String) = KonanModel::class.java.name == modelName override fun canBuild(modelName: String) = KonanModel::class.java.name == modelName
override fun buildAll(modelName: String, project: Project): KonanModel { private fun buildModelKonan(project: Project): KonanModel {
val artifacts = project.konanArtifactsContainer.flatten().toList().map { it.toModelArtifact() } val artifacts = project.konanArtifactsContainer.flatten().toList().map { it.toModelArtifact() }
return KonanModelImpl( return KonanModelImpl(
artifacts, artifacts,
project.file(project.konanHome), project.file(project.konanHome),
KonanVersion.CURRENT, KonanVersion.CURRENT,
// TODO: Provide defaults for these versions. // TODO: Provide defaults for these versions.
project.konanExtension.languageVersion, project.konanExtension.languageVersion,
project.konanExtension.apiVersion project.konanExtension.apiVersion
) )
} }
private fun buildModelKotlinNative(project: Project): KonanModel {
val artifacts = project.components.withType(AbstractKotlinNativeBinary::class.java).map {
it.toModelArtifact()
}
return KonanModelImpl(
artifacts,
project.file(project.konanHome),
KonanVersion.CURRENT,
null,
null
)
}
private fun AbstractKotlinNativeBinary.toModelArtifact(): KonanModelArtifact {
val compileTask = compileTask.get()
val sourceRoots = with(component.sources) {
kotlin.srcDirs + getPlatformSources(konanTarget).srcDirs
}
return KonanModelArtifactImpl(
name,
compileTask.outputFile,
kind,
konanTarget.name,
compileTask.name,
sourceRoots.toList(),
sources.files.toList(),
klibraries.files.toList(),
klibraries.files.map { it.parentFile }
)
}
private val Project.hasKonanPlugin: Boolean
get() = with(pluginManager) {
hasPlugin("konan") ||
hasPlugin("org.jetbrains.kotlin.konan")
}
private val Project.hasKotlinNativePlugin: Boolean
get() = with(pluginManager) {
hasPlugin("kotlin-native") ||
hasPlugin("kotlin-platform-native") ||
hasPlugin("org.jetbrains.kotlin.native") ||
hasPlugin("org.jetbrains.kotlin.platform.native")
}
override fun buildAll(modelName: String, project: Project): KonanModel =
when {
project.hasKotlinNativePlugin -> buildModelKotlinNative(project)
project.hasKonanPlugin -> buildModelKonan(project)
else -> throw IllegalStateException("The project '${project.path}' has no Kotlin/Native plugin")
}
} }
internal data class KonanModelImpl( internal data class KonanModelImpl(
@@ -9,7 +9,9 @@ import org.gradle.testkit.runner.TaskOutcome
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeMainComponent import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeMainComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.OutputKind import org.jetbrains.kotlin.gradle.plugin.experimental.internal.OutputKind
import org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinNativePlugin import org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinNativePlugin
import org.jetbrains.kotlin.gradle.plugin.experimental.plugins.kotlinNativeSourceSets
import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.plugin.model.KonanToolingModelBuilder
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -406,8 +408,8 @@ class ExperimentalPluginTests {
@Test @Test
fun `Plugin should not create compilation tasks for targets unsupported by the current host`() = fun `Plugin should not create compilation tasks for targets unsupported by the current host`() =
withProject { withProject {
val hosts = arrayOf("macos_x64", "linux_x64", "mingw_x64") val hosts = listOf("macos_x64", "linux_x64", "mingw_x64")
components.withType(KotlinNativeMainComponent::class.java).getByName("main").target(*hosts) components.withType(KotlinNativeMainComponent::class.java).getByName("main").targets = hosts
evaluate() evaluate()
hosts.map { HostManager().targetByName(it) }.forEach { hosts.map { HostManager().targetByName(it) }.forEach {
val task = tasks.findByName("compileDebug${it.name.capitalize()}KotlinNative") val task = tasks.findByName("compileDebug${it.name.capitalize()}KotlinNative")
@@ -727,4 +729,89 @@ class ExperimentalPluginTests {
output.contains("Transitive call!") output.contains("Transitive call!")
} }
} }
@Test
fun `Plugin should support the konan tooling model`() {
withProject {
val hostName = HostManager.hostName
val mainSrcDirs = listOf("src/main/kotlin", "src/other/kotlin").map {
file(it).apply { mkdirs() }
}
val testDir = file("src/test/kotlin").apply { mkdirs() }
val mainSrcFiles = listOf(
"src/main/kotlin/main.kt" to "fun main(args: Array<String>) { foo() }",
"src/other/kotlin/foo.kt" to "fun foo() { println(42) }"
).map { (name, content) ->
project.file(name).apply { createNewFile(); writeText(content) }
}
val testSrcFile = file("src/test/kotlin/test.kt").apply {
createNewFile()
writeText("""
import kotlin.test.*
@Test fun test() { foo() }
""".trimIndent())
}
components.withType(KotlinNativeMainComponent::class.java).getByName("main").apply {
outputKinds.set(listOf(OutputKind.KLIBRARY, OutputKind.EXECUTABLE))
targets = listOf(hostName, "wasm32")
}
kotlinNativeSourceSets.getByName("main").kotlin.srcDirs(*mainSrcDirs.toTypedArray())
evaluate()
val model = KonanToolingModelBuilder.buildAll("konan", this)
val mainArtifacts = model.artifacts.filterNot { it.name.toLowerCase().contains("test") }
val testArtifacts = model.artifacts.filter { it.name.toLowerCase().contains("test") }
mainArtifacts.forEach {
assertEquals(mainSrcDirs.toSet(), it.srcDirs.toSet())
assertEquals(mainSrcFiles.toSet(), it.srcFiles.toSet())
}
testArtifacts.forEach {
assertEquals(setOf(testDir), it.srcDirs.toSet())
assertEquals(setOf(testSrcFile) + mainSrcFiles.toSet(), it.srcFiles.toSet())
}
val nameToArtifact = model.artifacts.map { it.name to it }.toMap()
val buildTypes = listOf("debug", "release")
val kinds = listOf(OutputKind.EXECUTABLE, OutputKind.KLIBRARY)
val targets = listOf(HostManager.host, KonanTarget.WASM32)
// Production binaries
buildTypes.forEach { buildType ->
kinds.forEach { kind ->
targets.forEach { target ->
val suffix = "${buildType.capitalize()}${kind.name.toLowerCase().capitalize()}${target.name.capitalize()}"
val artifact = nameToArtifact.getValue("main$suffix")
assertEquals(target.name, artifact.targetPlatform)
assertEquals(kind.compilerOutputKind, artifact.type)
assertEquals("compile${suffix}KotlinNative", artifact.buildTaskName)
val outputRoot = if (kind == OutputKind.EXECUTABLE) "exe" else "lib"
val outputFile = file(
"build/$outputRoot/main/$buildType/${kind.name.toLowerCase()}/${target.name}/" +
"testProject${kind.compilerOutputKind.suffix(target)}"
)
assertEquals(outputFile, artifact.file)
}
}
}
// Test binaries
targets.forEach { target ->
val suffix = "Debug${target.name.capitalize()}"
val artifact = nameToArtifact.getValue("test$suffix")
assertEquals(target.name, artifact.targetPlatform)
assertEquals(CompilerOutputKind.PROGRAM, artifact.type)
assertEquals("compileTest${suffix}KotlinNative", artifact.buildTaskName)
assertEquals(
file("build/test-exe/test/debug/${target.name}/test${CompilerOutputKind.PROGRAM.suffix(target)}"),
artifact.file
)
}
}
}
} }