diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinExecution.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinExecution.kt new file mode 100644 index 00000000000..7aebc8ae828 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinExecution.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.Named +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution.ExecutionSource + +/** + * An execution of Kotlin code. Executions in a single family (e.g. test runs) are distinguished by [Named.getName]. + * Names may not be unique across different execution families, such as test runs of different targets. + * + * A particular kind of execution is a [KotlinTestRun] which runs tests. + */ +interface KotlinExecution : Named { + interface ExecutionSource + + /** + * The source of the executable code that this execution runs from. It is usually set via members of execution source support + * interfaces, such as [CompilationExecutionSourceSupport] or [ClasspathTestRunSourceSupport], or `setExecutionSourceFrom*` functions. + */ + val executionSource: SourceType +} + +interface KotlinTargetExecution : KotlinExecution { + val target: KotlinTarget +} + +/** + * An execution source that is produced by a [compilation]. + * + * See also: [CompilationExecutionSourceSupport]. + */ +interface CompilationExecutionSource> : ExecutionSource { + val compilation: CompilationType +} + +interface CompilationExecutionSourceSupport> { + /** + * Select a compilation to run the execution from. + * + * The [compilation]'s [KotlinCompilationToRunnableFiles.runtimeDependencyFiles] + * will be treated as runtime dependencies, and its [output] as runnable files. + * + * This overrides other [KotlinExecution.executionSource] selection options. + */ + fun setExecutionSourceFrom(compilation: T) +} + +interface ExecutionTaskHolder { + val executionTask: TaskProvider +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt index 87e7c4169c3..61e378c40d2 100644 --- a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTarget.kt @@ -54,4 +54,14 @@ interface KotlinTarget : Named, HasAttributes { val preset: KotlinTargetPreset? override fun getName(): String = targetName +} + +interface KotlinTargetWithTests> : KotlinTarget { + /** The container with the test run executions. + * A target may automatically create and configure a test run by the name [DEFAULT_TEST_RUN_NAME]. */ + val testRuns: NamedDomainObjectContainer + + companion object { + const val DEFAULT_TEST_RUN_NAME = "test" + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTestRun.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTestRun.kt new file mode 100644 index 00000000000..016e4be2fbd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTestRun.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.plugin + +import groovy.lang.Closure +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.testing.TestFilter +import org.gradle.util.ConfigureUtil + +interface KotlinTestRun : KotlinExecution { + fun filter(configureFilter: TestFilter.() -> Unit) + + fun filter(configureFilter: Closure<*>) = filter { ConfigureUtil.configureSelf(configureFilter, this) } +} + +interface KotlinTargetTestRun : + KotlinTestRun, + KotlinTargetExecution + +interface JvmClasspathTestRunSource : KotlinExecution.ExecutionSource { + val testClassesDirs: FileCollection + val classpath: FileCollection +} + +interface ClasspathTestRunSourceSupport { + /** + * Select the exact [classpath] to run the tests from. + * + * Only the classes from [testClasses] will be treated as tests. + * + * This overrides other [KotlinExecution.executionSource] selection options. + */ + fun setExecutionSourceFrom(classpath: FileCollection, testClassesDirs: FileCollection) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt index 2e03f905331..fbac5317cb9 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.generators.gradle.dsl import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.presetName internal class KotlinPresetEntry( @@ -21,6 +22,9 @@ internal const val MPP_PACKAGE = "org.jetbrains.kotlin.gradle.plugin.mpp" internal const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset" internal const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" +internal const val KOTLIN_NATIVE_TARGET_WITH_TESTS_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetWithTestsPreset" +internal const val KOTLIN_NATIVE_TARGET_WITH_TESTS_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetWithTests" + internal val jvmPresetEntry = KotlinPresetEntry( "jvm", typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"), @@ -39,11 +43,24 @@ internal val androidPresetEntry = KotlinPresetEntry( typeName("$MPP_PACKAGE.KotlinAndroidTarget") ) +// Note: modifying this set should also be reflected in the MPP plugin code, see 'setupDefaultPresets' +private val testableNativeTargets = setOf(KonanTarget.LINUX_X64, KonanTarget.MACOS_X64, KonanTarget.MINGW_X64) + internal val nativePresetEntries = HostManager().targets.map { (_, target) -> KotlinPresetEntry( target.presetName, - typeName(KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME), - typeName(KOTLIN_NATIVE_TARGET_CLASS_FQNAME) + typeName( + if (target in testableNativeTargets) + KOTLIN_NATIVE_TARGET_WITH_TESTS_PRESET_CLASS_FQNAME + else + KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME + ), + typeName( + if (target in testableNativeTargets) + KOTLIN_NATIVE_TARGET_WITH_TESTS_CLASS_FQNAME + else + KOTLIN_NATIVE_TARGET_CLASS_FQNAME + ) ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt index fdae157ed67..12f334f5676 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinMultiplatformExtension.kt @@ -9,9 +9,7 @@ import groovy.lang.Closure import org.gradle.api.InvalidUserCodeException import org.gradle.api.NamedDomainObjectCollection import org.gradle.util.ConfigureUtil -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast @@ -22,6 +20,10 @@ open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTarget override lateinit var targets: NamedDomainObjectCollection internal set + @Suppress("unused") // DSL + val testableTargets: NamedDomainObjectCollection> + get() = targets.withType(KotlinTargetWithTests::class.java) + internal var isGradleMetadataAvailable: Boolean = false internal var isGradleMetadataExperimental: Boolean = false diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt index c350ffe7117..018220d9846 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithPresetFunctions.kt @@ -9,6 +9,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmTargetPreset import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTests +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithTestsPreset import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget @@ -258,11 +260,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP fun linuxX64( name: String = "linuxX64", - configure: KotlinNativeTarget.() -> Unit = { } - ): KotlinNativeTarget = + configure: KotlinNativeTargetWithTests.() -> Unit = { } + ): KotlinNativeTargetWithTests = configureOrCreate( name, - presets.getByName("linuxX64") as KotlinNativeTargetPreset, + presets.getByName("linuxX64") as KotlinNativeTargetWithTestsPreset, configure ) @@ -333,11 +335,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP fun mingwX64( name: String = "mingwX64", - configure: KotlinNativeTarget.() -> Unit = { } - ): KotlinNativeTarget = + configure: KotlinNativeTargetWithTests.() -> Unit = { } + ): KotlinNativeTargetWithTests = configureOrCreate( name, - presets.getByName("mingwX64") as KotlinNativeTargetPreset, + presets.getByName("mingwX64") as KotlinNativeTargetWithTestsPreset, configure ) @@ -363,11 +365,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP fun macosX64( name: String = "macosX64", - configure: KotlinNativeTarget.() -> Unit = { } - ): KotlinNativeTarget = + configure: KotlinNativeTargetWithTests.() -> Unit = { } + ): KotlinNativeTargetWithTests = configureOrCreate( name, - presets.getByName("macosX64") as KotlinNativeTargetPreset, + presets.getByName("macosX64") as KotlinNativeTargetWithTestsPreset, configure ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/execution/KotlinAggregateExecutionSource.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/execution/KotlinAggregateExecutionSource.kt new file mode 100644 index 00000000000..c33fbdab731 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/execution/KotlinAggregateExecutionSource.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.execution + +import groovy.lang.Closure +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution.ExecutionSource + +/** + * An execution source that aggregates other [executionSources]. + */ +interface KotlinAggregateExecutionSource : ExecutionSource { + val executionSources: Iterable +} + +interface KotlinAggregatingExecution< + AggregatedSourceType : ExecutionSource, + AggregatingSourceType : KotlinAggregateExecutionSource, + AggregatedExecutionType : KotlinExecution + > : + KotlinExecution { + /** + * Configures all of the executions aggregated by this execution. If some of the executions are not yet created up to this point, + * [configure] will be called on them later, once they are created. + */ + fun configureAllExecutions(configure: AggregatedExecutionType.() -> Unit): Unit + + /** + * Returns the aggregated executions that are already configured up to this moment. + * Some test runs may be missing from the results if they are not yet configured. + */ + fun getConfiguredExecutions(): Iterable + + fun configureAllExecutions(configureClosure: Closure<*>) = configureAllExecutions { + ConfigureUtil.configureSelf( + configureClosure, + this + ) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index 1248c7e51c1..e2483a6d486 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.plugin +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Configuration @@ -25,30 +26,36 @@ import org.gradle.language.jvm.tasks.ProcessResources import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest -import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask -import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import java.util.concurrent.Callable +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.full.memberProperties -abstract class AbstractKotlinTargetConfigurator( - protected val createDefaultSourceSets: Boolean, - protected val createTestCompilation: Boolean -) { - open fun configureTarget( +interface KotlinTargetConfigurator { + fun configureTarget( target: KotlinTargetType ) { configureCompilationDefaults(target) configureCompilations(target) defineConfigurationsForTarget(target) configureArchivesAndComponent(target) - configureTest(target) configureBuild(target) + configurePlatformSpecificModel(target) } + fun configureCompilationDefaults(target: KotlinTargetType) + fun configureCompilations(target: KotlinTargetType) + fun defineConfigurationsForTarget(target: KotlinTargetType) + fun configureArchivesAndComponent(target: KotlinTargetType) + fun configureBuild(target: KotlinTargetType) - protected abstract fun configureArchivesAndComponent(target: KotlinTargetType) - protected abstract fun configureTest(target: KotlinTargetType) + fun configurePlatformSpecificModel(target: KotlinTargetType) = Unit +} + +abstract class AbstractKotlinTargetConfigurator( + protected val createDefaultSourceSets: Boolean, + protected val createTestCompilation: Boolean +) : KotlinTargetConfigurator { private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) { val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete @@ -62,17 +69,17 @@ abstract class AbstractKotlinTargetConfigurator } } - protected open fun configureCompilations(platformTarget: KotlinTargetType) { - val project = platformTarget.project - val main = platformTarget.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME) + override fun configureCompilations(target: KotlinTargetType) { + val project = target.project + val main = target.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME) - platformTarget.compilations.all { + target.compilations.all { project.registerOutputsForStaleOutputCleanup(it) setupCompilationDependencyFiles(project, it) } if (createTestCompilation) { - platformTarget.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply { + target.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply { compileDependencyFiles += main.output.allOutputs if (this is KotlinCompilationToRunnableFiles) { @@ -82,7 +89,7 @@ abstract class AbstractKotlinTargetConfigurator } } - protected fun configureCompilationDefaults(target: KotlinTargetType) { + override fun configureCompilationDefaults(target: KotlinTargetType) { val project = target.project target.compilations.all { compilation -> @@ -129,7 +136,7 @@ abstract class AbstractKotlinTargetConfigurator } } - protected open fun defineConfigurationsForTarget(target: KotlinTargetType) { + override fun defineConfigurationsForTarget(target: KotlinTargetType) { val project = target.project val configurations = project.configurations @@ -195,7 +202,7 @@ abstract class AbstractKotlinTargetConfigurator } } - protected fun configureBuild(target: KotlinTargetType) { + override fun configureBuild(target: KotlinTargetType) { val project = target.project val buildNeeded = project.tasks.getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME) @@ -305,23 +312,23 @@ internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationN internal val KotlinTarget.testTaskName: String get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix) -abstract class KotlinTargetConfigurator>( +abstract class KotlinOnlyTargetConfigurator, KotlinTargetType : KotlinOnlyTarget>( createDefaultSourceSets: Boolean, createTestCompilation: Boolean, val kotlinPluginVersion: String -) : AbstractKotlinTargetConfigurator>( +) : AbstractKotlinTargetConfigurator( createDefaultSourceSets, createTestCompilation ) { internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinSourceSetProcessor<*> - override fun configureCompilations(platformTarget: KotlinOnlyTarget) { - super.configureCompilations(platformTarget) + override fun configureCompilations(target: KotlinTargetType) { + super.configureCompilations(target) - platformTarget.compilations.all { compilation -> + target.compilations.all { compilation -> buildCompilationProcessor(compilation).run() if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) { - sourcesJarTask(compilation, platformTarget.targetName, platformTarget.targetName.toLowerCase()) + sourcesJarTask(compilation, target.targetName, target.targetName.toLowerCase()) } } } @@ -334,7 +341,7 @@ abstract class KotlinTargetConfigurator) { + override fun configureArchivesAndComponent(target: KotlinTargetType) { val project = target.project val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) @@ -365,30 +372,6 @@ abstract class KotlinTargetConfigurator) { - val testCompilation = - target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles<*> - ?: return // Otherwise, there is no runtime classpath - - val testTaskName = lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix) - val testTask = target.project.createOrRegisterTask(testTaskName) { testTask -> - testTask.targetName = target.disambiguationClassifier - } - - testTask.project.afterEvaluate { - // use afterEvaluate to override the JavaPlugin defaults for Test tasks - testTask.configure { testTask -> - testTask.conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs } - testTask.conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles } - testTask.description = "Runs the unit tests." - testTask.group = JavaBasePlugin.VERIFICATION_GROUP - testTask.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testTask) - } - } - - target.project.kotlinTestRegistry.registerTestTask(testTask) - } - private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) { val publications = configuration.outgoing @@ -398,6 +381,46 @@ abstract class KotlinTargetConfigurator, T : KotlinTargetWithTests<*, R>> + : KotlinTargetConfigurator { + + override fun configureTarget(target: T) { + super.configureTarget(target) + configureTest(target) + } + + val testRunClass: Class + + fun createTestRun(name: String, target: T): R + + fun configureTest(target: T) { + initializeTestRuns(target) + target.testRuns.create(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME) + } + + private fun initializeTestRuns(target: T) { + val project = target.project + + val testRunsPropertyName = KotlinTargetWithTests<*, *>::testRuns.name + val mutableProperty = + target::class.memberProperties + .find { it.name == testRunsPropertyName } as? KMutableProperty1<*, *> + ?: error( + "The ${this::class.qualifiedName} implementation of ${KotlinTargetWithTests::class.qualifiedName} must " + + "override the $testRunsPropertyName property with a var." + ) + + @Suppress("UNCHECKED_CAST") + (mutableProperty as KMutableProperty1, NamedDomainObjectContainer>) + .set( + target, + project.container(testRunClass) { testRunName -> + createTestRun(testRunName, target) + } + ) + } +} + internal fun Project.usageByName(usageName: String): Usage = project.objects.named(Usage::class.java, usageName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt index d7e8d4b8e52..1401a84b06f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.presetName class KotlinMultiplatformPlugin( @@ -137,8 +138,17 @@ class KotlinMultiplatformPlugin( add(KotlinJsTargetPreset(project, kotlinPluginVersion)) add(KotlinAndroidTargetPreset(project, kotlinPluginVersion)) add(KotlinJvmWithJavaTargetPreset(project, kotlinPluginVersion)) - HostManager().targets.forEach { _, target -> - add(KotlinNativeTargetPreset(target.presetName, project, target, kotlinPluginVersion)) + + // Note: modifying this set should also be reflected in the DSL code generator, see 'presetEntries.kt' + val testableNativeTargets = setOf(KonanTarget.LINUX_X64, KonanTarget.MACOS_X64, KonanTarget.MINGW_X64) + + HostManager().targets.forEach { (_, target) -> + add( + if (target in testableNativeTargets) + KotlinNativeTargetWithTestsPreset(target.presetName, project, target, kotlinPluginVersion) + else + KotlinNativeTargetPreset(target.presetName, project, target, kotlinPluginVersion) + ) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 83d8b73ecf1..7e0f24fce2e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Project import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.KotlinOnlyTargetConfigurator import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset abstract class KotlinOnlyTargetPreset, T : KotlinCompilation<*>>( @@ -16,7 +16,7 @@ abstract class KotlinOnlyTargetPreset, T : KotlinCompila protected val kotlinPluginVersion: String ) : KotlinTargetPreset { - protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator + protected abstract fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget): String? = target.targetName diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt index 86ef00bf7be..ef7b30cc563 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -254,7 +254,7 @@ internal fun javaApiUsageForMavenScoping() = JAVA_API } -open class KotlinOnlyTarget>( +abstract class KotlinOnlyTarget>( project: Project, override val platformType: KotlinPlatformType ) : AbstractKotlinTarget(project) { @@ -267,5 +267,4 @@ open class KotlinOnlyTarget>( override var disambiguationClassifier: String? = null internal set -} - +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinAggregatingTestRun.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinAggregatingTestRun.kt new file mode 100644 index 00000000000..71cd366b4dd --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinAggregatingTestRun.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.targets.js + +import groovy.lang.Closure +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.testing.TestFilter +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource +import org.jetbrains.kotlin.gradle.plugin.ExecutionTaskHolder +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution.ExecutionSource +import org.jetbrains.kotlin.gradle.plugin.KotlinTestRun +import org.jetbrains.kotlin.gradle.testing.KotlinAggregatingTestRun +import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport + + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlatformTestRun.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlatformTestRun.kt new file mode 100644 index 00000000000..21e83951dfc --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsPlatformTestRun.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.targets.js + +import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource +import org.jetbrains.kotlin.gradle.execution.KotlinAggregatingExecution +import org.jetbrains.kotlin.gradle.plugin.CompilationExecutionSource +import org.jetbrains.kotlin.gradle.plugin.CompilationExecutionSourceSupport +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetTestRun +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation +import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinJsSubTarget +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.testing.KotlinReportAggregatingTestRun +import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun +import org.jetbrains.kotlin.gradle.testing.requireCompilationOfTarget +import kotlin.properties.Delegates + +class JsCompilationExecutionSource(override val compilation: KotlinJsCompilation) : + CompilationExecutionSource + +open class KotlinJsPlatformTestRun(testRunName: String, subtarget: KotlinJsSubTarget) : + KotlinTaskTestRun(testRunName, subtarget.target), + CompilationExecutionSourceSupport { + + private var _executionSource: JsCompilationExecutionSource by Delegates.notNull() + + final override var executionSource: JsCompilationExecutionSource + get() = _executionSource + set(value) { + executionTask.configure { it.compilation = value.compilation } + _executionSource = value + } + + override fun setExecutionSourceFrom(compilation: KotlinJsCompilation) { + requireCompilationOfTarget(compilation, target) + + executionSource = JsCompilationExecutionSource(compilation) + } +} + +class JsAggregatingExecutionSource(private val aggregatingTestRun: KotlinJsReportAggregatingTestRun) : + KotlinAggregateExecutionSource { + + override val executionSources: Iterable + get() = aggregatingTestRun.getConfiguredExecutions().map { it.executionSource } +} + +open class KotlinJsReportAggregatingTestRun( + testRunName: String, + override val target: KotlinJsTarget +) : KotlinReportAggregatingTestRun(testRunName), + KotlinTargetTestRun, + CompilationExecutionSourceSupport { + + override fun setExecutionSourceFrom(compilation: KotlinJsCompilation) = configureAllExecutions { + setExecutionSourceFrom(compilation) + } + + override val executionSource: JsAggregatingExecutionSource + get() = JsAggregatingExecutionSource(this) + + private fun KotlinJsSubTarget.getChildTestExecution() = testRuns.maybeCreate(testRunName) + + override fun getConfiguredExecutions(): Iterable = mutableListOf().apply { + if (target.isNodejsConfigured) { + add(target.nodejs.getChildTestExecution()) + } + if (target.isBrowserConfigured) { + add(target.browser.getChildTestExecution()) + } + } + + override fun configureAllExecutions(configure: KotlinJsPlatformTestRun.() -> Unit) { + val doConfigureInChildren: KotlinJsSubTarget.() -> Unit = { + configure(getChildTestExecution()) + } + + target.whenBrowserConfigured { doConfigureInChildren(this as KotlinJsSubTarget) } + target.whenNodejsConfigured { doConfigureInChildren(this as KotlinJsSubTarget) } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt index a2d32c80f6f..3f88cf35168 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt @@ -5,11 +5,10 @@ package org.jetbrains.kotlin.gradle.targets.js +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Companion.runTaskNameSuffix -import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Companion.testTaskNameSuffix -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.TaskHolder import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl @@ -17,19 +16,22 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinNodeJs +import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport -import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry +import org.jetbrains.kotlin.gradle.testing.testTaskName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName class KotlinJsTarget(project: Project, platformType: KotlinPlatformType) : - KotlinOnlyTarget(project, platformType), KotlinJsTargetDsl { + KotlinOnlyTarget(project, platformType), + KotlinTargetWithTests, + KotlinJsTargetDsl +{ + override lateinit var testRuns: NamedDomainObjectContainer + internal set - val testTaskName get() = lowerCamelCaseName(disambiguationClassifier, testTaskNameSuffix) + val testTaskName get() = testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).testTaskName val testTask: TaskHolder - get() = project.kotlinTestRegistry.getOrCreateAggregatedTestTask( - name = testTaskName, - description = "Run JS tests for all platforms" - ) + get() = checkNotNull(project.locateTask(testTaskName)) val runTaskName get() = lowerCamelCaseName(disambiguationClassifier, runTaskNameSuffix) val runTask @@ -37,23 +39,56 @@ class KotlinJsTarget(project: Project, platformType: KotlinPlatformType) : it.description = "Run js on all configured platforms" } - val browser by lazy { + private val browserLazyDelegate = lazy { KotlinBrowserJs(this).also { it.configure() + browserConfiguredHandlers.forEach { handler -> handler(it) } + browserConfiguredHandlers.clear() } } - val nodejs by lazy { - KotlinNodeJs(this).also { - it.configure() - } - } + + private val browserConfiguredHandlers = mutableListOf Unit>() + + val browser by browserLazyDelegate + + internal val isBrowserConfigured: Boolean = browserLazyDelegate.isInitialized() override fun browser(body: KotlinJsBrowserDsl.() -> Unit) { - browser.body() + body(browser) } + private val nodejsLazyDelegate = lazy { + KotlinNodeJs(this).also { + it.configure() + nodejsConfiguredHandlers.forEach { handler -> handler(it) } + nodejsConfiguredHandlers.clear() + } + } + + private val nodejsConfiguredHandlers = mutableListOf Unit>() + + val nodejs by nodejsLazyDelegate + + internal val isNodejsConfigured: Boolean = nodejsLazyDelegate.isInitialized() + override fun nodejs(body: KotlinJsNodeDsl.() -> Unit) { - nodejs.body() + body(nodejs) + } + + fun whenBrowserConfigured(body: KotlinJsBrowserDsl.() -> Unit) { + if (browserLazyDelegate.isInitialized()) { + browser(body) + } else { + browserConfiguredHandlers += body + } + } + + fun whenNodejsConfigured(body: KotlinJsNodeDsl.() -> Unit) { + if (nodejsLazyDelegate.isInitialized()) { + nodejs(body) + } else { + nodejsConfiguredHandlers += body + } } fun useCommonJs() { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt index 1583e6be590..c69e009da77 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt @@ -5,29 +5,45 @@ package org.jetbrains.kotlin.gradle.targets.js +import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.KotlinOnlyTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTestsConfigurator import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider +import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport +import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry +import org.jetbrains.kotlin.gradle.testing.testTaskName open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : - KotlinTargetConfigurator(true, true, kotlinPluginVersion) { + KotlinOnlyTargetConfigurator(true, true, kotlinPluginVersion), + KotlinTargetWithTestsConfigurator { - override fun configureTarget(target: KotlinOnlyTarget) { - target as KotlinJsTarget + override val testRunClass: Class get() = KotlinJsReportAggregatingTestRun::class.java - super.configureTarget(target) - } + override fun createTestRun( + name: String, + target: KotlinJsTarget + ): KotlinJsReportAggregatingTestRun { + val result = KotlinJsReportAggregatingTestRun(name, target) - override fun configureTest(target: KotlinOnlyTarget) { - target as KotlinJsTarget + val testTask = target.project.kotlinTestRegistry.getOrCreateAggregatedTestTask( + name = result.testTaskName, + description = "Run JS tests for all platforms" + ) - // always create jsTest task for compatibility (KT-31527) - // actual tests tasks for browser and nodejs will be configured in KotlinJsSubTarget.configure - target.testTask + // workaround to avoid the infinite recursion in item factories of the target and the subtargets: + target.testRuns.matching { it.name == name }.whenObjectAdded { + it.configureAllExecutions { + // do not do anything with the aggregated test run, but ensure that they are created + } + } + + @Suppress("UNCHECKED_CAST") + result.executionTask = testTask.getTaskOrProvider() as TaskProvider + + return result } override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> { @@ -35,10 +51,10 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : return Kotlin2JsSourceSetProcessor(compilation.target.project, tasksProvider, compilation, kotlinPluginVersion) } - override fun configureCompilations(platformTarget: KotlinOnlyTarget) { - super.configureCompilations(platformTarget) + override fun configureCompilations(target: KotlinJsTarget) { + super.configureCompilations(target) - platformTarget.compilations.all { + target.compilations.all { it.compileKotlinTask.kotlinOptions { moduleKind = "umd" sourceMap = true diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt index a2bdaf9245f..a3f4ee9386a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt @@ -6,7 +6,10 @@ package org.jetbrains.kotlin.gradle.targets.js.dsl import groovy.lang.Closure +import org.gradle.api.NamedDomainObjectContainer import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun +import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack @@ -27,6 +30,8 @@ interface KotlinJsTargetDsl { ConfigureUtil.configure(fn, this) } } + + val testRuns: NamedDomainObjectContainer } interface KotlinJsSubTargetDsl { @@ -36,6 +41,8 @@ interface KotlinJsSubTargetDsl { ConfigureUtil.configure(fn, this) } } + + val testRuns: NamedDomainObjectContainer } interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt index 7c11b1dd560..4722c8b13f1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt @@ -5,18 +5,21 @@ package org.jetbrains.kotlin.gradle.targets.js.subtargets +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.whenEvaluated +import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask +import org.jetbrains.kotlin.gradle.tasks.locateTask import org.jetbrains.kotlin.gradle.testing.internal.configureConventions import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName @@ -31,6 +34,9 @@ abstract class KotlinJsSubTarget( val runTaskName = disambiguateCamelCased("run") val testTaskName = disambiguateCamelCased("test") + override val testRuns: NamedDomainObjectContainer = + project.container(KotlinJsPlatformTestRun::class.java) { name -> KotlinJsPlatformTestRun(name, this) } + fun configure() { NpmResolverPlugin.apply(project) @@ -47,17 +53,27 @@ abstract class KotlinJsSubTarget( lowerCamelCaseName(target.disambiguationClassifier, disambiguationClassifier, name) private fun configureTests() { - target.compilations.all { compilation -> - if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { - configureTests(compilation) - } + testRuns.all { configureTestRunDefaults(it) } + testRuns.create(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME) + } + + protected open fun configureTestRunDefaults(testRun: KotlinJsPlatformTestRun) { + target.compilations.matching { it.name == KotlinCompilation.TEST_COMPILATION_NAME }.all { compilation -> + configureTestsRun(testRun, compilation) } } abstract val testTaskDescription: String - private fun configureTests(compilation: KotlinJsCompilation) { - val testJs = project.createOrRegisterTask(testTaskName) { testJs -> + private fun configureTestsRun(testRun: KotlinJsPlatformTestRun, compilation: KotlinJsCompilation) { + fun KotlinJsPlatformTestRun.subtargetTestTaskName(): String = disambiguateCamelCased( + lowerCamelCaseName( + name.takeIf { it != KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME }, + AbstractKotlinTargetConfigurator.testTaskNameSuffix + ) + ) + + val testJs = project.createOrRegisterTask(testRun.subtargetTestTaskName()) { testJs -> val compileTask = compilation.compileKotlinTask testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP @@ -77,7 +93,14 @@ abstract class KotlinJsSubTarget( testJs.configureConventions() } - target.project.kotlinTestRegistry.registerTestTask(testJs, target.testTask.doGetTask()) + testRun.executionTask = testJs.getTaskOrProvider() as TaskProvider + + target.testRuns.matching { it.name == testRun.name }.all { parentTestRun -> + target.project.kotlinTestRegistry.registerTestTask( + testJs, + parentTestRun.executionTask.get() + ) + } project.whenEvaluated { testJs.configure { @@ -101,6 +124,6 @@ abstract class KotlinJsSubTarget( protected abstract fun configureRun(compilation: KotlinJsCompilation) override fun testTask(body: KotlinJsTest.() -> Unit) { - (project.tasks.getByName(testTaskName) as KotlinJsTest).body() + project.locateTask(testTaskName)!!.configure(body) } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt index d5d798fd895..386d6cb1468 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTarget.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.targets.jvm import org.gradle.api.InvalidUserCodeException +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.plugins.JavaPlugin @@ -23,9 +24,11 @@ import java.util.concurrent.Callable class KotlinJvmTarget( project: Project -) : KotlinOnlyTarget( - project, KotlinPlatformType.jvm -) { +) : KotlinOnlyTarget(project, KotlinPlatformType.jvm), + KotlinTargetWithTests { + + override lateinit var testRuns: NamedDomainObjectContainer + var withJavaEnabled = false private set @@ -162,4 +165,5 @@ class KotlinJvmTarget( project.addExtendsFromRelation(compilation.runtimeDependencyConfigurationName, configurationName) } } -} \ No newline at end of file +} + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt index ec4a75da3ed..09cfa5b4b2b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTargetConfigurator.kt @@ -5,14 +5,50 @@ package org.jetbrains.kotlin.gradle.targets.jvm +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.Kotlin2JvmSourceSetProcessor import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider +import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask +import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry +import org.jetbrains.kotlin.gradle.testing.testTaskName class KotlinJvmTargetConfigurator(kotlinPluginVersion: String) : - KotlinTargetConfigurator(true, true, kotlinPluginVersion) { + KotlinOnlyTargetConfigurator(true, true, kotlinPluginVersion), + KotlinTargetWithTestsConfigurator { + + override val testRunClass: Class + get() = KotlinJvmTestRun::class.java + + override fun createTestRun( + name: String, + target: KotlinJvmTarget + ): KotlinJvmTestRun = KotlinJvmTestRun(name, target).apply { + val testTaskOrProvider = target.project.createOrRegisterTask(testTaskName) { testTask -> + testTask.targetName = target.disambiguationClassifier + testTask.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testTask) + } + + executionTask = testTaskOrProvider.getTaskOrProvider() as TaskProvider + + val testCompilation = target.compilations.getByName(KotlinCompilation.TEST_COMPILATION_NAME) + + setExecutionSourceFrom(testCompilation) + + target.project.whenEvaluated { + // use afterEvaluate to override the JavaPlugin defaults for Test tasks + testTaskOrProvider.configure { testTask -> + testTask.description = "Runs the tests of the $name test run." + testTask.group = JavaBasePlugin.VERIFICATION_GROUP + } + } + + target.project.kotlinTestRegistry.registerTestTask(testTaskOrProvider) + } override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> { val tasksProvider = KotlinTasksProvider(compilation.target.targetName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTestRun.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTestRun.kt new file mode 100644 index 00000000000..a6fc642114c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/KotlinJvmTestRun.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.targets.jvm + +import org.gradle.api.file.FileCollection +import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest +import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun +import org.jetbrains.kotlin.gradle.testing.requireCompilationOfTarget +import kotlin.properties.Delegates + +internal class ClasspathOnlyTestRunSource( + override val classpath: FileCollection, + override val testClassesDirs: FileCollection +) : JvmClasspathTestRunSource + +internal open class JvmCompilationsTestRunSource( + val classpathCompilations: Iterable, + val testCompilations: Iterable +) : JvmClasspathTestRunSource { + private val project get() = testCompilations.first().target.project + + override val testClassesDirs: FileCollection + get() = project.files(testCompilations.map { it.output.classesDirs }) + + override val classpath: FileCollection + get() = project.files( + (testCompilations + classpathCompilations).distinct().map { it.output.allOutputs + it.runtimeDependencyFiles } + ) +} + +internal class SingleJvmCompilationTestRunSource( + override val compilation: KotlinJvmCompilation +) : JvmCompilationsTestRunSource(listOf(compilation), listOf(compilation)), CompilationExecutionSource + +open class KotlinJvmTestRun(testRunName: String, override val target: KotlinJvmTarget) : + KotlinTaskTestRun(testRunName, target), + CompilationExecutionSourceSupport, + ClasspathTestRunSourceSupport { + + override fun setExecutionSourceFrom(classpath: FileCollection, testClassesDirs: FileCollection) { + executionSource = ClasspathOnlyTestRunSource(classpath, testClassesDirs) + } + + fun setExecutionSourceFrom( + classpathCompilations: Iterable, + testClassesCompilations: Iterable + ) { + classpathCompilations.forEach { requireCompilationOfTarget(it, target) } + executionSource = JvmCompilationsTestRunSource(classpathCompilations, testClassesCompilations) + } + + override fun setExecutionSourceFrom(compilation: KotlinJvmCompilation) { + executionSource = SingleJvmCompilationTestRunSource(compilation) + } + + private var _executionSource: JvmClasspathTestRunSource by Delegates.notNull() + + final override var executionSource: JvmClasspathTestRunSource + get() = _executionSource + private set(value) { + setTestTaskClasspathAndClassesDirs(value.classpath, value.testClassesDirs) + } + + private fun setTestTaskClasspathAndClassesDirs(classpath: FileCollection, testClassesDirs: FileCollection) { + executionTask.configure { + it.classpath = classpath + it.testClassesDirs = testClassesDirs + } + } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt index 48f4b674a34..6fc2c634f8b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/jvm/tasks/KotlinJvmTest.kt @@ -9,6 +9,9 @@ import org.gradle.api.internal.tasks.testing.* import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.testing.TestFilter +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinTestRun open class KotlinJvmTest : Test() { @Input diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt index 2b09dd094ed..7026426a13b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetConfigurator.kt @@ -35,7 +35,7 @@ internal val Project.isKotlinGranularMetadataEnabled: Boolean get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : - KotlinTargetConfigurator( + KotlinOnlyTargetConfigurator( createDefaultSourceSets = false, createTestCompilation = false, kotlinPluginVersion = kotlinPluginVersion @@ -59,7 +59,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : private val KotlinOnlyTarget.apiElementsConfiguration: Configuration get() = project.configurations.getByName(apiElementsConfigurationName) - override fun configureTarget(target: KotlinOnlyTarget) { + override fun configureTarget(target: KotlinMetadataTarget) { super.configureTarget(target) if (target.project.isKotlinGranularMetadataEnabled) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt index 648e8dac671..47a6188f0e7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/metadata/KotlinMetadataTargetPreset.kt @@ -12,14 +12,14 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.KotlinOnlyTargetConfigurator import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinTask import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator class KotlinMetadataTargetPreset( project: Project, kotlinPluginVersion: String -) : KotlinOnlyTargetPreset, KotlinCommonCompilation>( +) : KotlinOnlyTargetPreset( project, kotlinPluginVersion ) { @@ -37,12 +37,12 @@ class KotlinMetadataTargetPreset( const val PRESET_NAME = "metadata" } - override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator = + override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator = KotlinMetadataTargetConfigurator(kotlinPluginVersion) - override fun instantiateTarget(): KotlinOnlyTarget = KotlinMetadataTarget(project) + override fun instantiateTarget(): KotlinMetadataTarget = KotlinMetadataTarget(project) - override fun createTarget(name: String): KotlinOnlyTarget = + override fun createTarget(name: String): KotlinMetadataTarget = super.createTarget(name).apply { val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeBinaryTestRun.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeBinaryTestRun.kt new file mode 100644 index 00000000000..e6a29738069 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeBinaryTestRun.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.targets.native + +import org.jetbrains.kotlin.gradle.plugin.CompilationExecutionSource +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetTestRun +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.TestExecutable +import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest +import org.jetbrains.kotlin.gradle.testing.KotlinTaskTestRun + +class NativeBinaryTestRunSource(val binary: TestExecutable) : + CompilationExecutionSource { + + override val compilation: KotlinNativeCompilation + get() = binary.compilation +} + +interface KotlinNativeBinaryTestRun : KotlinTargetTestRun { + /** + * Sets this test run to use the specified [testExecutable]. + * + * This overrides other [executionSource] options. + */ + fun setExecutionSourceFrom(testExecutable: TestExecutable) +} + +open class DefaultKotlinNativeTestRun(testRunName: String, target: KotlinNativeTarget) : + KotlinTaskTestRun(testRunName, target), + KotlinNativeBinaryTestRun { + + private lateinit var _executionSource: NativeBinaryTestRunSource + + final override var executionSource: NativeBinaryTestRunSource + get() = _executionSource + private set(value) { + executionTask.configure { + it.executable(value.binary.linkTask) { value.binary.outputFile } + } + _executionSource = value + } + + override fun setExecutionSourceFrom(testExecutable: TestExecutable) { + require(testExecutable.target === target) { + "Expected a test fbinary of target ${target.name}, " + + "got the binary ${testExecutable.name} of target ${testExecutable.target.name}" + } + + executionSource = NativeBinaryTestRunSource(testExecutable) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt index 179531d357f..e99ed56d9c2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTarget.kt @@ -7,16 +7,20 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import groovy.lang.Closure +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.attributes.Attribute import org.gradle.util.ConfigureUtil import org.gradle.util.WrapUtil import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests +import org.jetbrains.kotlin.gradle.targets.native.KotlinNativeBinaryTestRun +import org.jetbrains.kotlin.gradle.targets.native.NativeBinaryTestRunSource import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.konan.target.KonanTarget -class KotlinNativeTarget( +open class KotlinNativeTarget( project: Project, val konanTarget: KonanTarget ) : KotlinOnlyTarget(project, KotlinPlatformType.native) { @@ -61,4 +65,13 @@ class KotlinNativeTarget( String::class.java ) } +} + +open class KotlinNativeTargetWithTests( + project: Project, + konanTarget: KonanTarget +) : KotlinNativeTarget(project, konanTarget), KotlinTargetWithTests { + + override lateinit var testRuns: NamedDomainObjectContainer + internal set } \ 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 e3628201cf9..431f4c4e2aa 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 @@ -17,22 +17,24 @@ import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet import org.gradle.api.plugins.BasePlugin import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.targets.native.DefaultKotlinNativeTestRun +import org.jetbrains.kotlin.gradle.targets.native.KotlinNativeBinaryTestRun 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 org.jetbrains.kotlin.gradle.testing.testTaskName import java.io.File import java.util.* -open class KotlinNativeTargetConfigurator( +open class KotlinNativeTargetConfigurator( private val kotlinPluginVersion: String -) : AbstractKotlinTargetConfigurator( +) : AbstractKotlinTargetConfigurator( createDefaultSourceSets = true, createTestCompilation = true ) { @@ -212,15 +214,14 @@ open class KotlinNativeTargetConfigurator( // endregion. // region Configuration. - override fun configureTarget(target: KotlinNativeTarget) { - super.configureTarget(target) + override fun configurePlatformSpecificModel(target: T) { configureBinaries(target) configureFrameworkExport(target) configureCInterops(target) warnAboutIncorrectDependencies(target) } - override fun configureArchivesAndComponent(target: KotlinNativeTarget): Unit = with(target.project) { + override fun configureArchivesAndComponent(target: T): Unit = with(target.project) { tasks.create(target.artifactsTaskName) target.compilations.all { createKlibCompilationTask(it) @@ -231,35 +232,6 @@ open class KotlinNativeTargetConfigurator( } } - override fun configureTest(target: KotlinNativeTarget): Unit = with(target.project) { - // We create test binaries for all platforms. - target.binaries.test(listOf(NativeBuildType.DEBUG)) { - - // But run them only on host ones. - if (target.konanTarget in listOf(KonanTarget.MACOS_X64, KonanTarget.MINGW_X64, KonanTarget.LINUX_X64)) { - - val taskName = lowerCamelCaseName(target.disambiguationClassifier, testTaskNameSuffix) - 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) - } - } - } - protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) { target.compilations.all { compilation -> createCInteropTasks(compilation) @@ -306,6 +278,11 @@ open class KotlinNativeTargetConfigurator( project.tasks.getByName(it.compilation.binariesTaskName).dependsOn(it.linkTaskName) } } + + /** + * We create test binaries for all platforms but test runs only for host platforms, see [KotlinNativeTargetWithTestsConfigurator] + */ + target.binaries.test(listOf(NativeBuildType.DEBUG)) { } } fun configureFrameworkExport(target: KotlinNativeTarget) { @@ -335,7 +312,7 @@ open class KotlinNativeTargetConfigurator( } } - override fun defineConfigurationsForTarget(target: KotlinNativeTarget) { + override fun defineConfigurationsForTarget(target: T) { super.defineConfigurationsForTarget(target) val configurations = target.project.configurations @@ -411,3 +388,35 @@ open class KotlinNativeTargetConfigurator( } } } + +class KotlinNativeTargetWithTestsConfigurator(kotlinPluginVersion: String) + : KotlinNativeTargetConfigurator(kotlinPluginVersion), + KotlinTargetWithTestsConfigurator { + + override val testRunClass: Class + get() = KotlinNativeBinaryTestRun::class.java + + override fun createTestRun(name: String, target: KotlinNativeTargetWithTests): KotlinNativeBinaryTestRun = + DefaultKotlinNativeTestRun(name, target).apply { + val project = target.project + + val testTaskOrProvider = project.createOrRegisterTask(testTaskName) { testTask -> + testTask.group = LifecycleBasePlugin.VERIFICATION_GROUP + testTask.description = "Executes Kotlin/Native unit tests for target ${target.name}." + testTask.targetName = target.name + + testTask.enabled = target.konanTarget.isCurrentHost + + testTask.workingDir = project.projectDir.absolutePath + + testTask.configureConventions() + } + + @Suppress("UNCHECKED_CAST") + executionTask = testTaskOrProvider.getTaskOrProvider() as TaskProvider + + setExecutionSourceFrom(target.binaries.getTest(NativeBuildType.DEBUG)) + + project.kotlinTestRegistry.registerTestTask(testTaskOrProvider) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt index cf2dd802534..107983bed8e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt @@ -17,12 +17,12 @@ import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget -class KotlinNativeTargetPreset( +abstract class AbstractKotlinNativeTargetPreset( private val name: String, val project: Project, val konanTarget: KonanTarget, - private val kotlinPluginVersion: String -) : KotlinTargetPreset { + protected val kotlinPluginVersion: String +) : KotlinTargetPreset { init { // This is required to obtain Kotlin/Native home in CLion plugin: @@ -69,19 +69,23 @@ class KotlinNativeTargetPreset( filesList?.map { dir -> dependencies.create(files(dir)) } ?: emptyList() } - override fun createTarget(name: String): KotlinNativeTarget { + protected abstract fun createTargetConfigurator() : KotlinTargetConfigurator + + protected abstract fun instantiateTarget(name: String): T + + override fun createTarget(name: String): T { setupNativeCompiler() - val result = KotlinNativeTarget(project, konanTarget).apply { + val result = instantiateTarget(name).apply { targetName = name disambiguationClassifier = name - preset = this@KotlinNativeTargetPreset + preset = this@AbstractKotlinNativeTargetPreset val compilationFactory = KotlinNativeCompilationFactory(project, this) compilations = project.container(compilationFactory.itemClass, compilationFactory) } - KotlinNativeTargetConfigurator(kotlinPluginVersion).configureTarget(result) + createTargetConfigurator().configureTarget(result) // Allow IDE to resolve the libraries provided by the compiler by adding them into dependencies. @@ -114,6 +118,24 @@ class KotlinNativeTargetPreset( } } +open class KotlinNativeTargetPreset(name: String, project: Project, konanTarget: KonanTarget, kotlinPluginVersion: String) : + AbstractKotlinNativeTargetPreset(name, project, konanTarget, kotlinPluginVersion) { + + override fun createTargetConfigurator(): KotlinTargetConfigurator = + KotlinNativeTargetConfigurator(kotlinPluginVersion) + + override fun instantiateTarget(name: String): KotlinNativeTarget = KotlinNativeTarget(project, konanTarget) +} + +open class KotlinNativeTargetWithTestsPreset(name: String, project: Project, konanTarget: KonanTarget, kotlinPluginVersion: String) : + AbstractKotlinNativeTargetPreset(name, project, konanTarget, kotlinPluginVersion) { + + override fun createTargetConfigurator(): KotlinTargetConfigurator = + KotlinNativeTargetWithTestsConfigurator(kotlinPluginVersion) + + override fun instantiateTarget(name: String): KotlinNativeTargetWithTests = KotlinNativeTargetWithTests(project, konanTarget) +} + internal val KonanTarget.isCurrentHost: Boolean get() = this == HostManager.host 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 be87a411d5c..de91f5ae05d 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 @@ -6,11 +6,11 @@ package org.jetbrains.kotlin.gradle.targets.native.tasks import groovy.lang.Closure +import org.gradle.api.Task +import org.gradle.api.file.FileCollection import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* import org.gradle.process.ProcessForkOptions import org.gradle.process.internal.DefaultProcessForkOptions import org.jetbrains.kotlin.compilerRunner.konanVersion @@ -21,14 +21,27 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinTest import org.jetbrains.kotlin.konan.KonanVersion import org.jetbrains.kotlin.konan.MetaVersion import java.io.File +import java.util.concurrent.Callable open class KotlinNativeTest : KotlinTest() { @Suppress("LeakingThis") private val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver) - @InputFile - @SkipWhenEmpty - val executableProperty: Property = project.objects.property(File::class.java) + @get:Internal + val executableProperty: Property = project.objects.property(FileCollection::class.java) + + @get:InputFiles // use FileCollection & @InputFiles rather than @InputFile to allow for task dependencies built-into this FileCollection + @get:SkipWhenEmpty + @Suppress("UNUSED") // Gradle input + internal val executableFiles: FileCollection // Gradle < 5.0 seems to not work properly with @InputFiles Property + get() = executableProperty.get() + + private val executableFile + get() = executableProperty.get().singleFile + + init { + onlyIf { executableFile.exists() } + } @Input var args: List = emptyList() @@ -36,9 +49,9 @@ open class KotlinNativeTest : KotlinTest() { // Already taken into account in the executableProperty. @get:Internal var executable: File - get() = executableProperty.get() + get() = executableProperty.get().singleFile set(value) { - executableProperty.set(value) + executableProperty.set(project.files(value)) } @get:Input @@ -58,19 +71,27 @@ open class KotlinNativeTest : KotlinTest() { private fun Property.set(providerLambda: () -> T) = set(project.provider { providerLambda() }) fun executable(file: File) { - executableProperty.set(file) + executableProperty.set(project.files(file)) } fun executable(path: String) { - executableProperty.set { project.file(path) } + executableProperty.set { project.files(path) } } fun executable(provider: () -> File) { - executableProperty.set(provider) + executableProperty.set(project.files(Callable { provider() })) + } + + fun executable(builtByTask: Task, provider: () -> File) { + executableProperty.set(project.files(Callable { provider() }).builtBy(builtByTask)) + } + + fun executable(provider: Provider) { + executableProperty.set(provider.map { project.files(it) }) } fun executable(provider: Closure) { - executableProperty.set(project.provider(provider)) + executableProperty.set(project.provider(provider).map { project.files(it) }) } fun environment(name: String, value: Any) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/KotlinAggregateExecutionSource.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/KotlinAggregateExecutionSource.kt new file mode 100644 index 00000000000..94bd64ccd81 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/testing/KotlinAggregateExecutionSource.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2019 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.jetbrains.kotlin.gradle.testing + +import groovy.lang.Closure +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.testing.AbstractTestTask +import org.gradle.api.tasks.testing.TestFilter +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.execution.KotlinAggregateExecutionSource +import org.jetbrains.kotlin.gradle.execution.KotlinAggregatingExecution +import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.plugin.KotlinExecution.ExecutionSource +import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName + +interface KotlinAggregatingTestRun, T : KotlinTestRun> : + KotlinAggregatingExecution, + KotlinTestRun { + + override fun filter(configureFilter: TestFilter.() -> Unit) { + configureAllExecutions { filter(configureFilter) } + } +} + +abstract class KotlinTaskTestRun( + private val testRunName: String, + override val target: KotlinTarget +) : KotlinTargetTestRun, ExecutionTaskHolder { + + override fun getName(): String = + testRunName + + override lateinit var executionTask: TaskProvider + internal set + + override fun filter(configureFilter: TestFilter.() -> Unit) { + executionTask.configure { task -> configureFilter(task.filter) } + } +} + +internal val KotlinTargetTestRun<*>.testTaskName: String + get() = lowerCamelCaseName( + target.disambiguationClassifier, + name.takeIf { it != KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME }?.removeSuffix("Test"), + AbstractKotlinTargetConfigurator.testTaskNameSuffix + ) + +internal fun requireCompilationOfTarget(compilation: KotlinCompilation<*>, target: KotlinTarget) { + require(compilation.target === target) { + "Expected a compilation of target ${target.name}, " + + "got the compilation ${compilation.name} of target ${compilation.target.name}" + } +} + +abstract class KotlinReportAggregatingTestRun, T : KotlinTestRun>( + val testRunName: String +) : KotlinAggregatingTestRun, + ExecutionTaskHolder { + + override fun getName() = testRunName + + override lateinit var executionTask: TaskProvider + + override fun filter(configureFilter: TestFilter.() -> Unit) = configureAllExecutions { filter(configureFilter) } +} \ No newline at end of file