Introduce execution & test run API, KT-32679

* KotlinExecution
* KotlinExecutionSource
* KotlinTestRun
* Aggregating executions and execution sources
* KotlinTargetWithTests

* Add a `testableTargets` extension
* Make some Kotlin/Native targets testable (this requires
  having a separate preset type for them)
* Implement test runs encapsulating the tests for all
  existing testable targets by refactoring their
  TargetConfigurator classes

Issue #KT-32679 Fixed
This commit is contained in:
Sergey Igushkin
2019-07-31 18:21:08 +03:00
parent 6c8e829f19
commit 9f0c5675bd
29 changed files with 879 additions and 186 deletions
@@ -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<out SourceType : ExecutionSource> : 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<out SourceType : ExecutionSource> : KotlinExecution<SourceType> {
val target: KotlinTarget
}
/**
* An execution source that is produced by a [compilation].
*
* See also: [CompilationExecutionSourceSupport].
*/
interface CompilationExecutionSource<CompilationType : KotlinCompilation<*>> : ExecutionSource {
val compilation: CompilationType
}
interface CompilationExecutionSourceSupport<in T : KotlinCompilationToRunnableFiles<*>> {
/**
* 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<T : Task> {
val executionTask: TaskProvider<T>
}
@@ -54,4 +54,14 @@ interface KotlinTarget : Named, HasAttributes {
val preset: KotlinTargetPreset<out KotlinTarget>? val preset: KotlinTargetPreset<out KotlinTarget>?
override fun getName(): String = targetName override fun getName(): String = targetName
}
interface KotlinTargetWithTests<E : KotlinExecution.ExecutionSource, T : KotlinTargetTestRun<E>> : 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<T>
companion object {
const val DEFAULT_TEST_RUN_NAME = "test"
}
} }
@@ -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<out SourceType : KotlinExecution.ExecutionSource> : KotlinExecution<SourceType> {
fun filter(configureFilter: TestFilter.() -> Unit)
fun filter(configureFilter: Closure<*>) = filter { ConfigureUtil.configureSelf(configureFilter, this) }
}
interface KotlinTargetTestRun<ExecutionSource : KotlinExecution.ExecutionSource> :
KotlinTestRun<ExecutionSource>,
KotlinTargetExecution<ExecutionSource>
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)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.generators.gradle.dsl package org.jetbrains.kotlin.generators.gradle.dsl
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
internal class KotlinPresetEntry( 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_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset"
internal const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" 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( internal val jvmPresetEntry = KotlinPresetEntry(
"jvm", "jvm",
typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"), typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"),
@@ -39,11 +43,24 @@ internal val androidPresetEntry = KotlinPresetEntry(
typeName("$MPP_PACKAGE.KotlinAndroidTarget") 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) -> internal val nativePresetEntries = HostManager().targets.map { (_, target) ->
KotlinPresetEntry( KotlinPresetEntry(
target.presetName, target.presetName,
typeName(KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME), typeName(
typeName(KOTLIN_NATIVE_TARGET_CLASS_FQNAME) 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
)
) )
} }
@@ -9,9 +9,7 @@ import groovy.lang.Closure
import org.gradle.api.InvalidUserCodeException import org.gradle.api.InvalidUserCodeException
import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.NamedDomainObjectCollection
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets
import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
@@ -22,6 +20,10 @@ open class KotlinMultiplatformExtension : KotlinProjectExtension(), KotlinTarget
override lateinit var targets: NamedDomainObjectCollection<KotlinTarget> override lateinit var targets: NamedDomainObjectCollection<KotlinTarget>
internal set internal set
@Suppress("unused") // DSL
val testableTargets: NamedDomainObjectCollection<KotlinTargetWithTests<*, *>>
get() = targets.withType(KotlinTargetWithTests::class.java)
internal var isGradleMetadataAvailable: Boolean = false internal var isGradleMetadataAvailable: Boolean = false
internal var isGradleMetadataExperimental: Boolean = false internal var isGradleMetadataExperimental: Boolean = false
@@ -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.KotlinJvmTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetPreset 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.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
@@ -258,11 +260,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP
fun linuxX64( fun linuxX64(
name: String = "linuxX64", name: String = "linuxX64",
configure: KotlinNativeTarget.() -> Unit = { } configure: KotlinNativeTargetWithTests.() -> Unit = { }
): KotlinNativeTarget = ): KotlinNativeTargetWithTests =
configureOrCreate( configureOrCreate(
name, name,
presets.getByName("linuxX64") as KotlinNativeTargetPreset, presets.getByName("linuxX64") as KotlinNativeTargetWithTestsPreset,
configure configure
) )
@@ -333,11 +335,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP
fun mingwX64( fun mingwX64(
name: String = "mingwX64", name: String = "mingwX64",
configure: KotlinNativeTarget.() -> Unit = { } configure: KotlinNativeTargetWithTests.() -> Unit = { }
): KotlinNativeTarget = ): KotlinNativeTargetWithTests =
configureOrCreate( configureOrCreate(
name, name,
presets.getByName("mingwX64") as KotlinNativeTargetPreset, presets.getByName("mingwX64") as KotlinNativeTargetWithTestsPreset,
configure configure
) )
@@ -363,11 +365,11 @@ interface KotlinTargetContainerWithPresetFunctions : KotlinTargetsContainerWithP
fun macosX64( fun macosX64(
name: String = "macosX64", name: String = "macosX64",
configure: KotlinNativeTarget.() -> Unit = { } configure: KotlinNativeTargetWithTests.() -> Unit = { }
): KotlinNativeTarget = ): KotlinNativeTargetWithTests =
configureOrCreate( configureOrCreate(
name, name,
presets.getByName("macosX64") as KotlinNativeTargetPreset, presets.getByName("macosX64") as KotlinNativeTargetWithTestsPreset,
configure configure
) )
@@ -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<AggregatedSourceType : ExecutionSource> : ExecutionSource {
val executionSources: Iterable<AggregatedSourceType>
}
interface KotlinAggregatingExecution<
AggregatedSourceType : ExecutionSource,
AggregatingSourceType : KotlinAggregateExecutionSource<AggregatedSourceType>,
AggregatedExecutionType : KotlinExecution<AggregatedSourceType>
> :
KotlinExecution<AggregatingSourceType> {
/**
* 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<AggregatedExecutionType>
fun configureAllExecutions(configureClosure: Closure<*>) = configureAllExecutions {
ConfigureUtil.configureSelf(
configureClosure,
this
)
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.gradle.plugin package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.Task import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration 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.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.* 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 org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.util.concurrent.Callable import java.util.concurrent.Callable
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>( interface KotlinTargetConfigurator<KotlinTargetType : KotlinTarget> {
protected val createDefaultSourceSets: Boolean, fun configureTarget(
protected val createTestCompilation: Boolean
) {
open fun configureTarget(
target: KotlinTargetType target: KotlinTargetType
) { ) {
configureCompilationDefaults(target) configureCompilationDefaults(target)
configureCompilations(target) configureCompilations(target)
defineConfigurationsForTarget(target) defineConfigurationsForTarget(target)
configureArchivesAndComponent(target) configureArchivesAndComponent(target)
configureTest(target)
configureBuild(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) fun configurePlatformSpecificModel(target: KotlinTargetType) = Unit
protected abstract fun configureTest(target: KotlinTargetType) }
abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>(
protected val createDefaultSourceSets: Boolean,
protected val createTestCompilation: Boolean
) : KotlinTargetConfigurator<KotlinTargetType> {
private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) { private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) {
val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete
@@ -62,17 +69,17 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
} }
} }
protected open fun configureCompilations(platformTarget: KotlinTargetType) { override fun configureCompilations(target: KotlinTargetType) {
val project = platformTarget.project val project = target.project
val main = platformTarget.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME) val main = target.compilations.create(KotlinCompilation.MAIN_COMPILATION_NAME)
platformTarget.compilations.all { target.compilations.all {
project.registerOutputsForStaleOutputCleanup(it) project.registerOutputsForStaleOutputCleanup(it)
setupCompilationDependencyFiles(project, it) setupCompilationDependencyFiles(project, it)
} }
if (createTestCompilation) { if (createTestCompilation) {
platformTarget.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply { target.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
compileDependencyFiles += main.output.allOutputs compileDependencyFiles += main.output.allOutputs
if (this is KotlinCompilationToRunnableFiles) { if (this is KotlinCompilationToRunnableFiles) {
@@ -82,7 +89,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
} }
} }
protected fun configureCompilationDefaults(target: KotlinTargetType) { override fun configureCompilationDefaults(target: KotlinTargetType) {
val project = target.project val project = target.project
target.compilations.all { compilation -> target.compilations.all { compilation ->
@@ -129,7 +136,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
} }
} }
protected open fun defineConfigurationsForTarget(target: KotlinTargetType) { override fun defineConfigurationsForTarget(target: KotlinTargetType) {
val project = target.project val project = target.project
val configurations = project.configurations val configurations = project.configurations
@@ -195,7 +202,7 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
} }
} }
protected fun configureBuild(target: KotlinTargetType) { override fun configureBuild(target: KotlinTargetType) {
val project = target.project val project = target.project
val buildNeeded = project.tasks.getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME) val buildNeeded = project.tasks.getByName(JavaBasePlugin.BUILD_NEEDED_TASK_NAME)
@@ -305,23 +312,23 @@ internal val KotlinCompilationToRunnableFiles<*>.deprecatedRuntimeConfigurationN
internal val KotlinTarget.testTaskName: String internal val KotlinTarget.testTaskName: String
get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix) get() = lowerCamelCaseName(targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix)
abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>>( abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>, KotlinTargetType : KotlinOnlyTarget<KotlinCompilationType>>(
createDefaultSourceSets: Boolean, createDefaultSourceSets: Boolean,
createTestCompilation: Boolean, createTestCompilation: Boolean,
val kotlinPluginVersion: String val kotlinPluginVersion: String
) : AbstractKotlinTargetConfigurator<KotlinOnlyTarget<KotlinCompilationType>>( ) : AbstractKotlinTargetConfigurator<KotlinTargetType>(
createDefaultSourceSets, createDefaultSourceSets,
createTestCompilation createTestCompilation
) { ) {
internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinSourceSetProcessor<*> internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinSourceSetProcessor<*>
override fun configureCompilations(platformTarget: KotlinOnlyTarget<KotlinCompilationType>) { override fun configureCompilations(target: KotlinTargetType) {
super.configureCompilations(platformTarget) super.configureCompilations(target)
platformTarget.compilations.all { compilation -> target.compilations.all { compilation ->
buildCompilationProcessor(compilation).run() buildCompilationProcessor(compilation).run()
if (compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME) { 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<KotlinCompilationType : KotlinCompilatio
result.from(target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).output.allOutputs) result.from(target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).output.allOutputs)
} }
override fun configureArchivesAndComponent(target: KotlinOnlyTarget<KotlinCompilationType>) { override fun configureArchivesAndComponent(target: KotlinTargetType) {
val project = target.project val project = target.project
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
@@ -365,30 +372,6 @@ abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilatio
} }
} }
override fun configureTest(target: KotlinOnlyTarget<KotlinCompilationType>) {
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<KotlinJvmTest>(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) { private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) {
val publications = configuration.outgoing val publications = configuration.outgoing
@@ -398,6 +381,46 @@ abstract class KotlinTargetConfigurator<KotlinCompilationType : KotlinCompilatio
} }
} }
internal interface KotlinTargetWithTestsConfigurator<R : KotlinTargetTestRun<*>, T : KotlinTargetWithTests<*, R>>
: KotlinTargetConfigurator<T> {
override fun configureTarget(target: T) {
super.configureTarget(target)
configureTest(target)
}
val testRunClass: Class<R>
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<KotlinTargetWithTests<*, R>, NamedDomainObjectContainer<R>>)
.set(
target,
project.container(testRunClass) { testRunName ->
createTestRun(testRunName, target)
}
)
}
}
internal fun Project.usageByName(usageName: String): Usage = internal fun Project.usageByName(usageName: String): Usage =
project.objects.named(Usage::class.java, usageName) project.objects.named(Usage::class.java, usageName)
@@ -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.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.presetName import org.jetbrains.kotlin.konan.target.presetName
class KotlinMultiplatformPlugin( class KotlinMultiplatformPlugin(
@@ -137,8 +138,17 @@ class KotlinMultiplatformPlugin(
add(KotlinJsTargetPreset(project, kotlinPluginVersion)) add(KotlinJsTargetPreset(project, kotlinPluginVersion))
add(KotlinAndroidTargetPreset(project, kotlinPluginVersion)) add(KotlinAndroidTargetPreset(project, kotlinPluginVersion))
add(KotlinJvmWithJavaTargetPreset(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)
)
} }
} }
} }
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType 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 import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompilation<*>>( abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompilation<*>>(
@@ -16,7 +16,7 @@ abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompila
protected val kotlinPluginVersion: String protected val kotlinPluginVersion: String
) : KotlinTargetPreset<R> { ) : KotlinTargetPreset<R> {
protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T> protected abstract fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<T, R>
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? = protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
target.targetName target.targetName
@@ -254,7 +254,7 @@ internal fun javaApiUsageForMavenScoping() =
JAVA_API JAVA_API
} }
open class KotlinOnlyTarget<T : KotlinCompilation<*>>( abstract class KotlinOnlyTarget<T : KotlinCompilation<*>>(
project: Project, project: Project,
override val platformType: KotlinPlatformType override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) { ) : AbstractKotlinTarget(project) {
@@ -267,5 +267,4 @@ open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
override var disambiguationClassifier: String? = null override var disambiguationClassifier: String? = null
internal set internal set
} }
@@ -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
@@ -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<KotlinJsCompilation>
open class KotlinJsPlatformTestRun(testRunName: String, subtarget: KotlinJsSubTarget) :
KotlinTaskTestRun<JsCompilationExecutionSource, KotlinJsTest>(testRunName, subtarget.target),
CompilationExecutionSourceSupport<KotlinJsCompilation> {
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<JsCompilationExecutionSource> {
override val executionSources: Iterable<JsCompilationExecutionSource>
get() = aggregatingTestRun.getConfiguredExecutions().map { it.executionSource }
}
open class KotlinJsReportAggregatingTestRun(
testRunName: String,
override val target: KotlinJsTarget
) : KotlinReportAggregatingTestRun<JsCompilationExecutionSource, JsAggregatingExecutionSource, KotlinJsPlatformTestRun>(testRunName),
KotlinTargetTestRun<JsAggregatingExecutionSource>,
CompilationExecutionSourceSupport<KotlinJsCompilation> {
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<KotlinJsPlatformTestRun> = mutableListOf<KotlinJsPlatformTestRun>().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) }
}
}
@@ -5,11 +5,10 @@
package org.jetbrains.kotlin.gradle.targets.js package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project 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.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.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl 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.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinNodeJs 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.KotlinTestReport
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
class KotlinJsTarget(project: Project, platformType: KotlinPlatformType) : class KotlinJsTarget(project: Project, platformType: KotlinPlatformType) :
KotlinOnlyTarget<KotlinJsCompilation>(project, platformType), KotlinJsTargetDsl { KotlinOnlyTarget<KotlinJsCompilation>(project, platformType),
KotlinTargetWithTests<JsAggregatingExecutionSource, KotlinJsReportAggregatingTestRun>,
KotlinJsTargetDsl
{
override lateinit var testRuns: NamedDomainObjectContainer<KotlinJsReportAggregatingTestRun>
internal set
val testTaskName get() = lowerCamelCaseName(disambiguationClassifier, testTaskNameSuffix) val testTaskName get() = testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).testTaskName
val testTask: TaskHolder<KotlinTestReport> val testTask: TaskHolder<KotlinTestReport>
get() = project.kotlinTestRegistry.getOrCreateAggregatedTestTask( get() = checkNotNull(project.locateTask(testTaskName))
name = testTaskName,
description = "Run JS tests for all platforms"
)
val runTaskName get() = lowerCamelCaseName(disambiguationClassifier, runTaskNameSuffix) val runTaskName get() = lowerCamelCaseName(disambiguationClassifier, runTaskNameSuffix)
val runTask val runTask
@@ -37,23 +39,56 @@ class KotlinJsTarget(project: Project, platformType: KotlinPlatformType) :
it.description = "Run js on all configured platforms" it.description = "Run js on all configured platforms"
} }
val browser by lazy { private val browserLazyDelegate = lazy {
KotlinBrowserJs(this).also { KotlinBrowserJs(this).also {
it.configure() it.configure()
browserConfiguredHandlers.forEach { handler -> handler(it) }
browserConfiguredHandlers.clear()
} }
} }
val nodejs by lazy {
KotlinNodeJs(this).also { private val browserConfiguredHandlers = mutableListOf<KotlinJsBrowserDsl.() -> Unit>()
it.configure()
} val browser by browserLazyDelegate
}
internal val isBrowserConfigured: Boolean = browserLazyDelegate.isInitialized()
override fun browser(body: KotlinJsBrowserDsl.() -> Unit) { 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<KotlinJsNodeDsl.() -> Unit>()
val nodejs by nodejsLazyDelegate
internal val isNodejsConfigured: Boolean = nodejsLazyDelegate.isInitialized()
override fun nodejs(body: KotlinJsNodeDsl.() -> Unit) { 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() { fun useCommonJs() {
@@ -5,29 +5,45 @@
package org.jetbrains.kotlin.gradle.targets.js 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.Kotlin2JsSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor 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.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.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) : open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
KotlinTargetConfigurator<KotlinJsCompilation>(true, true, kotlinPluginVersion) { KotlinOnlyTargetConfigurator<KotlinJsCompilation, KotlinJsTarget>(true, true, kotlinPluginVersion),
KotlinTargetWithTestsConfigurator<KotlinJsReportAggregatingTestRun, KotlinJsTarget> {
override fun configureTarget(target: KotlinOnlyTarget<KotlinJsCompilation>) { override val testRunClass: Class<KotlinJsReportAggregatingTestRun> get() = KotlinJsReportAggregatingTestRun::class.java
target as KotlinJsTarget
super.configureTarget(target) override fun createTestRun(
} name: String,
target: KotlinJsTarget
): KotlinJsReportAggregatingTestRun {
val result = KotlinJsReportAggregatingTestRun(name, target)
override fun configureTest(target: KotlinOnlyTarget<KotlinJsCompilation>) { val testTask = target.project.kotlinTestRegistry.getOrCreateAggregatedTestTask(
target as KotlinJsTarget name = result.testTaskName,
description = "Run JS tests for all platforms"
)
// always create jsTest task for compatibility (KT-31527) // workaround to avoid the infinite recursion in item factories of the target and the subtargets:
// actual tests tasks for browser and nodejs will be configured in KotlinJsSubTarget.configure target.testRuns.matching { it.name == name }.whenObjectAdded {
target.testTask 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<KotlinTestReport>
return result
} }
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> { override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
@@ -35,10 +51,10 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
return Kotlin2JsSourceSetProcessor(compilation.target.project, tasksProvider, compilation, kotlinPluginVersion) return Kotlin2JsSourceSetProcessor(compilation.target.project, tasksProvider, compilation, kotlinPluginVersion)
} }
override fun configureCompilations(platformTarget: KotlinOnlyTarget<KotlinJsCompilation>) { override fun configureCompilations(target: KotlinJsTarget) {
super.configureCompilations(platformTarget) super.configureCompilations(target)
platformTarget.compilations.all { target.compilations.all {
it.compileKotlinTask.kotlinOptions { it.compileKotlinTask.kotlinOptions {
moduleKind = "umd" moduleKind = "umd"
sourceMap = true sourceMap = true
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.gradle.targets.js.dsl package org.jetbrains.kotlin.gradle.targets.js.dsl
import groovy.lang.Closure import groovy.lang.Closure
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.util.ConfigureUtil 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.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
@@ -27,6 +30,8 @@ interface KotlinJsTargetDsl {
ConfigureUtil.configure(fn, this) ConfigureUtil.configure(fn, this)
} }
} }
val testRuns: NamedDomainObjectContainer<KotlinJsReportAggregatingTestRun>
} }
interface KotlinJsSubTargetDsl { interface KotlinJsSubTargetDsl {
@@ -36,6 +41,8 @@ interface KotlinJsSubTargetDsl {
ConfigureUtil.configure(fn, this) ConfigureUtil.configure(fn, this)
} }
} }
val testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun>
} }
interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl { interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl {
@@ -5,18 +5,21 @@
package org.jetbrains.kotlin.gradle.targets.js.subtargets 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.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.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated 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.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl 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.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin 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.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.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask 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.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
@@ -31,6 +34,9 @@ abstract class KotlinJsSubTarget(
val runTaskName = disambiguateCamelCased("run") val runTaskName = disambiguateCamelCased("run")
val testTaskName = disambiguateCamelCased("test") val testTaskName = disambiguateCamelCased("test")
override val testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun> =
project.container(KotlinJsPlatformTestRun::class.java) { name -> KotlinJsPlatformTestRun(name, this) }
fun configure() { fun configure() {
NpmResolverPlugin.apply(project) NpmResolverPlugin.apply(project)
@@ -47,17 +53,27 @@ abstract class KotlinJsSubTarget(
lowerCamelCaseName(target.disambiguationClassifier, disambiguationClassifier, name) lowerCamelCaseName(target.disambiguationClassifier, disambiguationClassifier, name)
private fun configureTests() { private fun configureTests() {
target.compilations.all { compilation -> testRuns.all { configureTestRunDefaults(it) }
if (compilation.name == KotlinCompilation.TEST_COMPILATION_NAME) { testRuns.create(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME)
configureTests(compilation) }
}
protected open fun configureTestRunDefaults(testRun: KotlinJsPlatformTestRun) {
target.compilations.matching { it.name == KotlinCompilation.TEST_COMPILATION_NAME }.all { compilation ->
configureTestsRun(testRun, compilation)
} }
} }
abstract val testTaskDescription: String abstract val testTaskDescription: String
private fun configureTests(compilation: KotlinJsCompilation) { private fun configureTestsRun(testRun: KotlinJsPlatformTestRun, compilation: KotlinJsCompilation) {
val testJs = project.createOrRegisterTask<KotlinJsTest>(testTaskName) { testJs -> fun KotlinJsPlatformTestRun.subtargetTestTaskName(): String = disambiguateCamelCased(
lowerCamelCaseName(
name.takeIf { it != KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME },
AbstractKotlinTargetConfigurator.testTaskNameSuffix
)
)
val testJs = project.createOrRegisterTask<KotlinJsTest>(testRun.subtargetTestTaskName()) { testJs ->
val compileTask = compilation.compileKotlinTask val compileTask = compilation.compileKotlinTask
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
@@ -77,7 +93,14 @@ abstract class KotlinJsSubTarget(
testJs.configureConventions() testJs.configureConventions()
} }
target.project.kotlinTestRegistry.registerTestTask(testJs, target.testTask.doGetTask()) testRun.executionTask = testJs.getTaskOrProvider() as TaskProvider<KotlinJsTest>
target.testRuns.matching { it.name == testRun.name }.all { parentTestRun ->
target.project.kotlinTestRegistry.registerTestTask(
testJs,
parentTestRun.executionTask.get()
)
}
project.whenEvaluated { project.whenEvaluated {
testJs.configure { testJs.configure {
@@ -101,6 +124,6 @@ abstract class KotlinJsSubTarget(
protected abstract fun configureRun(compilation: KotlinJsCompilation) protected abstract fun configureRun(compilation: KotlinJsCompilation)
override fun testTask(body: KotlinJsTest.() -> Unit) { override fun testTask(body: KotlinJsTest.() -> Unit) {
(project.tasks.getByName(testTaskName) as KotlinJsTest).body() project.locateTask<KotlinJsTest>(testTaskName)!!.configure(body)
} }
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.jvm package org.jetbrains.kotlin.gradle.targets.jvm
import org.gradle.api.InvalidUserCodeException import org.gradle.api.InvalidUserCodeException
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
@@ -23,9 +24,11 @@ import java.util.concurrent.Callable
class KotlinJvmTarget( class KotlinJvmTarget(
project: Project project: Project
) : KotlinOnlyTarget<KotlinJvmCompilation>( ) : KotlinOnlyTarget<KotlinJvmCompilation>(project, KotlinPlatformType.jvm),
project, KotlinPlatformType.jvm KotlinTargetWithTests<JvmClasspathTestRunSource, KotlinJvmTestRun> {
) {
override lateinit var testRuns: NamedDomainObjectContainer<KotlinJvmTestRun>
var withJavaEnabled = false var withJavaEnabled = false
private set private set
@@ -162,4 +165,5 @@ class KotlinJvmTarget(
project.addExtendsFromRelation(compilation.runtimeDependencyConfigurationName, configurationName) project.addExtendsFromRelation(compilation.runtimeDependencyConfigurationName, configurationName)
} }
} }
} }
@@ -5,14 +5,50 @@
package org.jetbrains.kotlin.gradle.targets.jvm 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.Kotlin2JvmSourceSetProcessor
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor 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.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.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) : class KotlinJvmTargetConfigurator(kotlinPluginVersion: String) :
KotlinTargetConfigurator<KotlinJvmCompilation>(true, true, kotlinPluginVersion) { KotlinOnlyTargetConfigurator<KotlinJvmCompilation, KotlinJvmTarget>(true, true, kotlinPluginVersion),
KotlinTargetWithTestsConfigurator<KotlinJvmTestRun, KotlinJvmTarget> {
override val testRunClass: Class<KotlinJvmTestRun>
get() = KotlinJvmTestRun::class.java
override fun createTestRun(
name: String,
target: KotlinJvmTarget
): KotlinJvmTestRun = KotlinJvmTestRun(name, target).apply {
val testTaskOrProvider = target.project.createOrRegisterTask<KotlinJvmTest>(testTaskName) { testTask ->
testTask.targetName = target.disambiguationClassifier
testTask.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(testTask)
}
executionTask = testTaskOrProvider.getTaskOrProvider() as TaskProvider<KotlinJvmTest>
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<*> { override fun buildCompilationProcessor(compilation: KotlinJvmCompilation): KotlinSourceSetProcessor<*> {
val tasksProvider = KotlinTasksProvider(compilation.target.targetName) val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
@@ -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<KotlinJvmCompilation>,
val testCompilations: Iterable<KotlinJvmCompilation>
) : 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<KotlinJvmCompilation>
open class KotlinJvmTestRun(testRunName: String, override val target: KotlinJvmTarget) :
KotlinTaskTestRun<JvmClasspathTestRunSource, KotlinJvmTest>(testRunName, target),
CompilationExecutionSourceSupport<KotlinJvmCompilation>,
ClasspathTestRunSourceSupport {
override fun setExecutionSourceFrom(classpath: FileCollection, testClassesDirs: FileCollection) {
executionSource = ClasspathOnlyTestRunSource(classpath, testClassesDirs)
}
fun setExecutionSourceFrom(
classpathCompilations: Iterable<KotlinJvmCompilation>,
testClassesCompilations: Iterable<KotlinJvmCompilation>
) {
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
}
}
}
@@ -9,6 +9,9 @@ import org.gradle.api.internal.tasks.testing.*
import org.gradle.api.tasks.Input import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.testing.Test 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() { open class KotlinJvmTest : Test() {
@Input @Input
@@ -35,7 +35,7 @@ internal val Project.isKotlinGranularMetadataEnabled: Boolean
get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) : class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
KotlinTargetConfigurator<KotlinCommonCompilation>( KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget>(
createDefaultSourceSets = false, createDefaultSourceSets = false,
createTestCompilation = false, createTestCompilation = false,
kotlinPluginVersion = kotlinPluginVersion kotlinPluginVersion = kotlinPluginVersion
@@ -59,7 +59,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
private val KotlinOnlyTarget<KotlinCommonCompilation>.apiElementsConfiguration: Configuration private val KotlinOnlyTarget<KotlinCommonCompilation>.apiElementsConfiguration: Configuration
get() = project.configurations.getByName(apiElementsConfigurationName) get() = project.configurations.getByName(apiElementsConfigurationName)
override fun configureTarget(target: KotlinOnlyTarget<KotlinCommonCompilation>) { override fun configureTarget(target: KotlinMetadataTarget) {
super.configureTarget(target) super.configureTarget(target)
if (target.project.isKotlinGranularMetadataEnabled) { if (target.project.isKotlinGranularMetadataEnabled) {
@@ -12,14 +12,14 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 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.plugin.sources.applyLanguageSettingsToKotlinTask
import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator import org.jetbrains.kotlin.gradle.targets.metadata.KotlinMetadataTargetConfigurator
class KotlinMetadataTargetPreset( class KotlinMetadataTargetPreset(
project: Project, project: Project,
kotlinPluginVersion: String kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinOnlyTarget<KotlinCommonCompilation>, KotlinCommonCompilation>( ) : KotlinOnlyTargetPreset<KotlinMetadataTarget, KotlinCommonCompilation>(
project, project,
kotlinPluginVersion kotlinPluginVersion
) { ) {
@@ -37,12 +37,12 @@ class KotlinMetadataTargetPreset(
const val PRESET_NAME = "metadata" const val PRESET_NAME = "metadata"
} }
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> = override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget> =
KotlinMetadataTargetConfigurator(kotlinPluginVersion) KotlinMetadataTargetConfigurator(kotlinPluginVersion)
override fun instantiateTarget(): KotlinOnlyTarget<KotlinCommonCompilation> = KotlinMetadataTarget(project) override fun instantiateTarget(): KotlinMetadataTarget = KotlinMetadataTarget(project)
override fun createTarget(name: String): KotlinOnlyTarget<KotlinCommonCompilation> = override fun createTarget(name: String): KotlinMetadataTarget =
super.createTarget(name).apply { super.createTarget(name).apply {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) val commonMainSourceSet = project.kotlinExtension.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
@@ -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<KotlinNativeCompilation> {
override val compilation: KotlinNativeCompilation
get() = binary.compilation
}
interface KotlinNativeBinaryTestRun : KotlinTargetTestRun<NativeBinaryTestRunSource> {
/**
* 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<NativeBinaryTestRunSource, KotlinNativeTest>(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)
}
}
@@ -7,16 +7,20 @@
package org.jetbrains.kotlin.gradle.plugin.mpp package org.jetbrains.kotlin.gradle.plugin.mpp
import groovy.lang.Closure import groovy.lang.Closure
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.Attribute
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.gradle.util.WrapUtil import org.gradle.util.WrapUtil
import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType 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.gradle.utils.isGradleVersionAtLeast
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
class KotlinNativeTarget( open class KotlinNativeTarget(
project: Project, project: Project,
val konanTarget: KonanTarget val konanTarget: KonanTarget
) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) { ) : KotlinOnlyTarget<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
@@ -61,4 +65,13 @@ class KotlinNativeTarget(
String::class.java String::class.java
) )
} }
}
open class KotlinNativeTargetWithTests(
project: Project,
konanTarget: KonanTarget
) : KotlinNativeTarget(project, konanTarget), KotlinTargetWithTests<NativeBinaryTestRunSource, KotlinNativeBinaryTestRun> {
override lateinit var testRuns: NamedDomainObjectContainer<KotlinNativeBinaryTestRun>
internal set
} }
@@ -17,22 +17,24 @@ import org.gradle.api.internal.plugins.DefaultArtifactPublicationSet
import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Exec import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin 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.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_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.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.targets.native.tasks.KotlinNativeTest
import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File import java.io.File
import java.util.* import java.util.*
open class KotlinNativeTargetConfigurator( open class KotlinNativeTargetConfigurator<T : KotlinNativeTarget>(
private val kotlinPluginVersion: String private val kotlinPluginVersion: String
) : AbstractKotlinTargetConfigurator<KotlinNativeTarget>( ) : AbstractKotlinTargetConfigurator<T>(
createDefaultSourceSets = true, createDefaultSourceSets = true,
createTestCompilation = true createTestCompilation = true
) { ) {
@@ -212,15 +214,14 @@ open class KotlinNativeTargetConfigurator(
// endregion. // endregion.
// region Configuration. // region Configuration.
override fun configureTarget(target: KotlinNativeTarget) { override fun configurePlatformSpecificModel(target: T) {
super.configureTarget(target)
configureBinaries(target) configureBinaries(target)
configureFrameworkExport(target) configureFrameworkExport(target)
configureCInterops(target) configureCInterops(target)
warnAboutIncorrectDependencies(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) tasks.create(target.artifactsTaskName)
target.compilations.all { target.compilations.all {
createKlibCompilationTask(it) 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<KotlinNativeTest>(taskName) { testTask ->
testTask.group = LifecycleBasePlugin.VERIFICATION_GROUP
testTask.description = "Executes Kotlin/Native unit tests for target ${target.name}."
testTask.targetName = compilation.target.targetName
testTask.enabled = target.konanTarget.isCurrentHost
testTask.executable { outputFile }
testTask.workingDir = project.projectDir.absolutePath
testTask.onlyIf { outputFile.exists() }
testTask.dependsOn(linkTaskName)
testTask.configureConventions()
}
kotlinTestRegistry.registerTestTask(testTask)
}
}
}
protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) { protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) {
target.compilations.all { compilation -> target.compilations.all { compilation ->
createCInteropTasks(compilation) createCInteropTasks(compilation)
@@ -306,6 +278,11 @@ open class KotlinNativeTargetConfigurator(
project.tasks.getByName(it.compilation.binariesTaskName).dependsOn(it.linkTaskName) 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) { fun configureFrameworkExport(target: KotlinNativeTarget) {
@@ -335,7 +312,7 @@ open class KotlinNativeTargetConfigurator(
} }
} }
override fun defineConfigurationsForTarget(target: KotlinNativeTarget) { override fun defineConfigurationsForTarget(target: T) {
super.defineConfigurationsForTarget(target) super.defineConfigurationsForTarget(target)
val configurations = target.project.configurations val configurations = target.project.configurations
@@ -411,3 +388,35 @@ open class KotlinNativeTargetConfigurator(
} }
} }
} }
class KotlinNativeTargetWithTestsConfigurator(kotlinPluginVersion: String)
: KotlinNativeTargetConfigurator<KotlinNativeTargetWithTests>(kotlinPluginVersion),
KotlinTargetWithTestsConfigurator<KotlinNativeBinaryTestRun, KotlinNativeTargetWithTests> {
override val testRunClass: Class<KotlinNativeBinaryTestRun>
get() = KotlinNativeBinaryTestRun::class.java
override fun createTestRun(name: String, target: KotlinNativeTargetWithTests): KotlinNativeBinaryTestRun =
DefaultKotlinNativeTestRun(name, target).apply {
val project = target.project
val testTaskOrProvider = project.createOrRegisterTask<KotlinNativeTest>(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<KotlinNativeTest>
setExecutionSourceFrom(target.binaries.getTest(NativeBuildType.DEBUG))
project.kotlinTestRegistry.registerTestTask(testTaskOrProvider)
}
}
@@ -17,12 +17,12 @@ import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
class KotlinNativeTargetPreset( abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
private val name: String, private val name: String,
val project: Project, val project: Project,
val konanTarget: KonanTarget, val konanTarget: KonanTarget,
private val kotlinPluginVersion: String protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> { ) : KotlinTargetPreset<T> {
init { init {
// This is required to obtain Kotlin/Native home in CLion plugin: // 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() filesList?.map { dir -> dependencies.create(files(dir)) } ?: emptyList()
} }
override fun createTarget(name: String): KotlinNativeTarget { protected abstract fun createTargetConfigurator() : KotlinTargetConfigurator<T>
protected abstract fun instantiateTarget(name: String): T
override fun createTarget(name: String): T {
setupNativeCompiler() setupNativeCompiler()
val result = KotlinNativeTarget(project, konanTarget).apply { val result = instantiateTarget(name).apply {
targetName = name targetName = name
disambiguationClassifier = name disambiguationClassifier = name
preset = this@KotlinNativeTargetPreset preset = this@AbstractKotlinNativeTargetPreset
val compilationFactory = KotlinNativeCompilationFactory(project, this) val compilationFactory = KotlinNativeCompilationFactory(project, this)
compilations = project.container(compilationFactory.itemClass, compilationFactory) 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. // 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<KotlinNativeTarget>(name, project, konanTarget, kotlinPluginVersion) {
override fun createTargetConfigurator(): KotlinTargetConfigurator<KotlinNativeTarget> =
KotlinNativeTargetConfigurator(kotlinPluginVersion)
override fun instantiateTarget(name: String): KotlinNativeTarget = KotlinNativeTarget(project, konanTarget)
}
open class KotlinNativeTargetWithTestsPreset(name: String, project: Project, konanTarget: KonanTarget, kotlinPluginVersion: String) :
AbstractKotlinNativeTargetPreset<KotlinNativeTargetWithTests>(name, project, konanTarget, kotlinPluginVersion) {
override fun createTargetConfigurator(): KotlinTargetConfigurator<KotlinNativeTargetWithTests> =
KotlinNativeTargetWithTestsConfigurator(kotlinPluginVersion)
override fun instantiateTarget(name: String): KotlinNativeTargetWithTests = KotlinNativeTargetWithTests(project, konanTarget)
}
internal val KonanTarget.isCurrentHost: Boolean internal val KonanTarget.isCurrentHost: Boolean
get() = this == HostManager.host get() = this == HostManager.host
@@ -6,11 +6,11 @@
package org.jetbrains.kotlin.gradle.targets.native.tasks package org.jetbrains.kotlin.gradle.targets.native.tasks
import groovy.lang.Closure 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.provider.Property
import org.gradle.api.tasks.Input import org.gradle.api.provider.Provider
import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.*
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.process.ProcessForkOptions import org.gradle.process.ProcessForkOptions
import org.gradle.process.internal.DefaultProcessForkOptions import org.gradle.process.internal.DefaultProcessForkOptions
import org.jetbrains.kotlin.compilerRunner.konanVersion 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.KonanVersion
import org.jetbrains.kotlin.konan.MetaVersion import org.jetbrains.kotlin.konan.MetaVersion
import java.io.File import java.io.File
import java.util.concurrent.Callable
open class KotlinNativeTest : KotlinTest() { open class KotlinNativeTest : KotlinTest() {
@Suppress("LeakingThis") @Suppress("LeakingThis")
private val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver) private val processOptions: ProcessForkOptions = DefaultProcessForkOptions(fileResolver)
@InputFile @get:Internal
@SkipWhenEmpty val executableProperty: Property<FileCollection> = project.objects.property(FileCollection::class.java)
val executableProperty: Property<File> = project.objects.property(File::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<FileCollection>
get() = executableProperty.get()
private val executableFile
get() = executableProperty.get().singleFile
init {
onlyIf { executableFile.exists() }
}
@Input @Input
var args: List<String> = emptyList() var args: List<String> = emptyList()
@@ -36,9 +49,9 @@ open class KotlinNativeTest : KotlinTest() {
// Already taken into account in the executableProperty. // Already taken into account in the executableProperty.
@get:Internal @get:Internal
var executable: File var executable: File
get() = executableProperty.get() get() = executableProperty.get().singleFile
set(value) { set(value) {
executableProperty.set(value) executableProperty.set(project.files(value))
} }
@get:Input @get:Input
@@ -58,19 +71,27 @@ open class KotlinNativeTest : KotlinTest() {
private fun <T> Property<T>.set(providerLambda: () -> T) = set(project.provider { providerLambda() }) private fun <T> Property<T>.set(providerLambda: () -> T) = set(project.provider { providerLambda() })
fun executable(file: File) { fun executable(file: File) {
executableProperty.set(file) executableProperty.set(project.files(file))
} }
fun executable(path: String) { fun executable(path: String) {
executableProperty.set { project.file(path) } executableProperty.set { project.files(path) }
} }
fun executable(provider: () -> File) { 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<File>) {
executableProperty.set(provider.map { project.files(it) })
} }
fun executable(provider: Closure<File>) { fun executable(provider: Closure<File>) {
executableProperty.set(project.provider(provider)) executableProperty.set(project.provider(provider).map { project.files(it) })
} }
fun environment(name: String, value: Any) { fun environment(name: String, value: Any) {
@@ -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<S : ExecutionSource, A : KotlinAggregateExecutionSource<S>, T : KotlinTestRun<S>> :
KotlinAggregatingExecution<S, A, T>,
KotlinTestRun<A> {
override fun filter(configureFilter: TestFilter.() -> Unit) {
configureAllExecutions { filter(configureFilter) }
}
}
abstract class KotlinTaskTestRun<S : ExecutionSource, T : AbstractTestTask>(
private val testRunName: String,
override val target: KotlinTarget
) : KotlinTargetTestRun<S>, ExecutionTaskHolder<T> {
override fun getName(): String =
testRunName
override lateinit var executionTask: TaskProvider<T>
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<E : ExecutionSource, A : KotlinAggregateExecutionSource<E>, T : KotlinTestRun<E>>(
val testRunName: String
) : KotlinAggregatingTestRun<E, A, T>,
ExecutionTaskHolder<KotlinTestReport> {
override fun getName() = testRunName
override lateinit var executionTask: TaskProvider<KotlinTestReport>
override fun filter(configureFilter: TestFilter.() -> Unit) = configureAllExecutions { filter(configureFilter) }
}