From 48b1f71cefa180e25297e854e6f34fffab0cbbc9 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 17 May 2019 20:48:39 +0700 Subject: [PATCH] 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 --- .../gradle/dsl/mppNativeBinaryDSLCodegen.kt | 26 +++++-- .../kotlin/gradle/NewMultiplatformIT.kt | 57 +++++++++++++- .../groovy-dsl/build.gradle | 1 + .../kotlin-dsl/build.gradle.kts | 1 + .../new-mpp-native-tests/build.gradle | 51 +++++++++++++ .../new-mpp-native-tests/gradle.properties | 1 + .../src/anotherTest/kotlin/test.kt | 13 ++++ .../AbstractKotlinNativeBinaryContainer.kt | 56 ++++++++++++++ .../gradle/dsl/KotlinNativeBinaryContainer.kt | 64 ++++++++++------ .../targets/native/KotlinNativeCompilation.kt | 2 - .../native/KotlinNativeCompilationFactory.kt | 1 - .../native/KotlinNativeTargetConfigurator.kt | 75 ++++++++++--------- .../gradle/targets/native/NativeBinaries.kt | 47 ++++++------ .../targets/native/NativeBinaryTypes.kt | 5 ++ .../targets/native/tasks/KotlinNativeTasks.kt | 9 ++- .../targets/native/tasks/KotlinNativeTest.kt | 61 ++++++++++++--- 16 files changed, 366 insertions(+), 104 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-tests/src/anotherTest/kotlin/test.kt 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) } } }