[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
// TODO: implement ComponentWithObjectFiles when we build klibs as objects
interface KotlinNativeBinary: ComponentWithDependencies, BuildableComponent {
interface KotlinNativeBinary : ComponentWithDependencies, BuildableComponent, ComponentWithOutputs {
/** A component this binary belongs to. */
val component: KotlinNativeComponent
@@ -86,7 +86,6 @@ interface KotlinNativeBinary: ComponentWithDependencies, BuildableComponent {
*/
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeExecutable : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent,
ConfigurableComponentWithRuntimeUsage
@@ -95,29 +94,26 @@ interface KotlinNativeExecutable : KotlinNativeBinary,
*/
// TODO: Consider implementing ComponentWithLinkFile.
interface KotlinNativeLibrary : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent,
ConfigurableComponentWithLinkUsage
/**
* 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.
*/
interface KotlinNativeDynamic : KotlinNativeBinary, ComponentWithOutputs
interface KotlinNativeDynamic : KotlinNativeBinary
/**
* A static library compiled from Kotlin/Native sources.
*/
interface KotlinNativeStatic : KotlinNativeBinary, ComponentWithOutputs
interface KotlinNativeStatic : KotlinNativeBinary
/**
* Represents a test executable.
*/
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeTestExecutable : KotlinNativeBinary,
ComponentWithOutputs,
TestComponent
interface KotlinNativeTestExecutable : KotlinNativeBinary, TestComponent
@@ -18,29 +18,84 @@ package org.jetbrains.kotlin.gradle.plugin.model
import org.gradle.api.Project
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.konanExtension
import org.jetbrains.kotlin.gradle.plugin.konanHome
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import java.io.File
import java.lang.IllegalStateException
object KonanToolingModelBuilder : ToolingModelBuilder {
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() }
return KonanModelImpl(
artifacts,
project.file(project.konanHome),
KonanVersion.CURRENT,
// TODO: Provide defaults for these versions.
project.konanExtension.languageVersion,
project.konanExtension.apiVersion
artifacts,
project.file(project.konanHome),
KonanVersion.CURRENT,
// TODO: Provide defaults for these versions.
project.konanExtension.languageVersion,
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(
@@ -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.OutputKind
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.model.KonanToolingModelBuilder
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -406,8 +408,8 @@ class ExperimentalPluginTests {
@Test
fun `Plugin should not create compilation tasks for targets unsupported by the current host`() =
withProject {
val hosts = arrayOf("macos_x64", "linux_x64", "mingw_x64")
components.withType(KotlinNativeMainComponent::class.java).getByName("main").target(*hosts)
val hosts = listOf("macos_x64", "linux_x64", "mingw_x64")
components.withType(KotlinNativeMainComponent::class.java).getByName("main").targets = hosts
evaluate()
hosts.map { HostManager().targetByName(it) }.forEach {
val task = tasks.findByName("compileDebug${it.name.capitalize()}KotlinNative")
@@ -727,4 +729,89 @@ class ExperimentalPluginTests {
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
)
}
}
}
}