diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt index fd0b3542684..e650a2b0320 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt @@ -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") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index 8c40593e172..c9c75f2d10c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/groovy-dsl/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/groovy-dsl/build.gradle index f8687f5c274..b5fccf348c1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/groovy-dsl/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/groovy-dsl/build.gradle @@ -53,6 +53,7 @@ kotlin { executable("test2", [RELEASE]) { compilation = compilations["test"] + freeCompilerArgs.add("-tr") } sharedLib([RELEASE]) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/kotlin-dsl/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/kotlin-dsl/build.gradle.kts index 93a4fc8934b..a1f1b7993ce 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/kotlin-dsl/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-binaries/kotlin-dsl/build.gradle.kts @@ -44,6 +44,7 @@ kotlin { executable("test2") { compilation = compilations["test"] + freeCompilerArgs.add("-tr") } sharedLib(listOf(RELEASE)) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/build.gradle index 7ce402184bd..7ec28a19c2b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/build.gradle @@ -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}") + } + } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/gradle.properties new file mode 100644 index 00000000000..a4a181c3de2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/gradle.properties @@ -0,0 +1 @@ +kotlin.tests.individualTaskReports=true \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/src/anotherTest/kotlin/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/src/anotherTest/kotlin/test.kt new file mode 100644 index 00000000000..0c284d73ef5 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/src/anotherTest/kotlin/test.kt @@ -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") +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt index cb992852294..695755902d3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt @@ -142,6 +142,32 @@ abstract class AbstractKotlinNativeBinaryContainer : DomainObjectSet ) = 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.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.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.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.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = test(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt index e1b1e35350e..caac810d3d0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt @@ -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() internal val prefixGroups: NamedDomainObjectSet = 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. diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt index e5587f9cd28..af1a40b3c1c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt @@ -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 = mutableSetOf() - var isTestCompilation = false - var friendCompilationName: String? = null internal val friendCompilation: KotlinNativeCompilation? diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt index dfa75dba54c..2fdac4cf874 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilationFactory.kt @@ -21,7 +21,6 @@ class KotlinNativeCompilationFactory( KotlinNativeCompilation(target, name).apply { if (name == KotlinCompilation.TEST_COMPILATION_NAME) { friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME - isTestCompilation = true } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 59f3ecbec73..8254a0640e1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -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(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(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) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt index b7855cb7afb..99892aa386a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaries.kt @@ -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, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaryTypes.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaryTypes.kt index 51ca19f4170..5d3a6068d71 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaryTypes.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/NativeBinaryTypes.kt @@ -32,6 +32,11 @@ enum class NativeOutputKind( "executable", description = "an executable" ), + TEST( + CompilerOutputKind.PROGRAM, + "test", + description = "a test executable" + ), DYNAMIC( CompilerOutputKind.DYNAMIC, "shared", diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt index 1e3546e0ab9..83fd8b9ed17 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTasks.kt @@ -126,9 +126,6 @@ abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile 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().apply { addAll(superArgs) + addKey("-tr", processTests) addArgIfNotNull("-entry", entryPoint) when (embedBitcode) { Framework.BitcodeEmbeddingMode.MARKER -> add("-Xembed-bitcode-marker") diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt index b0cc9cbb0e7..5e30ab54dd1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/tasks/KotlinNativeTest.kt @@ -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 = project.objects.property(File::class.java) + + @Input + var args: List = 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 + get() = processOptions.environment + set(value) { + processOptions.environment = value + } + + private fun Property.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) { + 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 = setOf(), - val testNegativeGradleFilter: Set = setOf() + val testNegativeGradleFilter: Set = setOf(), + val userArgs: List = emptyList() ) { fun toList() = mutableListOf().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) } } }