Gradle, native: Introduce separate binary type for tests

Earlier native tests were represented by the same binary type as product
executables. We also used to create test tasks in the same manner as run
tasks for product executables. Such tasks could be obtained used a property
of an executable binary with type Exec.

But now we have a separate class for test tasks. Also we probably will want
to create several tasks for the same test. So we cannot use the same run task
property for both test and product executables. Also representing test and
product executables by the same binary type leads to the fact that assemble
task execution causes building not only product binaries but also the test
ones.

This patch solves the issues described above by introducing a separate binary
type for tests. Such a test binary can be created in the same manner as other
binaries:

    kotlin.macosX64 {
        binaries {
            test("integration") { ... }
        }
    }

One test binary and a task executing it is created by the plugin out of the box.
This test binary replaces a test executable used to be created earlier.

Issue #KT-31609 Fixed
This commit is contained in:
Ilya Matveev
2019-05-17 20:48:39 +07:00
parent 2db8409d85
commit 48b1f71cef
16 changed files with 366 additions and 104 deletions
@@ -17,17 +17,25 @@ internal data class BinaryType(
val nativeOutputKind: TypeName,
val factoryMethod: String,
val getMethod: String,
val findMethod: String
val findMethod: String,
val defaultBaseName: String
)
private fun binaryType(description: String, className: String, outputKind: String, baseMethodName: String) =
private fun binaryType(
description: String,
className: String,
outputKind: String,
baseMethodName: String,
defaultBaseName: String = "project.name"
) =
BinaryType(
description,
typeName("$MPP_PACKAGE.$className"),
typeName("${nativeOutputKindClass.fqName}.$outputKind"),
baseMethodName,
"get${baseMethodName.capitalize()}",
"find${baseMethodName.capitalize()}"
"find${baseMethodName.capitalize()}",
defaultBaseName
)
private val nativeBuildTypeClass = typeName("$MPP_PACKAGE.NativeBuildType")
@@ -41,6 +49,7 @@ private fun generateFactoryMethods(binaryType: BinaryType): String {
val nativeBuildType = nativeBuildTypeClass.renderShort()
val methodName = binaryType.factoryMethod
val binaryDescription = binaryType.description
val defaultBaseName = binaryType.defaultBaseName
return """
/** Creates $binaryDescription with the given [namePrefix] for each build type and configures it. */
@@ -56,7 +65,7 @@ private fun generateFactoryMethods(binaryType: BinaryType): String {
fun $methodName(
buildTypes: Collection<$nativeBuildType> = $nativeBuildType.DEFAULT_BUILD_TYPES,
configure: $className.() -> Unit = {}
) = createBinaries("", project.name, $outputKindClass.$outputKind, buildTypes, ::$className, configure)
) = createBinaries("", $defaultBaseName, $outputKindClass.$outputKind, buildTypes, ::$className, configure)
/** Creates $binaryDescription with the given [namePrefix] for each build type and configures it. */
@JvmOverloads
@@ -115,7 +124,14 @@ fun generateAbstractKotlinNativeBinaryContainer() {
binaryType("an executable","Executable", "EXECUTABLE", "executable"),
binaryType("a static library","StaticLibrary", "STATIC", "staticLib"),
binaryType("a shared library","SharedLibrary", "DYNAMIC", "sharedLib"),
binaryType("an Objective-C framework","Framework", "FRAMEWORK", "framework")
binaryType("an Objective-C framework","Framework", "FRAMEWORK", "framework"),
binaryType(
"a test executable",
"Test",
"TEST",
"test",
defaultBaseName = "\"test\""
)
)
val className = typeName("org.jetbrains.kotlin.gradle.dsl.AbstractKotlinNativeBinaryContainer")
@@ -942,7 +942,6 @@ class NewMultiplatformIT : BaseGradleIT() {
"fooReleaseExecutable" to "foo",
"barReleaseExecutable" to "bar",
"bazReleaseExecutable" to "my-baz",
"testDebugExecutable" to "test",
"test2ReleaseExecutable" to "test2",
"releaseStatic" to "native_binary",
"releaseShared" to "native_binary"
@@ -1136,19 +1135,73 @@ class NewMultiplatformIT : BaseGradleIT() {
fun testNativeTests() = with(Project("new-mpp-native-tests", gradleVersion)) {
val testTasks = listOf("macos64Test", "linux64Test", "mingw64Test")
val hostTestTask = "${nativeHostTargetName}Test"
val suffix = if (isWindows) "exe" else "kexe"
val defaultOutputFile = "build/bin/$nativeHostTargetName/debugTest/test.$suffix"
val anotherOutputFile = "build/bin/$nativeHostTargetName/anotherDebugTest/another.$suffix"
build("tasks") {
assertSuccessful()
println(output)
testTasks.forEach {
// We need to create tasks for all hosts
assertTrue(output.contains("$it - "), "There is no test task '$it' in the task list.")
}
}
// Check that tests are not built during the ":assemble" execution
build("assemble") {
assertSuccessful()
assertNoSuchFile(defaultOutputFile)
assertNoSuchFile(anotherOutputFile)
}
build("check") {
assertSuccessful()
assertTasksExecuted(":$hostTestTask")
assertFileExists(defaultOutputFile)
assertTestResults("testProject/new-mpp-native-tests/TEST-TestKt.xml", hostTestTask)
}
build("linkAnotherDebugTest${nativeHostTargetName}") {
assertSuccessful()
assertFileExists(anotherOutputFile)
}
// Check that test binaries can be accessed in a buildscript.
build("checkNewGetters") {
assertSuccessful()
listOf("test.$suffix", "another.$suffix").forEach {
assertContains("Get test: $it")
assertContains("Find test: $it")
}
}
// Check that accessing a test as an executable fails or returns null and shows the corresponding warning.
build("checkOldGet") {
assertFailed()
assertContains(
"""
|Probably you are accessing the default test binary using the 'binaries.getExecutable("test", DEBUG)' method.
|Since 1.3.40 tests are represented by a separate binary type. To get the default test binary, use:
|
| binaries.getTest(DEBUG)
""".trimMargin()
)
}
build("checkOldFind") {
assertSuccessful()
assertContains(
"""
|Probably you are accessing the default test binary using the 'binaries.findExecutable("test", DEBUG)' method.
|Since 1.3.40 tests are represented by a separate binary type. To get the default test binary, use:
|
| binaries.findTest(DEBUG)
""".trimMargin()
)
assertContains("Find test: null")
}
}
@Test
@@ -53,6 +53,7 @@ kotlin {
executable("test2", [RELEASE]) {
compilation = compilations["test"]
freeCompilerArgs.add("-tr")
}
sharedLib([RELEASE])
@@ -44,6 +44,7 @@ kotlin {
executable("test2") {
compilation = compilations["test"]
freeCompilerArgs.add("-tr")
}
sharedLib(listOf(RELEASE))
@@ -24,5 +24,56 @@ kotlin {
fromPreset(presets.macosX64, 'macos64')
fromPreset(presets.linuxX64, 'linux64')
fromPreset(presets.mingwX64, 'mingw64')
configure([macos64, linux64, mingw64]) {
compilations.create("anotherTest")
binaries {
test("another", [DEBUG]) {
compilation = compilations.anotherTest
}
}
}
}
sourceSets {
anotherTest
macos64AnotherTest.dependsOn(anotherTest)
linux64AnotherTest.dependsOn(anotherTest)
mingw64AnotherTest.dependsOn(anotherTest)
}
}
// Check that getting a test binary in an old way fails showing the corresponding warning
task checkOldGet {
doLast {
kotlin.targets {
configure([macos64, linux64, mingw64]) {
println("Get test: ${binaries.getExecutable("test", DEBUG)}")
}
}
}
}
// Check that finding a test binary in an old way returns null showing the corresponding warning.
task checkOldFind {
doLast {
kotlin.targets {
configure([macos64, linux64, mingw64]) {
println("Find test: ${binaries.findExecutable("test", DEBUG)}")
}
}
}
}
task checkNewGetters {
doLast {
kotlin.targets {
configure([macos64, linux64, mingw64]) {
println("Get test: ${binaries.getTest(DEBUG).outputFile.name}")
println("Find test: ${binaries.findTest(DEBUG).outputFile.name}")
println("Get test: ${binaries.getTest("another", DEBUG).outputFile.name}")
println("Find test: ${binaries.findTest("another", DEBUG).outputFile.name}")
}
}
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.foo.test
import kotlin.test.*
@Test
fun anotherTest() {
println("Another test")
}
@@ -142,6 +142,32 @@ abstract class AbstractKotlinNativeBinaryContainer : DomainObjectSet<NativeBinar
/** Returns an Objective-C framework with the empty name prefix and the given build type. Returns null if there is no such binary. */
fun findFramework(buildType: String): Framework? = findFramework("", buildType)
/** Returns a test executable with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/
abstract fun getTest(namePrefix: String, buildType: NativeBuildType): Test
/** Returns a test executable with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/
fun getTest(namePrefix: String, buildType: String): Test =
getTest(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase()))
/** Returns a test executable with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/
fun getTest(buildType: NativeBuildType): Test = getTest("", buildType)
/** Returns a test executable with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/
fun getTest(buildType: String): Test = getTest("", buildType)
/** Returns a test executable with the given [namePrefix] and the given build type. Returns null if there is no such binary. */
abstract fun findTest(namePrefix: String, buildType: NativeBuildType): Test?
/** Returns a test executable with the given [namePrefix] and the given build type. Returns null if there is no such binary. */
fun findTest(namePrefix: String, buildType: String): Test? =
findTest(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase()))
/** Returns a test executable with the empty name prefix and the given build type. Returns null if there is no such binary. */
fun findTest(buildType: NativeBuildType): Test? = findTest("", buildType)
/** Returns a test executable with the empty name prefix and the given build type. Returns null if there is no such binary. */
fun findTest(buildType: String): Test? = findTest("", buildType)
/** Creates an executable with the given [namePrefix] for each build type and configures it. */
@JvmOverloads
fun executable(
@@ -262,4 +288,34 @@ abstract class AbstractKotlinNativeBinaryContainer : DomainObjectSet<NativeBinar
configureClosure: Closure<*>
) = framework(buildTypes) { ConfigureUtil.configure(configureClosure, this) }
/** Creates a test executable with the given [namePrefix] for each build type and configures it. */
@JvmOverloads
fun test(
namePrefix: String,
buildTypes: Collection<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES,
configure: Test.() -> Unit = {}
) = createBinaries(namePrefix, namePrefix, NativeOutputKind.TEST, buildTypes, ::Test, configure)
/** Creates a test executable with the empty name prefix for each build type and configures it. */
@JvmOverloads
fun test(
buildTypes: Collection<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES,
configure: Test.() -> Unit = {}
) = createBinaries("", "test", NativeOutputKind.TEST, buildTypes, ::Test, configure)
/** Creates a test executable with the given [namePrefix] for each build type and configures it. */
@JvmOverloads
fun test(
namePrefix: String,
buildTypes: Collection<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES,
configureClosure: Closure<*>
) = test(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) }
/** Creates a test executable with the default name prefix for each build type and configures it. */
@JvmOverloads
fun test(
buildTypes: Collection<NativeBuildType> = NativeBuildType.DEFAULT_BUILD_TYPES,
configureClosure: Closure<*>
) = test(buildTypes) { ConfigureUtil.configure(configureClosure, this) }
}
@@ -32,6 +32,9 @@ open class KotlinNativeBinaryContainer @Inject constructor(
private val defaultCompilation: KotlinNativeCompilation
get() = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
private val defaultTestCompilation: KotlinNativeCompilation
get() = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME)
private val nameToBinary = mutableMapOf<String, NativeBinary>()
internal val prefixGroups: NamedDomainObjectSet<PrefixGroup> = project.container(PrefixGroup::class.java)
@@ -69,8 +72,16 @@ open class KotlinNativeBinaryContainer @Inject constructor(
override fun getByName(name: String): NativeBinary = nameToBinary.getValue(name)
override fun findByName(name: String): NativeBinary? = nameToBinary[name]
override fun getExecutable(namePrefix: String, buildType: NativeBuildType): Executable =
getBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE)
private fun checkDeprecatedTestAccess(namePrefix: String, buildType: NativeBuildType, warning: String) {
if (namePrefix == DEFAULT_TEST_NAME_PREFIX && buildType == DEFAULT_TEST_BUILD_TYPE) {
project.logger.warn(warning)
}
}
override fun getExecutable(namePrefix: String, buildType: NativeBuildType): Executable {
checkDeprecatedTestAccess(namePrefix, buildType, GET_TEST_DEPRECATION_WARNING)
return getBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE)
}
override fun getStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary =
getBinary(namePrefix, buildType, NativeOutputKind.STATIC)
@@ -81,9 +92,13 @@ open class KotlinNativeBinaryContainer @Inject constructor(
override fun getFramework(namePrefix: String, buildType: NativeBuildType): Framework =
getBinary(namePrefix, buildType, NativeOutputKind.FRAMEWORK)
override fun getTest(namePrefix: String, buildType: NativeBuildType): Test =
getBinary(namePrefix, buildType, NativeOutputKind.TEST)
override fun findExecutable(namePrefix: String, buildType: NativeBuildType): Executable? =
findBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE)
override fun findExecutable(namePrefix: String, buildType: NativeBuildType): Executable? {
checkDeprecatedTestAccess(namePrefix, buildType, FIND_TEST_DEPRECATED_WARNING)
return findBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE)
}
override fun findStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary? =
findBinary(namePrefix, buildType, NativeOutputKind.STATIC)
@@ -93,6 +108,9 @@ open class KotlinNativeBinaryContainer @Inject constructor(
override fun findFramework(namePrefix: String, buildType: NativeBuildType): Framework? =
findBinary(namePrefix, buildType, NativeOutputKind.FRAMEWORK)
override fun findTest(namePrefix: String, buildType: NativeBuildType): Test? =
findBinary(namePrefix, buildType, NativeOutputKind.TEST)
// endregion.
// region Factories
@@ -119,7 +137,8 @@ open class KotlinNativeBinaryContainer @Inject constructor(
"Cannot create ${outputKind.description}: $name. Binaries of this kind are not available for target ${target.name}"
}
val binary = create(name, baseName, buildType, defaultCompilation)
val compilation = if (outputKind == NativeOutputKind.TEST) defaultTestCompilation else defaultCompilation
val binary = create(name, baseName, buildType, compilation)
add(binary)
prefixGroup.binaries.add(binary)
nameToBinary[binary.name] = binary
@@ -131,28 +150,31 @@ open class KotlinNativeBinaryContainer @Inject constructor(
}
}
internal fun defaultTestExecutable(
configure: Executable.() -> Unit
) = createBinaries(
DEFAULT_TEST_NAME_PREFIX,
"test",
NativeOutputKind.EXECUTABLE,
listOf(DEFAULT_TEST_BUILD_TYPE),
{ name, baseName, buildType, compilation ->
Executable(name, baseName, buildType, compilation, true)
},
configure
)
internal fun getDefaultTestExecutable(): Executable =
getExecutable(DEFAULT_TEST_NAME_PREFIX, DEFAULT_TEST_BUILD_TYPE)
companion object {
internal val DEFAULT_TEST_BUILD_TYPE = NativeBuildType.DEBUG
internal val DEFAULT_TEST_NAME_PREFIX = "test"
internal fun generateBinaryName(prefix: String, buildType: NativeBuildType, outputKindClassifier: String) =
lowerCamelCaseName(prefix, buildType.getName(), outputKindClassifier)
// TODO: Remove in 1.3.50.
private val GET_TEST_DEPRECATION_WARNING = """
|
|Probably you are accessing the default test binary using the 'binaries.getExecutable("$DEFAULT_TEST_NAME_PREFIX", ${DEFAULT_TEST_BUILD_TYPE.name})' method.
|Since 1.3.40 tests are represented by a separate binary type. To get the default test binary, use:
|
| binaries.getTest(DEBUG)
|
""".trimMargin()
private val FIND_TEST_DEPRECATED_WARNING = """
|
|Probably you are accessing the default test binary using the 'binaries.findExecutable("$DEFAULT_TEST_NAME_PREFIX", ${DEFAULT_TEST_BUILD_TYPE.name})' method.
|Since 1.3.40 tests are represented by a separate binary type. To get the default test binary, use:
|
| binaries.findTest(DEBUG)
|
""".trimMargin()
}
// endregion.
@@ -42,8 +42,6 @@ class KotlinNativeCompilation(
// TODO: Move into the compilation task when the linking task does klib linking instead of compilation.
internal val commonSources: MutableSet<SourceDirectorySet> = mutableSetOf()
var isTestCompilation = false
var friendCompilationName: String? = null
internal val friendCompilation: KotlinNativeCompilation?
@@ -21,7 +21,6 @@ class KotlinNativeCompilationFactory(
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
isTestCompilation = true
}
}
}
@@ -25,6 +25,8 @@ import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import java.util.*
@@ -119,45 +121,26 @@ open class KotlinNativeTargetConfigurator(
destinationDir = binary.outputDirectory
addCompilerPlugins()
tasks.maybeCreate(target.artifactsTaskName).dependsOn(this)
tasks.maybeCreate(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(this)
if (binary !is Test) {
tasks.maybeCreate(target.artifactsTaskName).dependsOn(this)
tasks.maybeCreate(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(this)
}
}
}
private fun Project.createRunTask(binary: Executable) {
val taskName = binary.runTaskName ?: return
tasks.create(taskName, Exec::class.java).apply {
group = RUN_GROUP
description = "Executes Kotlin/Native executable ${binary.name} for target ${binary.target.name}"
if (binary.isDefaultTestExecutable) {
val testTask = createOrRegisterTask<KotlinNativeTest>(taskName) { testTask ->
testTask.group = LifecycleBasePlugin.VERIFICATION_GROUP
testTask.description = "Executes Kotlin/Native unit tests for target ${binary.target.name}."
testTask.targetName = binary.compilation.target.targetName
enabled = binary.target.konanTarget.isCurrentHost
testTask.enabled = binary.target.konanTarget.isCurrentHost
executable = binary.outputFile.absolutePath
workingDir = project.projectDir
testTask.executable = binary.outputFile
testTask.workingDir = project.projectDir.absolutePath
testTask.onlyIf { binary.outputFile.exists() }
testTask.dependsOn(binary.linkTaskName)
testTask.configureConventions()
}
kotlinTestRegistry.registerTestTask(testTask)
} else {
tasks.create(taskName, Exec::class.java).apply {
group = RUN_GROUP
description = "Executes Kotlin/Native executable ${binary.name} for target ${binary.target.name}"
enabled = binary.target.konanTarget.isCurrentHost
executable = binary.outputFile.absolutePath
workingDir = project.projectDir
onlyIf { binary.outputFile.exists() }
dependsOn(binary.linkTaskName)
}
onlyIf { binary.outputFile.exists() }
dependsOn(binary.linkTaskName)
}
}
@@ -181,7 +164,7 @@ open class KotlinNativeTargetConfigurator(
project.tasks.getByName(compilation.compileAllTaskName).dependsOn(compileTask)
if (compilation.compilationName == KotlinCompilation.MAIN_COMPILATION_NAME) {
if (compilation.compilationName == MAIN_COMPILATION_NAME) {
project.tasks.getByName(compilation.target.artifactsTaskName).apply {
dependsOn(compileTask)
}
@@ -248,9 +231,31 @@ open class KotlinNativeTargetConfigurator(
}
}
override fun configureTest(target: KotlinNativeTarget) {
target.binaries.defaultTestExecutable {
compilation = target.compilations.maybeCreate(TEST_COMPILATION_NAME)
override fun configureTest(target: KotlinNativeTarget): Unit = with(target.project) {
// We don't create test tasks for non-host platforms.
if (target.konanTarget !in listOf(KonanTarget.MACOS_X64, KonanTarget.MINGW_X64, KonanTarget.LINUX_X64)) {
return
}
val taskName = lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix)
target.binaries.test(listOf(NativeBuildType.DEBUG)) {
val testTask = createOrRegisterTask<KotlinNativeTest>(taskName) { testTask ->
testTask.group = LifecycleBasePlugin.VERIFICATION_GROUP
testTask.description = "Executes Kotlin/Native unit tests for target ${target.name}."
testTask.targetName = compilation.target.targetName
testTask.enabled = target.konanTarget.isCurrentHost
testTask.executable { outputFile }
testTask.workingDir = project.projectDir.absolutePath
testTask.onlyIf { outputFile.exists() }
testTask.dependsOn(linkTaskName)
testTask.configureConventions()
}
kotlinTestRegistry.registerTestTask(testTask)
}
}
@@ -12,10 +12,8 @@ import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.tasks.AbstractExecTask
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
@@ -83,19 +81,19 @@ sealed class NativeBinary(
override fun getName(): String = name
}
abstract class AbstractExecutable(
name: String,
baseName: String,
buildType: NativeBuildType,
compilation: KotlinNativeCompilation
) : NativeBinary(name, baseName, buildType, compilation)
class Executable constructor(
name: String,
baseName: String,
buildType: NativeBuildType,
compilation: KotlinNativeCompilation,
internal val isDefaultTestExecutable: Boolean
) : NativeBinary(name, baseName, buildType, compilation) {
constructor(
name: String,
baseName: String,
buildType: NativeBuildType,
compilation: KotlinNativeCompilation) : this(name, baseName, buildType, compilation, false)
compilation: KotlinNativeCompilation
) : AbstractExecutable(name, baseName, buildType, compilation) {
override val outputKind: NativeOutputKind
get() = NativeOutputKind.EXECUTABLE
@@ -114,20 +112,14 @@ class Executable constructor(
}
/**
* A name of task running this executable.
* A name of a task running this executable.
* Returns null if the executables's target is not a host one (macosX64, linuxX64 or mingw64).
*/
val runTaskName: String?
get() {
if (target.konanTarget !in listOf(KonanTarget.MACOS_X64, KonanTarget.LINUX_X64, KonanTarget.MINGW_X64)) {
return null
}
return if (isDefaultTestExecutable) {
lowerCamelCaseName(compilation.target.targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix)
} else {
lowerCamelCaseName("run", name, compilation.target.targetName)
}
get() = if (target.konanTarget in listOf(KonanTarget.MACOS_X64, KonanTarget.LINUX_X64, KonanTarget.MINGW_X64)) {
lowerCamelCaseName("run", name, compilation.target.targetName)
} else {
null
}
/**
@@ -138,6 +130,17 @@ class Executable constructor(
get() = runTaskName?.let { project.tasks.getByName(it) as AbstractExecTask<*> }
}
class Test(
name: String,
baseName: String,
buildType: NativeBuildType,
compilation: KotlinNativeCompilation
) : AbstractExecutable(name, baseName, buildType, compilation) {
override val outputKind: NativeOutputKind
get() = NativeOutputKind.TEST
}
class StaticLibrary(
name: String,
baseName: String,
@@ -32,6 +32,11 @@ enum class NativeOutputKind(
"executable",
description = "an executable"
),
TEST(
CompilerOutputKind.PROGRAM,
"test",
description = "a test executable"
),
DYNAMIC(
CompilerOutputKind.DYNAMIC,
"shared",
@@ -126,9 +126,6 @@ abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile<Ko
throw UnsupportedOperationException("Setting classpath directly is unsupported.")
}
val processTests
@Input get() = compilation.isTestCompilation
val target: String
@Input get() = compilation.target.konanTarget.name
@@ -273,7 +270,6 @@ abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile<Ko
addKey("-opt", optimized)
addKey("-g", debuggable)
addKey("-ea", debuggable)
addKey("-tr", processTests)
addArg("-target", target)
addArg("-p", outputKind.name.toLowerCase())
@@ -366,6 +362,10 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile() {
val linkerOpts: List<String>
get() = binary.linkerOpts
@get:Input
val processTests: Boolean
get() = binary is Test
@get:InputFiles
val exportLibraries: FileCollection
get() = binary.let {
@@ -392,6 +392,7 @@ open class KotlinNativeLink : AbstractKotlinNativeCompile() {
val superArgs = super.buildArgs(defaultsOnly)
return mutableListOf<String>().apply {
addAll(superArgs)
addKey("-tr", processTests)
addArgIfNotNull("-entry", entryPoint)
when (embedBitcode) {
Framework.BitcodeEmbeddingMode.MARKER -> add("-Xembed-bitcode-marker")
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.gradle.targets.native.tasks
import groovy.lang.Closure
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
@@ -19,31 +21,63 @@ import java.io.File
open class KotlinNativeTest : KotlinTest() {
@Suppress("LeakingThis")
@Internal
val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver)
private val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver)
@InputFile
@SkipWhenEmpty
val executableProperty: Property<File> = project.objects.property(File::class.java)
@Input
var args: List<String> = emptyList()
// Already taken into account in the executableProperty.
@get:Internal
var executable: File
@InputFile
@SkipWhenEmpty
get() = File(processOptions.executable)
get() = executableProperty.get()
set(value) {
processOptions.executable = value.absolutePath
executableProperty.set(value)
}
@get:Input
var workingDir: String
@Input get() = processOptions.workingDir.canonicalPath
get() = processOptions.workingDir.canonicalPath
set(value) {
processOptions.workingDir = File(value)
}
@Suppress("unused")
fun processOptions(options: ProcessForkOptions.() -> Unit) {
options(processOptions)
@get:Input
var environment: Map<String, Any>
get() = processOptions.environment
set(value) {
processOptions.environment = value
}
private fun <T> Property<T>.set(providerLambda: () -> T) = set(project.provider { providerLambda() })
fun executable(file: File) {
executableProperty.set(file)
}
fun executable(path: String) {
executableProperty.set { project.file(path) }
}
fun executable(provider: () -> File) {
executableProperty.set(provider)
}
fun executable(provider: Closure<File>) {
executableProperty.set(project.provider(provider))
}
fun environment(name: String, value: Any) {
processOptions.environment(name, value)
}
override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec {
val extendedForkOptions = DefaultProcessForkOptions(fileResolver)
processOptions.copyTo(extendedForkOptions)
extendedForkOptions.executable = executable.absolutePath
val clientSettings = TCServiceMessagesClientSettings(
name,
@@ -53,7 +87,7 @@ open class KotlinNativeTest : KotlinTest() {
stackTraceParser = ::parseKotlinNativeStackTraceAsJvm
)
val cliArgs = CliArgs("TEAMCITY", includePatterns, excludePatterns)
val cliArgs = CliArgs("TEAMCITY", includePatterns, excludePatterns, args)
return TCServiceMessagesTestExecutionSpec(
extendedForkOptions,
@@ -66,7 +100,8 @@ open class KotlinNativeTest : KotlinTest() {
private class CliArgs(
val testLogger: String? = null,
val testGradleFilter: Set<String> = setOf(),
val testNegativeGradleFilter: Set<String> = setOf()
val testNegativeGradleFilter: Set<String> = setOf(),
val userArgs: List<String> = emptyList()
) {
fun toList() = mutableListOf<String>().also {
if (testLogger != null) {
@@ -80,6 +115,8 @@ open class KotlinNativeTest : KotlinTest() {
if (testNegativeGradleFilter.isNotEmpty()) {
it.add("--ktest_negative_gradle_filter=${testNegativeGradleFilter.joinToString(",")}")
}
it.addAll(userArgs)
}
}
}