[WASM] Support wasm backend DSL for d8

This commit is contained in:
Igor Yakovlev
2022-05-03 20:11:17 +02:00
committed by teamcity
parent ee8a90f668
commit 4943d5d683
24 changed files with 973 additions and 196 deletions
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2022 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 kotlin.test
@Suppress("EnumEntryName")
internal enum class IgnoredTestSuitesReporting {
skip, reportAsIgnoredTest, reportAllInnerTestsAsIgnored
}
internal class FrameworkTestArguments(
val includedQualifiers: List<String>,
val includedClassMethods: List<Pair<String, String>>,
val excludedQualifiers: List<String>,
val excludedClassMethods: List<Pair<String, String>>,
val ignoredTestSuites: IgnoredTestSuitesReporting,
val dryRun: Boolean
) {
companion object {
fun parse(args: List<String>): FrameworkTestArguments {
var isInclude = false
var isExclude = false
val includesClassMethods = mutableListOf<Pair<String, String>>()
val includesQualifiers = mutableListOf<String>()
val excludesClassMethods = mutableListOf<Pair<String, String>>()
val excludesQualifiers = mutableListOf<String>()
fun addToIncludeOrExcludeList(argument: String) {
if (argument.isEmpty()) return
if (argument[0].let { it != it.lowercaseChar() }){
val dotIndex = argument.indexOf('.')
val listToAdd = if (isInclude) includesClassMethods else excludesClassMethods
if (dotIndex == -1) {
listToAdd.add(argument to "*")
} else {
if (dotIndex < 1 || dotIndex >= argument.lastIndex) return
val className = argument.substring(0, dotIndex)
val methodName = argument.substring(dotIndex + 1)
listToAdd.add(className to methodName)
}
} else {
(if (isInclude) includesQualifiers else excludesQualifiers).add(argument)
}
}
var ignoredTestSuites: IgnoredTestSuitesReporting = IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored
var isIgnoredTestSuites = false
var dryRun = false
for (arg in args) {
if (isInclude || isExclude) {
for (splitArg in arg.split(',')) {
addToIncludeOrExcludeList(splitArg)
}
isInclude = false
isExclude = false
continue
}
if (isIgnoredTestSuites) {
val value = IgnoredTestSuitesReporting.values().firstOrNull { it.name == arg }
if (value != null) {
ignoredTestSuites = value
}
isIgnoredTestSuites = false
continue
}
when (arg) {
"--include" -> isInclude = true
"--exclude" -> isExclude = true
"--ignoredTestSuites" -> isIgnoredTestSuites = true
"--dryRun" -> dryRun = true
}
}
if (includesClassMethods.isEmpty() && includesQualifiers.isEmpty()) {
includesQualifiers.add("*")
}
return FrameworkTestArguments(
includedQualifiers = includesQualifiers,
includedClassMethods = includesClassMethods,
excludedQualifiers = excludesQualifiers,
excludedClassMethods = excludesClassMethods,
ignoredTestSuites = ignoredTestSuites,
dryRun = dryRun
)
}
}
}
@@ -6,6 +6,12 @@
package kotlin.test
import kotlin.test.FrameworkAdapter
import kotlin.math.abs
@JsFun("() => (typeof arguments !== 'undefined' && typeof arguments.join !== 'undefined') ? arguments.join(' ') : '' ")
private external fun d8Arguments(): String
@JsFun("() => (typeof process != 'undefined' && typeof process.argv != 'undefined') ? process.argv.slice(2).join(' ') : ''")
private external fun nodeArguments(): String
internal class TeamcityAdapter : FrameworkAdapter {
private enum class MessageType(val type: String) {
@@ -30,41 +36,153 @@ internal class TeamcityAdapter : FrameworkAdapter {
?.replace("]", "|]")
?: ""
private val flowId: String = "flowId='wasmTcAdapter${abs(hashCode())}'"
private fun MessageType.report(name: String) {
println("##teamcity[$type name='${name.tcEscape()}']")
println("##teamcity[$type name='${name.tcEscape()}' $flowId]")
}
private fun MessageType.report(name: String, e: Throwable) {
if (this == MessageType.Failed) {
println("##teamcity[$type name='${name.tcEscape()}' message='${e.message.tcEscape()}' details='${e.stackTraceToString().tcEscape()}']")
println("##teamcity[$type name='${name.tcEscape()}' message='${e.message.tcEscape()}' details='${e.stackTraceToString().tcEscape()}' $flowId]")
} else {
println("##teamcity[$type name='${name.tcEscape()}' text='${e.message.tcEscape()}' errorDetails='${e.stackTraceToString().tcEscape()}' status='ERROR']")
println("##teamcity[$type name='${name.tcEscape()}' text='${e.message.tcEscape()}' errorDetails='${e.stackTraceToString().tcEscape()}' status='ERROR' $flowId]")
}
}
private val testArguments: FrameworkTestArguments by lazy {
val arguments = d8Arguments().takeIf { it.isNotEmpty() } ?: nodeArguments()
FrameworkTestArguments.parse(arguments.split(' '))
}
private fun runSuite(name: String, suiteFn: () -> Unit) {
MessageType.SuiteStarted.report(name)
try {
suiteFn()
MessageType.SuiteFinished.report(name)
} catch (e: Throwable) {
MessageType.SuiteFinished.report(name, e)
}
}
private fun runIgnoredSuite(name: String, suiteFn: () -> Unit) {
MessageType.SuiteStarted.report(name)
suiteFn()
MessageType.SuiteFinished.report(name)
}
private var isUnderIgnoredSuit: Boolean = false
private var qualifiedName: String = ""
private var className: String = ""
private inline fun enterIfIncluded(name: String, isSuit: Boolean, body: () -> Unit) {
if (name.isEmpty()) {
body()
return
}
val oldQualifiedName = qualifiedName
val oldClassName = className
try {
qualifiedName = if (oldQualifiedName.isEmpty()) name else "$oldQualifiedName.$name"
className = name
if (isSuit) {
body()
} else {
val runTest = testArguments.run {
val included =
includedClassMethods.any { it.first == oldClassName && name.matched(it.second) } ||
includedQualifiers.any { oldQualifiedName.matched(it) || qualifiedName.matched(it) }
included &&
excludedClassMethods.none { it.first == oldClassName && name.matched(it.second) } &&
excludedQualifiers.none { oldQualifiedName.matched(it) || qualifiedName.matched(it) }
}
if (runTest) {
body()
}
}
} finally {
qualifiedName = oldQualifiedName
className = oldClassName
}
}
override fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) {
MessageType.SuiteStarted.report(name)
if (isUnderIgnoredSuit) {
runIgnoredSuite(name, suiteFn)
return
}
if (!ignored) {
try {
suiteFn()
MessageType.SuiteFinished.report(name)
} catch (e: Throwable) {
MessageType.SuiteFinished.report(name, e)
enterIfIncluded(name, true) {
runSuite(name, suiteFn)
}
} else {
when (testArguments.ignoredTestSuites) {
IgnoredTestSuitesReporting.reportAsIgnoredTest -> {
MessageType.Ignored.report(name)
}
IgnoredTestSuitesReporting.reportAllInnerTestsAsIgnored -> {
var oldIsUnderIgnoredSuit = isUnderIgnoredSuit
isUnderIgnoredSuit = true
try {
runIgnoredSuite(name, suiteFn)
} finally {
isUnderIgnoredSuit = oldIsUnderIgnoredSuit
}
}
IgnoredTestSuitesReporting.skip -> { }
}
}
}
override fun test(name: String, ignored: Boolean, testFn: () -> Any?) {
if (isUnderIgnoredSuit) {
MessageType.Ignored.report(name)
return
}
if (ignored) {
MessageType.Ignored.report(name)
} else {
return
}
enterIfIncluded(name, false) {
try {
MessageType.Started.report(name)
testFn()
if (!testArguments.dryRun) {
testFn()
}
} catch (e: Throwable) {
MessageType.Failed.report(name, e)
}
MessageType.Finished.report(name)
}
}
}
private fun String.matched(prefix: String, startSourceIndex: Int = 0, startPrefixIndex: Int = 0): Boolean {
var sourceIndex = startSourceIndex
var prefixIndex = startPrefixIndex
if (prefixIndex == prefix.lastIndex && prefix[prefixIndex] == '*') return true
if (sourceIndex > this.lastIndex) return false
while (prefixIndex <= prefix.lastIndex && sourceIndex <= this.lastIndex) {
val currentSearchSymbol = prefix[prefixIndex]
if (currentSearchSymbol == '*') {
if (prefixIndex == prefix.lastIndex) return true
val searchSymbolAfterStar = prefix[prefixIndex + 1]
val foundIndexOfSymbolAfterStar = this.indexOf(searchSymbolAfterStar, sourceIndex)
if (foundIndexOfSymbolAfterStar == -1) return false
if (this.matched(prefix, foundIndexOfSymbolAfterStar + 1, prefixIndex)) return true
sourceIndex = foundIndexOfSymbolAfterStar
} else {
if (currentSearchSymbol != this[sourceIndex]) return false
sourceIndex++
}
prefixIndex++
}
return sourceIndex > this.lastIndex &&
(prefixIndex > prefix.lastIndex || (prefixIndex == prefix.lastIndex && prefix[prefixIndex] == '*'))
}
@@ -8,16 +8,18 @@ package org.jetbrains.kotlin.gradle.dsl
import groovy.lang.Closure
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinWasmTargetPreset
interface KotlinTargetContainerWithWasmPresetFunctions : KotlinTargetContainerWithPresetFunctions {
fun wasm(
name: String = "wasm",
configure: KotlinJsTargetDsl.() -> Unit = { }
configure: KotlinWasmTargetDsl.() -> Unit = { }
): KotlinJsTargetDsl =
configureOrCreate(
name,
presets.getByName("wasm") as KotlinJsIrTargetPreset,
presets.getByName("wasm") as KotlinWasmTargetPreset,
configure
)
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.gradle.plugin.sources.checkSourceSetVisibilityRequir
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
import org.jetbrains.kotlin.gradle.scripting.internal.ScriptingGradleSubplugin
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTargetPreset
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinWasmTargetPreset
import org.jetbrains.kotlin.gradle.targets.native.tasks.artifact.registerKotlinArtifactsExtension
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool
import org.jetbrains.kotlin.gradle.tasks.locateTask
@@ -195,14 +196,13 @@ class KotlinMultiplatformPlugin : Plugin<Project> {
with(project.multiplatformExtension.presets) {
add(KotlinJvmTargetPreset(project))
add(KotlinJsTargetPreset(project).apply { irPreset = null })
add(KotlinJsIrTargetPreset(project, isWasm = false).apply { mixedMode = false })
add(KotlinJsIrTargetPreset(project).apply { mixedMode = false })
add(
KotlinJsTargetPreset(project).apply {
irPreset = KotlinJsIrTargetPreset(project, isWasm = false)
.apply { mixedMode = true }
irPreset = KotlinJsIrTargetPreset(project).apply { mixedMode = true }
}
)
add(KotlinJsIrTargetPreset(project, isWasm = true).apply { mixedMode = false })
add(KotlinWasmTargetPreset(project))
add(KotlinAndroidTargetPreset(project))
add(KotlinJvmWithJavaTargetPreset(project))
@@ -16,10 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Compa
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.LEGACY
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsBinaryContainer
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs
@@ -29,7 +26,6 @@ import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport
import org.jetbrains.kotlin.gradle.testing.testTaskName
import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
import org.jetbrains.kotlin.gradle.utils.listProperty
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.gradle.utils.setProperty
import javax.inject.Inject
@@ -143,6 +139,7 @@ constructor(
private val propertiesProvider = PropertiesProvider(project)
//Browser
private val browserLazyDelegate = lazy {
project.objects.newInstance(KotlinBrowserJs::class.java, this).also {
it.configure()
@@ -170,6 +167,7 @@ constructor(
irTarget?.browser(body)
}
//node.js
private val nodejsLazyDelegate = lazy {
project.objects.newInstance(KotlinNodeJs::class.java, this).also {
it.configure()
@@ -0,0 +1,14 @@
package org.jetbrains.kotlin.gradle.targets.js.d8
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.File
import java.net.URL
data class D8Env(
val cleanableStore: CleanableStore,
val zipPath: File,
val targetPath: File,
val executablePath: File,
val isWindows: Boolean,
val downloadUrl: URL
)
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2020 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.d8
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.tasks.registerTask
import org.jetbrains.kotlin.gradle.utils.newFileProperty
import javax.inject.Inject
open class D8Exec
@Inject
constructor(
private val compilation: KotlinJsCompilation
) : AbstractExecTask<D8Exec>(D8Exec::class.java) {
@Transient
@get:Internal
lateinit var d8: D8RootExtension
init {
onlyIf {
!inputFileProperty.isPresent || inputFileProperty.asFile.map { it.exists() }.get()
}
}
@Input
var d8Args: MutableList<String> = mutableListOf()
@Optional
@PathSensitive(PathSensitivity.ABSOLUTE)
@InputFile
val inputFileProperty: RegularFileProperty = project.newFileProperty()
override fun exec() {
val newArgs = mutableListOf<String>()
newArgs.addAll(d8Args)
if (inputFileProperty.isPresent) {
val inputFile = inputFileProperty.asFile.get()
workingDir = inputFile.parentFile
if (compilation.target.platformType == KotlinPlatformType.wasm) {
newArgs.add("--module")
}
newArgs.add(inputFile.canonicalPath)
}
args?.let {
if (it.isNotEmpty()) {
newArgs.add("--")
newArgs.addAll(it)
}
}
this.args = newArgs
super.exec()
}
companion object {
fun create(
compilation: KotlinJsCompilation,
name: String,
configuration: D8Exec.() -> Unit = {}
): TaskProvider<D8Exec> {
val target = compilation.target
val project = target.project
val d8 = D8RootPlugin.apply(project.rootProject)
return project.registerTask(
name,
listOf(compilation)
) {
it.d8 = d8
it.executable = d8.requireConfigured().executablePath.absolutePath
it.dependsOn(d8.setupTaskProvider)
it.dependsOn(compilation.compileKotlinTaskProvider)
it.configuration()
}
}
}
}
@@ -0,0 +1,30 @@
package org.jetbrains.kotlin.gradle.targets.js.d8
/**
* Provides platform and architecture names that is used to download D8.
*/
internal object D8Platform {
private val props = System.getProperties()
private fun property(name: String) = props.getProperty(name) ?: System.getProperty(name)
const val WIN = "win"
const val LINUX = "linux"
const val DARWIN = "mac"
val name: String = run {
val name = property("os.name").toLowerCase()
when {
name.contains("windows") -> WIN
name.contains("mac") -> DARWIN
name.contains("linux") -> LINUX
name.contains("freebsd") -> LINUX
else -> throw IllegalArgumentException("Unsupported OS: $name")
}
}
const val X64 = "64"
const val X86 = "86"
val architecture: String
get() = if (property("os.arch").contains("64")) X64 else X86
}
@@ -0,0 +1,58 @@
/*
* Copyright 2010-2020 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.d8
import org.gradle.api.Project
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.Serializable
import java.net.URL
open class D8RootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware<D8Env>(), Serializable {
init {
check(rootProject.rootProject == rootProject)
}
private val gradleHome = rootProject.gradle.gradleUserHomeDir.also {
rootProject.logger.kotlinInfo("Storing cached files in $it")
}
var installationPath by Property(gradleHome.resolve("d8"))
var downloadBaseUrl by Property("https://storage.googleapis.com/chromium-v8/official/canary/")
var version by Property("10.2.9")
var edition by Property("rel") // rel or dbg
val setupTaskProvider: TaskProvider<out Copy>
get() = rootProject.tasks.withType(Copy::class.java).named(D8RootPlugin.INSTALL_TASK_NAME)
override fun finalizeConfiguration(): D8Env {
val platform = D8Platform.name
val architecture = D8Platform.architecture
val d8osString = platform + architecture
val requiredVersionName = "v8-$d8osString-$edition-$version"
val requiredZipName = "$requiredVersionName.zip"
val cleanableStore = CleanableStore[installationPath.absolutePath]
val targetPath = cleanableStore[requiredVersionName].use()
val isWindows = D8Platform.name == D8Platform.WIN
return D8Env(
cleanableStore = cleanableStore,
zipPath = cleanableStore[requiredZipName].use(),
targetPath = targetPath,
executablePath = targetPath.resolve(if (isWindows) "d8.exe" else "d8"),
isWindows = isWindows,
downloadUrl = URL("${downloadBaseUrl.trimEnd('/')}/$requiredZipName"),
)
}
companion object {
const val EXTENSION_NAME: String = "kotlinD8"
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 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.d8
import de.undercouch.gradle.tasks.download.Download
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootExtension.Companion.EXTENSION_NAME
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
open class D8RootPlugin : Plugin<Project> {
override fun apply(project: Project) {
MultiplePluginDeclarationDetector.detect(project)
project.plugins.apply(BasePlugin::class.java)
check(project == project.rootProject) {
"D8RootPlugin can be applied only to root project"
}
val settings = project.extensions.create(EXTENSION_NAME, D8RootExtension::class.java, project)
val downloadTask = project.registerTask<Download>("${TASKS_GROUP_NAME}Download") {
val env = settings.requireConfigured()
it.group = TASKS_GROUP_NAME
it.src(env.downloadUrl)
it.dest(env.zipPath)
it.overwrite(false)
it.description = "Download local d8 version"
}
project.registerTask<Copy>(INSTALL_TASK_NAME) {
val env = settings.requireConfigured()
it.onlyIf { env.zipPath.exists() && !env.executablePath.exists() }
it.group = TASKS_GROUP_NAME
it.from(project.zipTree(env.zipPath))
it.into(env.targetPath)
it.dependsOn(downloadTask)
it.description = "Install local d8 version"
}
project.registerTask<CleanDataTask>("d8" + CleanDataTask.NAME_SUFFIX) {
it.cleanableStoreProvider = project.provider { settings.requireConfigured().cleanableStore }
it.group = TASKS_GROUP_NAME
it.description = "Clean unused local d8 version"
}
}
companion object {
const val TASKS_GROUP_NAME: String = "d8"
const val INSTALL_TASK_NAME: String = "${TASKS_GROUP_NAME}Install"
fun apply(rootProject: Project): D8RootExtension {
check(rootProject == rootProject.rootProject)
rootProject.plugins.apply(D8RootPlugin::class.java)
return rootProject.extensions.getByName(EXTENSION_NAME) as D8RootExtension
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2022 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.dsl
import groovy.lang.Closure
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import org.jetbrains.kotlin.gradle.targets.js.d8.D8Exec
interface KotlinWasmSubTargetContainerDsl : KotlinTarget {
val d8: KotlinWasmD8Dsl
val isD8Configured: Boolean
fun whenD8Configured(body: KotlinWasmD8Dsl.() -> Unit)
}
interface KotlinWasmTargetDsl : KotlinJsTargetDsl {
fun d8() = d8 { }
fun d8(body: KotlinWasmD8Dsl.() -> Unit)
fun d8(fn: Closure<*>) {
d8 {
ConfigureUtil.configure(fn, this)
}
}
}
interface KotlinWasmD8Dsl : KotlinJsSubTargetDsl {
fun runTask(body: D8Exec.() -> Unit)
}
@@ -35,6 +35,8 @@ open class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
KotlinJsIrSubTarget(target, "browser"),
KotlinJsBrowserDsl {
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val webpackTaskConfigurations: MutableList<KotlinWebpack.() -> Unit> = mutableListOf()
private val runTaskConfigurations: MutableList<KotlinWebpack.() -> Unit> = mutableListOf()
@@ -45,12 +47,25 @@ open class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
override val testTaskDescription: String
get() = "Run all ${target.name} tests inside browser using karma and webpack"
override fun configureDefaultTestFramework(it: KotlinJsTest) {
it.useKarma {
useChromeHeadless()
override fun configureTestDependencies(test: KotlinJsTest) {
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
}
override fun configureDefaultTestFramework(test: KotlinJsTest) {
if (test.testFramework == null) {
test.useKarma {
useChromeHeadless()
}
}
if (test.enabled) {
nodeJs.taskRequirements.addTaskRequirements(test)
}
}
override val additionalCompilerOption: String?
get() = "-Xwasm-launcher=esm".takeIf { target.platformType == KotlinPlatformType.wasm }
override fun commonWebpackConfig(body: KotlinWebpackConfig.() -> Unit) {
webpackTaskConfigurations.add {
webpackConfigApplier(body)
@@ -282,20 +297,6 @@ open class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
KotlinJsBinaryMode.DEVELOPMENT -> debugValue
}
override fun addLinkOptions(compilation: KotlinJsIrCompilation) {
if (compilation.platformType != KotlinPlatformType.wasm)
return
// Wasm requires different kinds of launchers for browser and nodejs. This might change in the future.
compilation.binaries
.withType(JsIrBinary::class.java)
.all {
it.linkTask.configure { linkTask ->
linkTask.kotlinOptions.freeCompilerArgs += "-Xwasm-launcher=esm"
}
}
}
companion object {
private const val WEBPACK_TASK_NAME = "webpack"
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2022 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.ir
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.targets.js.d8.D8Exec
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWasmD8Dsl
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinWasmD8
import org.jetbrains.kotlin.gradle.tasks.withType
import javax.inject.Inject
open class KotlinD8Ir @Inject constructor(target: KotlinJsIrTarget) :
KotlinJsIrSubTargetBase(target, "d8"),
KotlinWasmD8Dsl {
private val d8 = D8RootPlugin.apply(project.rootProject)
override val testTaskDescription: String
get() = "Run all ${target.name} tests inside d8 using the builtin test framework"
override fun runTask(body: D8Exec.() -> Unit) {
project.tasks.withType<D8Exec>().named(runTaskName).configure(body)
}
override fun configureDefaultTestFramework(test: KotlinJsTest) {
test.testFramework = KotlinWasmD8(test)
}
override fun configureTestDependencies(test: KotlinJsTest) {
test.dependsOn(d8.setupTaskProvider)
}
override val additionalCompilerOption: String?
get() = "-Xwasm-launcher=d8".takeIf { target.platformType == KotlinPlatformType.wasm }
}
@@ -11,9 +11,7 @@ import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.TaskProvider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.isMain
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun
@@ -21,7 +19,6 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
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.testing.KotlinJsTest
@@ -38,8 +35,6 @@ abstract class KotlinJsIrSubTarget(
) : KotlinJsSubTargetDsl {
val project get() = target.project
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
abstract val testTaskDescription: String
final override lateinit var testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun>
@@ -107,7 +102,16 @@ abstract class KotlinJsIrSubTarget(
}
}
abstract fun addLinkOptions(compilation: KotlinJsIrCompilation)
private fun addLinkOptions(compilation: KotlinJsIrCompilation) {
val additionalCompilerOption = additionalCompilerOption ?: return
compilation.binaries
.withType(JsIrBinary::class.java)
.all {
it.linkTask.configure { linkTask ->
linkTask.kotlinOptions.freeCompilerArgs += additionalCompilerOption
}
}
}
private fun configureTestsRun(testRun: KotlinJsPlatformTestRun, compilation: KotlinJsIrCompilation) {
fun KotlinJsPlatformTestRun.subtargetTestTaskName(): String = disambiguateCamelCased(
@@ -139,7 +143,7 @@ abstract class KotlinJsIrSubTarget(
)
)
testJs.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
configureTestDependencies(testJs)
testJs.onlyIf { task ->
(task as KotlinJsTest).inputFileProperty
@@ -166,18 +170,14 @@ abstract class KotlinJsIrSubTarget(
project.whenEvaluated {
testJs.configure {
if (it.testFramework == null) {
configureDefaultTestFramework(it)
}
if (it.enabled) {
nodeJs.taskRequirements.addTaskRequirements(it)
}
configureDefaultTestFramework(it)
}
}
}
protected abstract fun configureDefaultTestFramework(it: KotlinJsTest)
protected abstract fun configureDefaultTestFramework(test: KotlinJsTest)
protected abstract fun configureTestDependencies(test: KotlinJsTest)
protected abstract val additionalCompilerOption: String?
private fun configureMain() {
target.compilations.all { compilation ->
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2022 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.ir
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateTask
abstract class KotlinJsIrSubTargetBase(target: KotlinJsIrTarget, classifier: String) :
KotlinJsIrSubTarget(target, classifier) {
protected val runTaskName = disambiguateCamelCased("run")
override fun configureRun(compilation: KotlinJsIrCompilation) {
compilation.binaries
.withType(JsIrBinary::class.java)
.matching { it is Executable }
.all { developmentExecutable ->
configureRun(developmentExecutable)
}
}
private fun configureRun(binary: JsIrBinary) {
val binaryRunName = disambiguateCamelCased(
binary.mode.name.toLowerCase(),
RUN_TASK_NAME
)
locateOrRegisterRunTask(binary, binaryRunName)
if (binary.mode == KotlinJsBinaryMode.DEVELOPMENT) {
val runName = disambiguateCamelCased(RUN_TASK_NAME)
locateOrRegisterRunTask(binary, runName)
}
}
private fun locateOrRegisterRunTask(
binary: JsIrBinary,
name: String
) {
val runTask = project.locateTask<NodeJsExec>(name)
if (runTask == null) {
val runTaskHolder = NodeJsExec.create(binary.compilation, name) {
group = taskGroupName
inputFileProperty.set(
project.layout.file(
binary.linkSyncTask.map {
it.destinationDir
.resolve(binary.linkTask.get().outputFileProperty.get().name)
}
)
)
}
target.runTask.dependsOn(runTaskHolder)
}
}
override fun configureBuild(compilation: KotlinJsIrCompilation) {
compilation.binaries
.getIrBinaries(KotlinJsBinaryMode.PRODUCTION)
.matching { it is Executable }
.all { productionExecutable ->
project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(productionExecutable.linkTask)
}
}
override fun configureLibrary(compilation: KotlinJsIrCompilation) {
super.configureLibrary(compilation)
compilation.binaries
.withType(JsIrBinary::class.java)
.matching { it is Library }
.all { binary ->
configureRun(binary)
}
}
}
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinTargetWithBinaries
import org.jetbrains.kotlin.gradle.targets.js.JsAggregatingExecutionSource
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask
import org.jetbrains.kotlin.gradle.tasks.registerTask
@@ -40,7 +37,9 @@ constructor(
KotlinTargetWithBinaries<KotlinJsIrCompilation, KotlinJsBinaryContainer>(project, platformType),
KotlinTargetWithTests<JsAggregatingExecutionSource, KotlinJsReportAggregatingTestRun>,
KotlinJsTargetDsl,
KotlinJsSubTargetContainerDsl {
KotlinWasmTargetDsl,
KotlinJsSubTargetContainerDsl,
KotlinWasmSubTargetContainerDsl {
override lateinit var testRuns: NamedDomainObjectContainer<KotlinJsReportAggregatingTestRun>
internal set
@@ -151,6 +150,7 @@ constructor(
}
}
//Browser
private val browserLazyDelegate = lazy {
commonLazy
project.objects.newInstance(KotlinBrowserJsIr::class.java, this).also {
@@ -173,6 +173,7 @@ constructor(
body(browser)
}
//node.js
private val nodejsLazyDelegate = lazy {
commonLazy
project.objects.newInstance(KotlinNodeJsIr::class.java, this).also {
@@ -196,11 +197,35 @@ constructor(
body(nodejs)
}
//d8
private val d8LazyDelegate = lazy {
commonLazy
project.objects.newInstance(KotlinD8Ir::class.java, this).also {
it.configureSubTarget()
d8ConfiguredHandlers.forEach { handler ->
handler(it)
}
d8ConfiguredHandlers.clear()
}
}
private val d8ConfiguredHandlers = mutableListOf<KotlinWasmD8Dsl.() -> Unit>()
override val d8 by d8LazyDelegate
override val isD8Configured: Boolean
get() = d8LazyDelegate.isInitialized()
private fun KotlinJsIrSubTarget.configureSubTarget() {
configureTestSideEffect
configure()
}
override fun d8(body: KotlinWasmD8Dsl.() -> Unit) {
body(d8)
}
override fun whenBrowserConfigured(body: KotlinJsBrowserDsl.() -> Unit) {
if (browserLazyDelegate.isInitialized()) {
browser(body)
@@ -217,6 +242,14 @@ constructor(
}
}
override fun whenD8Configured(body: KotlinWasmD8Dsl.() -> Unit) {
if (d8LazyDelegate.isInitialized()) {
d8(body)
} else {
d8ConfiguredHandlers += body
}
}
override fun useCommonJs() {
compilations.all {
it.kotlinOptions.configureCommonJsOptions()
@@ -19,8 +19,7 @@ import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenE
import org.jetbrains.kotlin.statistics.metrics.StringMetrics
open class KotlinJsIrTargetPreset(
project: Project,
isWasm: Boolean,
project: Project
) : KotlinOnlyTargetPreset<KotlinJsIrTarget, KotlinJsIrCompilation>(
project
) {
@@ -29,21 +28,9 @@ open class KotlinJsIrTargetPreset(
open val isMpp: Boolean
get() = true
override val platformType: KotlinPlatformType =
if (isWasm)
KotlinPlatformType.wasm
else
KotlinPlatformType.js
override val platformType: KotlinPlatformType = KotlinPlatformType.js
override fun instantiateTarget(name: String): KotlinJsIrTarget {
if (platformType == KotlinPlatformType.wasm && !PropertiesProvider(project).wasmStabilityNoWarn) {
project.logger.warn(
"""
New 'wasm' target is Work-in-Progress and is subject to change without notice.
""".trimIndent()
)
}
return project.objects.newInstance(KotlinJsIrTarget::class.java, project, platformType, mixedMode).apply {
this.isMpp = this@KotlinJsIrTargetPreset.isMpp
if (!mixedMode) {
@@ -87,11 +74,7 @@ open class KotlinJsIrTargetPreset(
return result
}
override fun getName(): String = when (platformType) {
KotlinPlatformType.wasm -> WASM_PRESET_NAME
KotlinPlatformType.js -> JS_PRESET_NAME
else -> error("Unsupported platform type")
}
override fun getName(): String = JS_PRESET_NAME
//TODO[Ilya Goncharov] remove public morozov
public override fun createCompilationFactory(
@@ -104,15 +87,13 @@ open class KotlinJsIrTargetPreset(
"js",
KotlinJsCompilerType.IR.lowerName
)
private const val WASM_PRESET_NAME = "wasm"
}
}
class KotlinJsIrSingleTargetPreset(
project: Project
) : KotlinJsIrTargetPreset(
project,
isWasm = false,
project
) {
override val isMpp: Boolean
get() = false
@@ -5,114 +5,45 @@
package org.jetbrains.kotlin.gradle.targets.js.ir
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.tasks.dependsOn
import org.jetbrains.kotlin.gradle.tasks.locateTask
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinWasmNode
import org.jetbrains.kotlin.gradle.tasks.withType
import javax.inject.Inject
open class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) :
KotlinJsIrSubTarget(target, "node"),
KotlinJsIrSubTargetBase(target, "node"),
KotlinJsNodeDsl {
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
override val testTaskDescription: String
get() = "Run all ${target.name} tests inside nodejs using the builtin test framework"
private val runTaskName = disambiguateCamelCased("run")
override fun runTask(body: NodeJsExec.() -> Unit) {
project.tasks.withType<NodeJsExec>().named(runTaskName).configure(body)
}
override fun configureDefaultTestFramework(it: KotlinJsTest) {
it.useMocha { }
override fun configureTestDependencies(test: KotlinJsTest) {
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
}
override fun configureRun(
compilation: KotlinJsIrCompilation
) {
compilation.binaries
.withType(JsIrBinary::class.java)
.matching { it is Executable }
.all { developmentExecutable ->
configureRun(developmentExecutable)
override fun configureDefaultTestFramework(test: KotlinJsTest) {
if (target.platformType != KotlinPlatformType.wasm) {
if (test.testFramework == null) {
test.useMocha { }
}
}
private fun configureRun(binary: JsIrBinary) {
val binaryRunName = disambiguateCamelCased(
binary.mode.name.toLowerCase(),
RUN_TASK_NAME
)
locateOrRegisterRunTask(binary, binaryRunName)
if (binary.mode == KotlinJsBinaryMode.DEVELOPMENT) {
val runName = disambiguateCamelCased(
RUN_TASK_NAME
)
locateOrRegisterRunTask(binary, runName)
if (test.enabled) {
nodeJs.taskRequirements.addTaskRequirements(test)
}
} else {
test.testFramework = KotlinWasmNode(test)
}
}
private fun locateOrRegisterRunTask(
binary: JsIrBinary,
name: String
) {
val runTask = project.locateTask<NodeJsExec>(name)
if (runTask == null) {
val runTaskHolder = NodeJsExec.create(binary.compilation, name) {
group = taskGroupName
inputFileProperty.set(
project.layout.file(
binary.linkSyncTask.map {
it.destinationDir
.resolve(binary.linkTask.get().outputFileProperty.get().name)
}
)
)
}
target.runTask.dependsOn(runTaskHolder)
}
}
override fun configureBuild(
compilation: KotlinJsIrCompilation
) {
compilation.binaries
.getIrBinaries(KotlinJsBinaryMode.PRODUCTION)
.matching { it is Executable }
.all { productionExecutable ->
project.tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME).dependsOn(productionExecutable.linkTask)
}
}
override fun configureLibrary(compilation: KotlinJsIrCompilation) {
super.configureLibrary(compilation)
compilation.binaries
.withType(JsIrBinary::class.java)
.matching { it is Library }
.all { binary ->
configureRun(binary)
}
}
override fun addLinkOptions(compilation: KotlinJsIrCompilation) {
if (compilation.platformType != KotlinPlatformType.wasm)
return
// Wasm requires different kinds of launchers for browser and nodejs. This might change in the future.
compilation.binaries
.withType(JsIrBinary::class.java)
.all {
it.linkTask.configure { linkTask ->
linkTask.kotlinOptions.freeCompilerArgs += "-Xwasm-launcher=nodejs"
}
}
}
override val additionalCompilerOption: String?
get() = "-Xwasm-launcher=nodejs".takeIf { target.platformType == KotlinPlatformType.wasm }
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2020 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.ir
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinCompilationFactory
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.PublicationRegistrationMode
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.hasKpmModel
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.mapTargetCompilationsToKpmVariants
import org.jetbrains.kotlin.gradle.utils.runProjectConfigurationHealthCheckWhenEvaluated
class KotlinWasmTargetPreset(
project: Project,
) : KotlinOnlyTargetPreset<KotlinJsIrTarget, KotlinJsIrCompilation>(project) {
override val platformType: KotlinPlatformType = KotlinPlatformType.wasm
override fun instantiateTarget(name: String): KotlinJsIrTarget {
if (!PropertiesProvider(project).wasmStabilityNoWarn) {
project.logger.warn("New 'wasm' target is Work-in-Progress and is subject to change without notice.")
}
val irTarget = project.objects.newInstance(KotlinJsIrTarget::class.java, project, KotlinPlatformType.wasm, false)
irTarget.isMpp = true
project.runProjectConfigurationHealthCheckWhenEvaluated {
if (!irTarget.isBrowserConfigured && !irTarget.isNodejsConfigured && !irTarget.isD8Configured) {
project.logger.warn(
"""
Please choose a JavaScript environment to run tests.
kotlin {
wasm {
// To build distributions for and run tests on browser, Node.js or d8 use one:
browser()
nodejs()
d8()
}
}
""".trimIndent()
)
}
}
return irTarget
}
override fun createKotlinTargetConfigurator(): AbstractKotlinTargetConfigurator<KotlinJsIrTarget> =
KotlinJsIrTargetConfigurator()
override fun createTarget(name: String): KotlinJsIrTarget {
val result = super.createTarget(name)
if (project.hasKpmModel) {
mapTargetCompilationsToKpmVariants(result, PublicationRegistrationMode.IMMEDIATE)
}
return result
}
override fun getName(): String = WASM_PRESET_NAME
public override fun createCompilationFactory(
forTarget: KotlinJsIrTarget
): KotlinCompilationFactory<KotlinJsIrCompilation> =
KotlinJsIrCompilationFactory(project, forTarget)
companion object {
private const val WASM_PRESET_NAME = "wasm"
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2022 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.testing
import org.gradle.api.Project
import org.gradle.process.ProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.isTeamCity
import org.jetbrains.kotlin.gradle.utils.getValue
internal class KotlinWasmD8(private val kotlinJsTest: KotlinJsTest) : KotlinJsTestFramework {
override val settingsState: String = "KotlinWasmD8"
override val compilation: KotlinJsCompilation = kotlinJsTest.compilation
private val project: Project = compilation.target.project
private val d8 = D8RootPlugin.apply(project.rootProject)
private val d8Executable by project.provider { d8.requireConfigured().executablePath }
init {
kotlinJsTest.outputs.upToDateWhen { false }
}
override fun createTestExecutionSpec(
task: KotlinJsTest,
forkOptions: ProcessForkOptions,
nodeJsArgs: MutableList<String>,
debug: Boolean
): TCServiceMessagesTestExecutionSpec {
val compiledFile = task.inputFileProperty.get().asFile
forkOptions.executable = d8Executable.absolutePath
forkOptions.workingDir = compiledFile.parentFile
val clientSettings = TCServiceMessagesClientSettings(
task.name,
testNameSuffix = task.targetName,
prependSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true,
escapeTCMessagesInLog = project.isTeamCity
)
val cliArgs = KotlinTestRunnerCliArgs(
include = task.includePatterns,
exclude = task.excludePatterns
)
val args = mutableListOf(
"--module",
compiledFile.absolutePath,
"--experimental-wasm-typed-funcref",
"--experimental-wasm-gc",
"--experimental-wasm-eh",
)
args.add("--")
args.addAll(cliArgs.toList())
val dryRunArgs = mutableListOf<String>()
dryRunArgs.addAll(args)
dryRunArgs.add("--dryRun")
return TCServiceMessagesTestExecutionSpec(
forkOptions = forkOptions,
args = args,
checkExitCode = false,
clientSettings = clientSettings,
dryRunArgs = dryRunArgs
)
}
override val requiredNpmDependencies: Set<RequiredKotlinJsDependency> = emptySet()
override fun getPath(): String = "${kotlinJsTest.path}:kotlinTestFrameworkStub"
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2022 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.testing
import org.gradle.api.Project
import org.gradle.process.ProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.isTeamCity
internal class KotlinWasmNode(private val kotlinJsTest: KotlinJsTest) : KotlinJsTestFramework {
override val settingsState: String = "KotlinWasmNode"
override val compilation: KotlinJsCompilation = kotlinJsTest.compilation
private val project: Project = compilation.target.project
init {
kotlinJsTest.outputs.upToDateWhen { false }
}
override fun createTestExecutionSpec(
task: KotlinJsTest,
forkOptions: ProcessForkOptions,
nodeJsArgs: MutableList<String>,
debug: Boolean
): TCServiceMessagesTestExecutionSpec {
val compiledFile = task.inputFileProperty.get().asFile
val clientSettings = TCServiceMessagesClientSettings(
task.name,
testNameSuffix = task.targetName,
prependSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true,
escapeTCMessagesInLog = project.isTeamCity
)
val cliArgs = KotlinTestRunnerCliArgs(
include = task.includePatterns,
exclude = task.excludePatterns
)
val args = mutableListOf<String>()
with(args) {
add("--experimental-wasm-typed-funcref")
add("--experimental-wasm-gc")
add("--experimental-wasm-eh")
add(compiledFile.absolutePath)
addAll(cliArgs.toList())
}
val dryRunArgs = mutableListOf<String>()
dryRunArgs.addAll(args)
dryRunArgs.add("--dryRun")
return TCServiceMessagesTestExecutionSpec(
forkOptions = forkOptions,
args = args,
checkExitCode = false,
clientSettings = clientSettings,
dryRunArgs = dryRunArgs
)
}
override val requiredNpmDependencies: Set<RequiredKotlinJsDependency> = emptySet()
override fun getPath(): String = "${kotlinJsTest.path}:kotlinTestFrameworkStub"
}
@@ -19,14 +19,10 @@ import org.jetbrains.kotlin.gradle.internal.operation
import org.jetbrains.kotlin.gradle.internal.processLogMessage
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor.Companion.TC_PROJECT_PROPERTY
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.NpmPackageVersion
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.appendConfigsFromDir
import org.jetbrains.kotlin.gradle.targets.js.*
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.jsQuoted
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.*
@@ -36,7 +32,6 @@ import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Compan
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
import org.jetbrains.kotlin.gradle.testing.internal.reportsDir
import org.jetbrains.kotlin.gradle.utils.appendLine
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import org.jetbrains.kotlin.gradle.utils.property
import org.slf4j.Logger
import java.io.File
@@ -66,13 +61,6 @@ class KotlinKarma(
private var configDirectory: File by property {
defaultConfigDirectory
}
private val isTeamCity by lazy {
if (isConfigurationCacheAvailable(project.gradle)) {
project.providers.gradleProperty(TC_PROJECT_PROPERTY).forUseAtConfigurationTime().isPresent
} else {
project.hasProperty(TC_PROJECT_PROPERTY)
}
}
override val requiredNpmDependencies: Set<RequiredKotlinJsDependency>
get() = requiredDependencies + webpackConfig.getRequiredDependencies(versions)
@@ -392,7 +380,7 @@ class KotlinKarma(
prependSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true,
escapeTCMessagesInLog = isTeamCity
escapeTCMessagesInLog = project.isTeamCity
)
config.basePath = npmProject.nodeModulesDir.absolutePath
@@ -9,7 +9,6 @@ import org.gradle.api.Project
import org.gradle.process.ProcessForkOptions
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesClientSettings
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor.Companion.TC_PROJECT_PROPERTY
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
@@ -20,8 +19,8 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinTestRunnerCliArgs
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import java.io.File
import org.jetbrains.kotlin.gradle.targets.js.isTeamCity
class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, private val basePath: String) :
KotlinJsTestFramework {
@@ -29,13 +28,6 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv
private val project: Project = compilation.target.project
private val npmProject = compilation.npmProject
private val versions = NodeJsRootPlugin.apply(project.rootProject).versions
private val isTeamCity by lazy {
if (isConfigurationCacheAvailable(project.gradle)) {
project.providers.gradleProperty(TC_PROJECT_PROPERTY).forUseAtConfigurationTime().isPresent
} else {
project.hasProperty(TC_PROJECT_PROPERTY)
}
}
override val settingsState: String
get() = "mocha"
@@ -67,7 +59,7 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv
prependSuiteName = true,
stackTraceParser = ::parseNodeJsStackTraceAsJvm,
ignoreOutOfRootNodes = true,
escapeTCMessagesInLog = isTeamCity
escapeTCMessagesInLog = project.isTeamCity
)
val cliArgs = KotlinTestRunnerCliArgs(
@@ -80,7 +72,6 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv
val file = task.inputFileProperty.get().asFile.toString()
val adapter = createAdapterJs(file, "kotlin-test-nodejs-runner", ADAPTER_NODEJS)
val args = mutableListOf(
"--require",
npmProject.require("source-map-support/register.js")
@@ -7,8 +7,11 @@ package org.jetbrains.kotlin.gradle.targets.js
import org.gradle.internal.hash.FileHasher
import org.gradle.internal.hash.Hashing.defaultFunction
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutor
import org.jetbrains.kotlin.gradle.utils.appendLine
import org.jetbrains.kotlin.gradle.utils.isConfigurationCacheAvailable
import java.io.File
import org.gradle.api.Project
fun Appendable.appendConfigsFromDir(confDir: File) {
val files = confDir.listFiles() ?: return
@@ -58,3 +61,10 @@ fun FileHasher.calculateDirHash(
const val JS = "js"
const val JS_MAP = "js.map"
const val META_JS = "meta.js"
val Project.isTeamCity: Boolean
get() = if (isConfigurationCacheAvailable(project.gradle)) {
project.providers.gradleProperty(TCServiceMessagesTestExecutor.TC_PROJECT_PROPERTY).forUseAtConfigurationTime().isPresent
} else {
project.hasProperty(TCServiceMessagesTestExecutor.TC_PROJECT_PROPERTY)
}