Introduce tooling model for Kotlin gradle projects

A KotlinProject model for Gradle projects including Kotlin (JVM, JS or
common) plugins is introduced. Its model builder KotlinModelBuilder
produces an implementation of these models and can be queried through
the Gradle Tooling API when needed. Model builder is registered in the
corresponding Kotlin Gradle plugins.

Model definition should be published as a separate artifact in public
repositories so it can be consumed when needed.
This commit is contained in:
Lucas Smaira
2018-06-04 16:33:02 +01:00
committed by Sergey Igushkin
parent 4b03db771a
commit 9def6f020f
24 changed files with 900 additions and 16 deletions
@@ -1,7 +1,10 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.gradle.tooling.GradleConnector
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.model.ModelContainer
import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction
import org.jetbrains.kotlin.gradle.util.*
import org.junit.After
import org.junit.AfterClass
@@ -274,6 +277,18 @@ abstract class BaseGradleIT {
}
}
fun <T> Project.getModels(modelType: Class<T>): ModelContainer<T> {
if (!projectDir.exists()) {
setupWorkingDir()
}
val connection = GradleConnector.newConnector().forProjectDirectory(projectDir).connect()
val options = defaultBuildOptions()
val model = connection.action(ModelFetcherBuildAction(modelType)) .withArguments("-Pkotlin_version=" + options.kotlinVersion).run()
connection.close()
return model
}
fun CompiledProject.assertSuccessful() {
if (resultCode == 0) return
@@ -0,0 +1,154 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.model
import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class KotlinProjectIT : BaseGradleIT() {
@Test
fun testKotlinProject() {
val project = Project("kotlinProject")
val kotlinProject = project.getModels(KotlinProject::class.java).getModel(":")!!
kotlinProject.assertBasics("kotlinProject", defaultBuildOptions().kotlinVersion, KotlinProject.ProjectType.PLATFORM_JVM, "WARN")
assertTrue(kotlinProject.expectedByDependencies.isEmpty())
assertEquals(2, kotlinProject.sourceSets.size)
val mainSourceSet = kotlinProject.sourceSets.find { it.name == "main" }!!
val testSourceSet = kotlinProject.sourceSets.find { it.name == "test" }!!
mainSourceSet.assertBasics("main", SourceSet.SourceSetType.PRODUCTION, emptySet())
testSourceSet.assertBasics("test", SourceSet.SourceSetType.TEST, listOf("main"))
assertEquals(2, mainSourceSet.sourceDirectories.size)
assertEquals(1, mainSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/main/kotlin") }.size)
assertEquals(1, mainSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/main/java") }.size)
assertEquals(1, mainSourceSet.resourcesDirectories.size)
assertEquals(1, mainSourceSet.resourcesDirectories.filter { it.absolutePath.contains("src/main/resources") }.size)
assertTrue(mainSourceSet.classesOutputDirectory.absolutePath.contains("build/classes/kotlin/main"))
assertTrue(mainSourceSet.resourcesOutputDirectory.absolutePath.contains("build/resources/main"))
assertTrue(mainSourceSet.compilerArguments.compileClasspath.any { it.absolutePath.contains("guava") })
assertFalse(mainSourceSet.compilerArguments.compileClasspath.any { it.absolutePath.contains("testng") })
assertEquals(2, testSourceSet.sourceDirectories.size)
assertEquals(1, testSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/test/kotlin") }.size)
assertEquals(1, testSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/test/java") }.size)
assertEquals(1, testSourceSet.resourcesDirectories.size)
assertEquals(1, testSourceSet.resourcesDirectories.filter { it.absolutePath.contains("src/test/resources") }.size)
assertTrue(testSourceSet.classesOutputDirectory.absolutePath.contains("build/classes/kotlin/test"))
assertTrue(testSourceSet.resourcesOutputDirectory.absolutePath.contains("build/resources/test"))
assertTrue(testSourceSet.compilerArguments.compileClasspath.any { it.absolutePath.contains("guava") })
assertTrue(testSourceSet.compilerArguments.compileClasspath.any { it.absolutePath.contains("testng") })
}
@Test
fun testKotlinJavaProject() {
val project = Project("kotlinJavaProject")
val kotlinProject = project.getModels(KotlinProject::class.java).getModel(":")!!
kotlinProject.assertBasics(
"kotlinJavaProject",
defaultBuildOptions().kotlinVersion,
KotlinProject.ProjectType.PLATFORM_JVM,
"WARN"
)
assertEquals(3, kotlinProject.sourceSets.size)
val mainSourceSet = kotlinProject.sourceSets.find { it.name == "main" }!!
val deploySourceSet = kotlinProject.sourceSets.find { it.name == "deploy" }!!
val testSourceSet = kotlinProject.sourceSets.find { it.name == "test" }!!
mainSourceSet.assertBasics("main", SourceSet.SourceSetType.PRODUCTION, emptySet())
deploySourceSet.assertBasics("deploy", SourceSet.SourceSetType.PRODUCTION, emptySet())
testSourceSet.assertBasics("test", SourceSet.SourceSetType.TEST, listOf("main"))
}
@Test
fun testMultiplatformProject() {
val project = Project("multiplatformProject")
val models = project.getModels(KotlinProject::class.java)
val libKotlinProject = models.getModel(":lib")!!
val libJsKotlinProject = models.getModel(":libJs")!!
val libJvmKotlinProject = models.getModel(":libJvm")!!
libKotlinProject.assertBasics("lib", defaultBuildOptions().kotlinVersion, KotlinProject.ProjectType.PLATFORM_COMMON, "WARN")
libJsKotlinProject.assertBasics("libJs", defaultBuildOptions().kotlinVersion, KotlinProject.ProjectType.PLATFORM_JS, "WARN")
libJvmKotlinProject.assertBasics("libJvm", defaultBuildOptions().kotlinVersion, KotlinProject.ProjectType.PLATFORM_JVM, "WARN")
assertEquals(1, libJsKotlinProject.expectedByDependencies.size)
assertTrue(libJsKotlinProject.expectedByDependencies.contains(":lib"))
assertEquals(1, libJvmKotlinProject.expectedByDependencies.size)
assertTrue(libJvmKotlinProject.expectedByDependencies.contains(":lib"))
assertEquals(2, libJsKotlinProject.sourceSets.size)
val mainJsSourceSet = libJsKotlinProject.sourceSets.find { it.name == "main" }!!
val testJsSourceSet = libJsKotlinProject.sourceSets.find { it.name == "test" }!!
mainJsSourceSet.assertBasics("main", SourceSet.SourceSetType.PRODUCTION, emptySet())
testJsSourceSet.assertBasics("test", SourceSet.SourceSetType.TEST, listOf("main"))
assertEquals(1, mainJsSourceSet.sourceDirectories.size)
assertEquals(1, mainJsSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/main/kotlin") }.size)
assertEquals(1, mainJsSourceSet.resourcesDirectories.size)
assertEquals(1, mainJsSourceSet.resourcesDirectories.filter { it.absolutePath.contains("src/main/resources") }.size)
assertTrue(mainJsSourceSet.classesOutputDirectory.absolutePath.contains("build/classes/kotlin/main"))
assertTrue(mainJsSourceSet.resourcesOutputDirectory.absolutePath.contains("build/resources/main"))
assertEquals(1, testJsSourceSet.sourceDirectories.size)
assertEquals(1, testJsSourceSet.sourceDirectories.filter { it.absolutePath.contains("src/test/kotlin") }.size)
assertEquals(1, testJsSourceSet.resourcesDirectories.size)
assertEquals(1, testJsSourceSet.resourcesDirectories.filter { it.absolutePath.contains("src/test/resources") }.size)
assertTrue(testJsSourceSet.classesOutputDirectory.absolutePath.contains("build/classes/kotlin/test"))
assertTrue(testJsSourceSet.resourcesOutputDirectory.absolutePath.contains("build/resources/test"))
}
@Test
fun testCoroutinesProjectDSL() {
val project = Project("coroutinesProjectDSL")
val kotlinProject = project.getModels(KotlinProject::class.java).getModel(":")!!
kotlinProject.assertBasics(
"coroutinesProjectDSL",
defaultBuildOptions().kotlinVersion,
KotlinProject.ProjectType.PLATFORM_JVM,
"ENABLE"
)
}
companion object {
private fun KotlinProject.assertBasics(
expectedName: String,
expectedKotlinVersion: String,
expectedProjectType: KotlinProject.ProjectType,
expectedCoroutines: String
) {
assertEquals(1L, modelVersion)
assertEquals(expectedName, name)
assertEquals(expectedKotlinVersion, kotlinVersion)
assertEquals(expectedProjectType, projectType)
assertEquals(expectedCoroutines, experimentalFeatures.coroutines)
}
private fun SourceSet.assertBasics(
expectedName: String,
expectedType: SourceSet.SourceSetType,
expectedFriendSourceSets: Collection<String>
) {
assertEquals(expectedName, name)
assertEquals(expectedType, type)
assertEquals(expectedFriendSourceSets.toList(), friendSourceSets.toList())
}
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.model
import java.io.Serializable
/**
* Wraps models of a given type for all different projects inside a Gradle multi project.
*/
class ModelContainer<T> : Serializable {
// Key is the project path
private val modelContainer = HashMap<String, T>()
fun addModel(path: String, model: T) {
modelContainer[path] = model
}
fun getModel(path: String): T? {
return modelContainer[path]
}
fun hasModel(path: String): Boolean {
return modelContainer[path] != null
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.model
import org.gradle.tooling.BuildAction
import org.gradle.tooling.BuildController
import org.gradle.tooling.model.gradle.BasicGradleProject
import java.io.Serializable
class ModelFetcherBuildAction<T>(private val modelType: Class<T>) : BuildAction<ModelContainer<T>>, Serializable {
override fun execute(controller: BuildController): ModelContainer<T> {
val modelContainer = ModelContainer<T>()
modelContainer.populateModels(controller, controller.buildModel.rootProject)
return modelContainer
}
private fun ModelContainer<T>.populateModels(controller: BuildController, gradleProject: BasicGradleProject) {
val model = controller.findModel(gradleProject, modelType)
if (model != null && !hasModel(gradleProject.path)) {
addModel(gradleProject.path, model)
}
gradleProject.children.forEach { populateModels(controller, it) }
}
}