Add test for multiplatform projects with Gradle Kotlin DSL

* Adjust some of the test tools to work with *.kts scripts
* Expose the tool for version substitution into plugins DSL and use it
* Transform the lib-and-app test case to Gradle Kotlin DSL
This commit is contained in:
Sergey Igushkin
2018-11-13 16:37:02 +03:00
parent fe64d33ae4
commit 207bf7cf4a
29 changed files with 367 additions and 41 deletions
@@ -22,7 +22,7 @@ abstract class BaseGradleIT {
protected open fun defaultBuildOptions(): BuildOptions = BuildOptions(withDaemon = true)
protected open val defaultGradleVersion: GradleVersionRequired
open val defaultGradleVersion: GradleVersionRequired
get() = GradleVersionRequired.None
@Before
@@ -532,7 +532,14 @@ abstract class BaseGradleIT {
createGradleCommand(wrapperDir, createGradleTailParameters(options, params))
fun Project.gradleBuildScript(subproject: String? = null): File =
File(projectDir, subproject?.plus("/").orEmpty() + "build.gradle")
listOf("build.gradle", "build.gradle.kts").mapNotNull {
File(projectDir, subproject?.plus("/").orEmpty() + it).takeIf(File::exists)
}.single()
fun Project.gradleSettingsScript(): File =
listOf("settings.gradle", "settings.gradle.kts").mapNotNull {
File(projectDir, it).takeIf(File::exists)
}.single()
private fun Project.createGradleTailParameters(options: BuildOptions, params: Array<out String> = arrayOf()): List<String> =
params.toMutableList().apply {
@@ -35,10 +35,23 @@ class NewMultiplatformIT : BaseGradleIT() {
classesDir(sourceSet = "$targetName/$sourceSetName")
@Test
fun testLibAndApp() {
val libProject = Project("sample-lib", gradleVersion, "new-mpp-lib-and-app")
val appProject = Project("sample-app", gradleVersion, "new-mpp-lib-and-app")
val oldStyleAppProject = Project("sample-old-style-app", gradleVersion, "new-mpp-lib-and-app")
fun testLibAndApp() = doTestLibAndApp(
"sample-lib",
"sample-app",
gradleVersion
)
@Test
fun testLibAndAppWithGradleKotlinDsl() = doTestLibAndApp(
"sample-lib-gradle-kotlin-dsl",
"sample-app-gradle-kotlin-dsl",
GradleVersionRequired.AtLeast("4.10.2") // Using a SNAPSHOT version in the plugins DSL requires 4.10
)
fun doTestLibAndApp(libProjectName: String, appProjectName: String, gradleVersionRequired: GradleVersionRequired) {
val libProject = transformProjectWithPluginsDsl(libProjectName, gradleVersionRequired, "new-mpp-lib-and-app")
val appProject = transformProjectWithPluginsDsl(appProjectName, gradleVersionRequired, "new-mpp-lib-and-app")
val oldStyleAppProject = Project("sample-old-style-app", gradleVersionRequired, "new-mpp-lib-and-app")
val compileTasksNames =
listOf("Jvm6", "NodeJs", "Metadata", "Wasm32", nativeHostTargetName.capitalize()).map { ":compileKotlin$it" }
@@ -94,7 +107,7 @@ class NewMultiplatformIT : BaseGradleIT() {
with(appProject) {
setupWorkingDir()
gradleBuildScript().appendText("\nrepositories { maven { url '$libLocalRepoUri' } }")
gradleBuildScript().appendText("\nrepositories { maven { setUrl(\"$libLocalRepoUri\") } }")
fun CompiledProject.checkAppBuild() {
assertSuccessful()
@@ -141,8 +154,12 @@ class NewMultiplatformIT : BaseGradleIT() {
// Now run again with a project dependency instead of a module one:
libProject.projectDir.copyRecursively(projectDir.resolve(libProject.projectDir.name))
projectDir.resolve("settings.gradle").appendText("\ninclude '${libProject.projectDir.name}'")
gradleBuildScript().modify { it.replace("'com.example:sample-lib:1.0'", "project(':${libProject.projectDir.name}')") }
gradleSettingsScript().appendText("\ninclude(\"${libProject.projectDir.name}\")")
gradleBuildScript().modify { it.replace("\"com.example:sample-lib:1.0\"", "project(\":${libProject.projectDir.name}\")") }
gradleBuildScript(libProjectName).takeIf { it.extension == "kts" }?.modify {
it.replace(Regex("""\.version\(.*\)"""), "")
}
build("clean", "assemble", "--rerun-tasks") {
checkAppBuild()
@@ -3,7 +3,6 @@ package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.jetbrains.kotlin.gradle.util.modify
import org.junit.Assert
import org.junit.AssumptionViolatedException
import org.junit.Test
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
@@ -54,43 +53,42 @@ class PluginsDslIT : BaseGradleIT() {
private val GRADLE_VERSION = GradleVersionRequired.AtLeast("4.0")
private const val DIRECTORY_PREFIX = "pluginsDsl"
private const val MAVEN_LOCAL_URL_PLACEHOLDER = "<mavenLocalUrl>"
private const val PLUGIN_MARKER_VERSION_PLACEHOLDER = "<pluginMarkerVersion>"
private fun BaseGradleIT.projectWithMavenLocalPlugins(
projectName: String,
wrapperVersion: GradleVersionRequired = GRADLE_VERSION,
directoryPrefix: String? = DIRECTORY_PREFIX,
minLogLevel: LogLevel = LogLevel.DEBUG
): Project = transformProjectWithPluginsDsl(projectName, wrapperVersion, directoryPrefix, minLogLevel)
}
}
// Workaround for the restriction that snapshot versions are not supported
private val MARKER_VERSION = KOTLIN_VERSION + (System.getProperty("pluginMarkerVersionSuffix") ?: "")
private const val MAVEN_LOCAL_URL_PLACEHOLDER = "<mavenLocalUrl>"
private const val PLUGIN_MARKER_VERSION_PLACEHOLDER = "<pluginMarkerVersion>"
internal fun BaseGradleIT.transformProjectWithPluginsDsl(
projectName: String,
wrapperVersion: GradleVersionRequired = defaultGradleVersion,
directoryPrefix: String? = null,
minLogLevel: LogLevel = LogLevel.DEBUG
): BaseGradleIT.Project {
val result = Project(projectName, wrapperVersion, directoryPrefix, minLogLevel)
result.setupWorkingDir()
val settingsGradle = File(result.projectDir, "settings.gradle").takeIf(File::exists)
settingsGradle?.modify {
it.replace(MAVEN_LOCAL_URL_PLACEHOLDER, MavenLocalUrlProvider.mavenLocalUrl)
}
private fun projectWithMavenLocalPlugins(
projectName: String,
wrapperVersion: GradleVersionRequired = GRADLE_VERSION,
directoryPrefix: String? = DIRECTORY_PREFIX,
minLogLevel: LogLevel = LogLevel.DEBUG
): Project {
val result = Project(projectName, wrapperVersion, directoryPrefix, minLogLevel)
result.setupWorkingDir()
val settingsGradle = File(result.projectDir, "settings.gradle")
settingsGradle.modify {
val mavenLocalUrl = MavenLocalUrlProvider.mavenLocalUrl
it.replace(MAVEN_LOCAL_URL_PLACEHOLDER, mavenLocalUrl).apply {
if (this == it)
throw AssumptionViolatedException("$MAVEN_LOCAL_URL_PLACEHOLDER placeholder not found in settings.gradle")
result.projectDir.walkTopDown()
.filter { it.isFile && (it.name == "build.gradle" || it.name == "build.gradle.kts") }
.forEach { buildGradle ->
buildGradle.modify { text ->
text.replace(PLUGIN_MARKER_VERSION_PLACEHOLDER, KOTLIN_VERSION)
}
}
result.projectDir.walkTopDown()
.filter { it.isFile && it.name == "build.gradle" }
.forEach { buildGradle ->
buildGradle.modify { text ->
text.replace(PLUGIN_MARKER_VERSION_PLACEHOLDER, MARKER_VERSION)
}
}
return result
}
return result
}
/** Copies the logic of Gradle [`mavenLocal()`](https://docs.gradle.org/3.4.1/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html#org.gradle.api.artifacts.dsl.RepositoryHandler:mavenLocal())
@@ -0,0 +1,70 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeOutputKind.EXECUTABLE
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "com.example"
version = "1.0"
repositories {
mavenLocal()
jcenter()
maven { setUrl("http://dl.bintray.com/kotlin/kotlinx.html/") }
}
kotlin {
val jvm6 = jvm("jvm6")
val jvm8 = jvm("jvm8") {
compilations["main"].kotlinOptions.jvmTarget = "1.8"
}
val nodeJs = js("nodeJs")
val wasm32 = wasm32()
val linux64 = linuxX64("linux64")
val mingw64 = mingwX64("mingw64")
val macos64 = macosX64("macos64")
configure(listOf(wasm32, linux64, mingw64, macos64)) {
compilations.getByName("main") {
outputKinds.add(EXECUTABLE)
entryPoint = "com.example.app.native.main"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.example:sample-lib:1.0")
}
}
val allJvm by creating {
dependsOn(commonMain)
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
}
}
jvm6.compilations["main"].defaultSourceSet {
dependsOn(allJvm)
}
jvm8.compilations["main"].defaultSourceSet {
dependsOn(allJvm)
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}
}
nodeJs.compilations["main"].defaultSourceSet {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-js")
}
}
}
}
tasks.create("resolveRuntimeDependencies", DefaultTask::class.java) {
doFirst {
// KT-26301
val configName = kotlin.jvm("jvm6").compilations["main"].runtimeDependencyConfigurationName
configurations[configName].resolve()
}
}
@@ -0,0 +1,11 @@
rootProject.name = "sample-app"
enableFeaturePreview("GRADLE_METADATA")
pluginManagement {
repositories {
mavenLocal()
jcenter()
gradlePluginPortal()
}
}
@@ -0,0 +1,9 @@
package com.example.app
import com.example.lib.*
fun main(args: Array<String>) {
println(id(123))
println(idUsage(456))
println(x())
}
@@ -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()
}
@@ -0,0 +1,5 @@
package com.example.app
import kotlin.streams.asStream
fun useJdk8Api() = listOf(1, 2, 3).asSequence().asStream()
@@ -0,0 +1,13 @@
/*
* 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 com.example.app.native
import com.example.lib.*
fun main(args: Array<String>) {
id(id(123))
expectedFun()
}
@@ -0,0 +1,13 @@
/*
* 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 com.example.app.native
import com.example.lib.*
fun main(args: Array<String>) {
id(id(123))
expectedFun()
}
@@ -0,0 +1,13 @@
/*
* 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 com.example.app.native
import com.example.lib.*
fun main(args: Array<String>) {
id(id(123))
expectedFun()
}
@@ -0,0 +1,9 @@
import kotlin.js.Console
import com.example.lib.*
external val console: Console
fun nodeJsMain(args: Array<String>) {
console.info(id(123), idUsage(), expectedFun())
}
@@ -0,0 +1,13 @@
/*
* 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 com.example.app.native
import com.example.lib.*
fun main(args: Array<String>) {
id(id(123))
expectedFun()
}
@@ -0,0 +1,56 @@
plugins {
id("org.jetbrains.kotlin.multiplatform").version("<pluginMarkerVersion>")
id("maven-publish")
}
group = "com.example"
version = "1.0"
repositories {
mavenLocal()
jcenter()
maven { setUrl("http://dl.bintray.com/kotlin/kotlinx.html/") }
}
kotlin {
val jvm = jvm("jvm6")
val js = js("nodeJs")
wasm32()
linuxX64("linux64")
mingwX64("mingw64")
macosX64("macos64")
targets.all {
mavenPublication {
pom.withXml {
asNode().appendNode("name", "Sample MPP library")
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
}
}
jvm.compilations["main"].defaultSourceSet {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.23.4")
}
}
js.compilations["main"].defaultSourceSet {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-js")
implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.6.11")
}
}
}
}
publishing {
repositories {
maven { setUrl("file://${projectDir.absolutePath.replace('\\', '/')}/repo") }
}
}
@@ -0,0 +1,11 @@
rootProject.name = "sample-lib"
enableFeaturePreview("GRADLE_METADATA")
pluginManagement {
repositories {
mavenLocal()
jcenter()
gradlePluginPortal()
}
}
@@ -0,0 +1,5 @@
package com.example.lib
fun <T> id(t: T): T = t
expect fun expectedFun(): Unit
@@ -0,0 +1,7 @@
package com.example.lib
fun x(): String = "x"
actual fun expectedFun() {
println(id(x()))
}
@@ -0,0 +1,10 @@
/*
* 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 com.example.lib
actual fun expectedFun(): Unit {
id(123)
}
@@ -0,0 +1,10 @@
/*
* 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 com.example.lib
actual fun expectedFun(): Unit {
id(123)
}
@@ -0,0 +1,10 @@
/*
* 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 com.example.lib
actual fun expectedFun(): Unit {
id(123)
}
@@ -0,0 +1,12 @@
package com.example.lib
import com.example.lib.id
import com.example.lib.expectedFun
fun idUsage() = id("123")
actual fun expectedFun() = Unit
fun main(args: Array<String>) {
expectedFun()
}
@@ -0,0 +1,10 @@
/*
* 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 com.example.lib
actual fun expectedFun(): Unit {
id(123)
}