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>?
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
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
)
)
}
@@ -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<KotlinTarget>
internal set
@Suppress("unused") // DSL
val testableTargets: NamedDomainObjectCollection<KotlinTargetWithTests<*, *>>
get() = targets.withType(KotlinTargetWithTests::class.java)
internal var isGradleMetadataAvailable: 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.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
)
@@ -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
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<KotlinTargetType : KotlinTarget>(
protected val createDefaultSourceSets: Boolean,
protected val createTestCompilation: Boolean
) {
open fun configureTarget(
interface KotlinTargetConfigurator<KotlinTargetType : KotlinTarget> {
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<KotlinTargetType : KotlinTarget>(
protected val createDefaultSourceSets: Boolean,
protected val createTestCompilation: Boolean
) : KotlinTargetConfigurator<KotlinTargetType> {
private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) {
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) {
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<KotlinTargetType : KotlinTarget>
}
}
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<KotlinTargetType : KotlinTarget>
}
}
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<KotlinTargetType : KotlinTarget>
}
}
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<KotlinCompilationType : KotlinCompilation<*>>(
abstract class KotlinOnlyTargetConfigurator<KotlinCompilationType : KotlinCompilation<*>, KotlinTargetType : KotlinOnlyTarget<KotlinCompilationType>>(
createDefaultSourceSets: Boolean,
createTestCompilation: Boolean,
val kotlinPluginVersion: String
) : AbstractKotlinTargetConfigurator<KotlinOnlyTarget<KotlinCompilationType>>(
) : AbstractKotlinTargetConfigurator<KotlinTargetType>(
createDefaultSourceSets,
createTestCompilation
) {
internal abstract fun buildCompilationProcessor(compilation: KotlinCompilationType): KotlinSourceSetProcessor<*>
override fun configureCompilations(platformTarget: KotlinOnlyTarget<KotlinCompilationType>) {
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<KotlinCompilationType : KotlinCompilatio
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 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) {
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 =
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.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)
)
}
}
}
@@ -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<R : KotlinOnlyTarget<T>, T : KotlinCompilation<*>>(
@@ -16,7 +16,7 @@ abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompila
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<R> {
protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T>
protected abstract fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<T, R>
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
target.targetName
@@ -254,7 +254,7 @@ internal fun javaApiUsageForMavenScoping() =
JAVA_API
}
open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
abstract class KotlinOnlyTarget<T : KotlinCompilation<*>>(
project: Project,
override val platformType: KotlinPlatformType
) : AbstractKotlinTarget(project) {
@@ -267,5 +267,4 @@ open class KotlinOnlyTarget<T : KotlinCompilation<*>>(
override var disambiguationClassifier: String? = null
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
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<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>
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<KotlinJsBrowserDsl.() -> 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<KotlinJsNodeDsl.() -> 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() {
@@ -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<KotlinJsCompilation>(true, true, kotlinPluginVersion) {
KotlinOnlyTargetConfigurator<KotlinJsCompilation, KotlinJsTarget>(true, true, kotlinPluginVersion),
KotlinTargetWithTestsConfigurator<KotlinJsReportAggregatingTestRun, KotlinJsTarget> {
override fun configureTarget(target: KotlinOnlyTarget<KotlinJsCompilation>) {
target as KotlinJsTarget
override val testRunClass: Class<KotlinJsReportAggregatingTestRun> 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<KotlinJsCompilation>) {
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<KotlinTestReport>
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<KotlinJsCompilation>) {
super.configureCompilations(platformTarget)
override fun configureCompilations(target: KotlinJsTarget) {
super.configureCompilations(target)
platformTarget.compilations.all {
target.compilations.all {
it.compileKotlinTask.kotlinOptions {
moduleKind = "umd"
sourceMap = true
@@ -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<KotlinJsReportAggregatingTestRun>
}
interface KotlinJsSubTargetDsl {
@@ -36,6 +41,8 @@ interface KotlinJsSubTargetDsl {
ConfigureUtil.configure(fn, this)
}
}
val testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun>
}
interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl {
@@ -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<KotlinJsPlatformTestRun> =
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<KotlinJsTest>(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<KotlinJsTest>(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<KotlinJsTest>
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<KotlinJsTest>(testTaskName)!!.configure(body)
}
}
@@ -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<KotlinJvmCompilation>(
project, KotlinPlatformType.jvm
) {
) : KotlinOnlyTarget<KotlinJvmCompilation>(project, KotlinPlatformType.jvm),
KotlinTargetWithTests<JvmClasspathTestRunSource, KotlinJvmTestRun> {
override lateinit var testRuns: NamedDomainObjectContainer<KotlinJvmTestRun>
var withJavaEnabled = false
private set
@@ -162,4 +165,5 @@ class KotlinJvmTarget(
project.addExtendsFromRelation(compilation.runtimeDependencyConfigurationName, configurationName)
}
}
}
}
@@ -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<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<*> {
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.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
@@ -35,7 +35,7 @@ internal val Project.isKotlinGranularMetadataEnabled: Boolean
get() = PropertiesProvider(rootProject).enableGranularSourceSetsMetadata == true
class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
KotlinTargetConfigurator<KotlinCommonCompilation>(
KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget>(
createDefaultSourceSets = false,
createTestCompilation = false,
kotlinPluginVersion = kotlinPluginVersion
@@ -59,7 +59,7 @@ class KotlinMetadataTargetConfigurator(kotlinPluginVersion: String) :
private val KotlinOnlyTarget<KotlinCommonCompilation>.apiElementsConfiguration: Configuration
get() = project.configurations.getByName(apiElementsConfigurationName)
override fun configureTarget(target: KotlinOnlyTarget<KotlinCommonCompilation>) {
override fun configureTarget(target: KotlinMetadataTarget) {
super.configureTarget(target)
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.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<KotlinOnlyTarget<KotlinCommonCompilation>, KotlinCommonCompilation>(
) : KotlinOnlyTargetPreset<KotlinMetadataTarget, KotlinCommonCompilation>(
project,
kotlinPluginVersion
) {
@@ -37,12 +37,12 @@ class KotlinMetadataTargetPreset(
const val PRESET_NAME = "metadata"
}
override fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<KotlinCommonCompilation> =
override fun createKotlinTargetConfigurator(): KotlinOnlyTargetConfigurator<KotlinCommonCompilation, KotlinMetadataTarget> =
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 {
val mainCompilation = compilations.getByName(KotlinCompilation.MAIN_COMPILATION_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
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<KotlinNativeCompilation>(project, KotlinPlatformType.native) {
@@ -61,4 +65,13 @@ class KotlinNativeTarget(
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.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<T : KotlinNativeTarget>(
private val kotlinPluginVersion: String
) : AbstractKotlinTargetConfigurator<KotlinNativeTarget>(
) : AbstractKotlinTargetConfigurator<T>(
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<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) {
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<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.KonanTarget
class KotlinNativeTargetPreset(
abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
private val name: String,
val project: Project,
val konanTarget: KonanTarget,
private val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinNativeTarget> {
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<T> {
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<T>
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<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
get() = this == HostManager.host
@@ -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<File> = project.objects.property(File::class.java)
@get:Internal
val executableProperty: Property<FileCollection> = 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<FileCollection>
get() = executableProperty.get()
private val executableFile
get() = executableProperty.get().singleFile
init {
onlyIf { executableFile.exists() }
}
@Input
var args: List<String> = 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 <T> Property<T>.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<File>) {
executableProperty.set(provider.map { project.files(it) })
}
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) {
@@ -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) }
}