Gradle, tests: fix type in "prepend"

This commit is contained in:
Sergey Rostov
2019-05-17 10:00:51 +03:00
parent 68cb28d76f
commit 64f997daa4
11 changed files with 25 additions and 279 deletions
@@ -21,7 +21,7 @@ import java.text.ParseException
data class TCServiceMessagesClientSettings(
val rootNodeName: String,
val testNameSuffix: String? = null,
val prepandSuiteName: Boolean = false,
val prependSuiteName: Boolean = false,
val treatFailedTestOutputAsStacktrace: Boolean = false,
val stackTraceParser: (String) -> ParsedStackTrace? = { null },
val ignoreOutOfRootNodes: Boolean = false
@@ -96,7 +96,7 @@ internal open class TCServiceMessagesClient(
parent.requireReportingNode()
val finalTestName = testName.let {
if (settings.prepandSuiteName) "${parent.fullNameWithoutRoot}.$it"
if (settings.prependSuiteName) "${parent.fullNameWithoutRoot}.$it"
else it
}
@@ -6,22 +6,25 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.*
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.KotlinTargetPreset
abstract class KotlinOnlyTargetPreset<T : KotlinCompilation<*>>(
abstract class KotlinOnlyTargetPreset<R : KotlinOnlyTarget<T>, T : KotlinCompilation<*>>(
protected val project: Project,
protected val kotlinPluginVersion: String
) : KotlinTargetPreset<KotlinOnlyTarget<T>> {
) : KotlinTargetPreset<R> {
protected abstract fun createKotlinTargetConfigurator(): KotlinTargetConfigurator<T>
protected open fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<T>): String? =
target.targetName
protected open fun createKotlinOnlyTarget() = KotlinOnlyTarget<T>(project, platformType)
abstract protected fun instantiateTarget(): R
override fun createTarget(name: String): KotlinOnlyTarget<T> {
val result = createKotlinOnlyTarget().apply {
override fun createTarget(name: String): R {
val result = instantiateTarget().apply {
targetName = name
disambiguationClassifier = provideTargetDisambiguationClassifier(this@apply)
preset = this@KotlinOnlyTargetPreset
@@ -16,14 +16,14 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator
open class KotlinJsTargetPreset(
project: Project,
kotlinPluginVersion: String
) : KotlinOnlyTargetPreset<KotlinJsCompilation>(
) : KotlinOnlyTargetPreset<KotlinJsTarget, KotlinJsCompilation>(
project,
kotlinPluginVersion
) {
override val platformType: KotlinPlatformType
get() = KotlinPlatformType.js
override fun createKotlinOnlyTarget() = KotlinJsTarget(project, platformType)
override fun instantiateTarget() = KotlinJsTarget(project, platformType)
override fun createKotlinTargetConfigurator() = KotlinJsTargetConfigurator(kotlinPluginVersion)
@@ -46,6 +46,7 @@ fun parseNodeJsStackTrace(stackTrace: String): NodeJsStackTrace {
var firstLines = true
val stack = mutableListOf<NodeJsStackTraceElement>()
// see examples at NodeJsStackTraceParserKtTest
stackTrace.lines().forEach {
val srcLine = it.trim()
@@ -6,7 +6,6 @@ internal data class NodeJsEnv(
val nodeDir: File,
val nodeBinDir: File,
val nodeExecutable: String,
val npmExecutable: String,
val platformName: String,
val architectureName: String,
@@ -2,7 +2,7 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs
/**
* Provides platform and architecture names that is used to download NodeJs.
* See [NodeJsEnv.ivyDependency] that is filled in [NodeJsRootExtension.buildEnv].
* See [NodeJsEnv.ivyDependency] that is filled in [NodeJsRootExtension.environment].
*/
internal object NodeJsPlatform {
private val props = System.getProperties()
@@ -11,8 +11,8 @@ import java.io.File
import java.net.URI
open class NodeJsSetupTask : DefaultTask() {
private val settings = NodeJsRootExtension[project]
private val env by lazy { settings.buildEnv() }
private val settings = project.nodeJs.root
private val env by lazy { settings.environment }
init {
group = NodeJsRootExtension.NODE_JS
@@ -55,7 +55,7 @@ open class NodeJsSetupTask : DefaultTask() {
}
}
val dep = this.project.dependencies.create(env.ivyDependency)
val dep = this.project.dependencies.create(ivyDependency)
val conf = this.project.configurations.detachedConfiguration(dep)
conf.isTransitive = false
val result = conf.resolve().single()
@@ -1,246 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.npm
import com.google.gson.GsonBuilder
import com.google.gson.JsonParseException
import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.internal.hash.FileHasher
import org.jetbrains.kotlin.daemon.common.toHexString
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesSync.*
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout.Companion.PACKAGE_JSON
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import java.io.File
/**
* Incrementally sync gradle JS dependencies into node_modules directory.
*
* Working by traversing giving classpath.
* For each classpath element (called [Element]) `.js` and `.js.map` files are stored into `node_modules`.
*
* To do it incrementally, state of previously traversed classpath elements are stored in
* `node_modules/.from-gradle` file. Unchanged elements will not been visited.
*
* See [Element] and [getOrCompute] for more details.
*/
internal class GradleNodeModulesSync(val project: Project) {
companion object {
const val STATE_FILE_NAME = ".from-gradle"
}
private val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
private val npmProjectLayout = NpmProjectLayout[project]
private val nodeModulesDir = npmProjectLayout.nodeModulesDir
private val gson = GsonBuilder().setPrettyPrinting().create()
private class State {
val contents: MutableMap<String, Element> = mutableMapOf()
operator fun get(elementHash: ByteArray) =
contents[elementHash.toHexString()]
operator fun set(elementHash: ByteArray, element: Element) {
contents[elementHash.toHexString()] = element
}
}
/**
* Traversed element from classpath.
* Stored in state file.
*
* When classpath traversed all elements are in one of this states:
* - unchanged: was loaded from previous state and contents not changed.
* only [fileNames] are known and contents of element not visited,
* but visitor are saved in [_fillFiles] to load in next case
* - unchanged but missed in node_modules: some of [fileNames] missed in node_modules
* in this case [_fillFiles] called before [files] was getted for copy spec
* - unchanged and existed in node_modules: nothing will be done in this case
* - new: not existed in previous state. In this case element is visited,
* both [fileNames] and [_files] are filled already
* - deleted: was loaded from previous state, but not existed in current state
* only [fileNames] are known for this case, which is used to remove outdated files
* - changed: this is just combination of deleted and new state
*/
private class Element {
lateinit var source: String
val fileNames: MutableList<String> = mutableListOf()
@Transient
@Suppress("PropertyName")
var _files: List<File>? = null
@Transient
@Suppress("PropertyName")
var _fillFiles: (() -> List<File>)? = null
val files: List<File>
get() {
if (_files == null) {
_files = _fillFiles!!()
}
return _files!!
}
}
/**
* Get info of classpath element from previous state (with storing visitor for case when files are deleted in node_modules)
* or compute it now.
*
* @param file classpath element
* @param buildElement js files list provider
*/
private fun getOrCompute(file: File, buildElement: () -> List<File>) {
val hash = hasher.hash(file).toByteArray()
val old = old[hash]
if (old != null) old._fillFiles = buildElement
new[hash] = old ?: Element().also {
it.source = file.absolutePath
it._files = buildElement()
it._files!!.mapTo(it.fileNames) { it.name }
}
}
private lateinit var old: State
private val new = State()
private val stateFile = nodeModulesDir.resolve(STATE_FILE_NAME)
fun loadOldState() {
val state: State? = if (stateFile.isFile) try {
stateFile.reader().use {
gson.fromJson(it, State::class.java)
}
} catch (e: JsonParseException) {
project.logger.warn("Cannot load previous state of Gradle depencies stored in node_modules", e)
null
} else {
if (stateFile.isDirectory) stateFile.deleteRecursively()
null
}
old = state ?: State()
}
fun sync() {
val children = nodeModulesDir.list()?.toSet() ?: setOf()
val deleted = old.contents.keys.toMutableSet()
val absented = mutableSetOf<String>()
new.contents.forEach { (key, value) ->
deleted.remove(key)
if (key !in old.contents.keys) absented.add(key)
else if (value.fileNames.any { it !in children }) absented.add(key)
}
deleted.forEach {
old.contents[it]!!.fileNames.forEach { fileName ->
nodeModulesDir.resolve(fileName).delete()
}
}
if (absented.isNotEmpty()) {
project.copy { copy ->
absented.forEach { key ->
copy.from(new.contents[key]!!.files)
}
copy.into(nodeModulesDir)
}
}
if (deleted.isNotEmpty() || absented.isNotEmpty()) {
saveNewState()
}
}
private fun saveNewState() {
stateFile.writer().use {
gson.toJson(new, it)
}
}
class TransitiveNpmDependency(
val key: String,
val version: String
)
fun visitCompilation(
compilation: KotlinCompilation<KotlinCommonOptions>,
project: Project,
transitiveDependencies: MutableList<TransitiveNpmDependency>
): List<TransitiveNpmDependency> {
val kotlin2JsCompile = compilation.compileKotlinTask as Kotlin2JsCompile
// classpath
compilation.relatedConfigurationNames.forEach {
val configuration = project.configurations.getByName(it)
if (configuration.isCanBeResolved) {
configuration.resolve().forEach { srcFile ->
when {
srcFile.name == PACKAGE_JSON -> visitPackageJson(srcFile, transitiveDependencies)
isKotlinJsRuntimeFile(srcFile) -> getOrCompute(srcFile) {
listOf(srcFile)
}
srcFile.isZip -> getOrCompute(srcFile) {
mutableListOf<File>().also { files ->
this.project.zipTree(srcFile).forEach { innerFile ->
when {
innerFile.name == PACKAGE_JSON -> visitPackageJson(innerFile, transitiveDependencies)
isKotlinJsRuntimeFile(innerFile) -> files.add(innerFile)
}
}
}
}
}
}
}
}
// output
if (kotlin2JsCompile.state.executed) {
copyCompileOutput(project, kotlin2JsCompile)
} else {
kotlin2JsCompile.doLast {
copyCompileOutput(project, kotlin2JsCompile)
}
}
return transitiveDependencies
}
private fun copyCompileOutput(project: Project, kotlin2JsCompile: Kotlin2JsCompile) {
project.copy { copy ->
copy.from(kotlin2JsCompile.outputFile)
copy.from(kotlin2JsCompile.outputFile.path + ".map")
copy.into(nodeModulesDir)
}
}
private fun visitPackageJson(
innerFile: File,
transitiveDependencies: MutableList<TransitiveNpmDependency>
) {
val packageJson = innerFile.reader().use {
gson.fromJson(it, PackageJson::class.java)
}
packageJson.dependencies.forEach { (key, version) ->
transitiveDependencies.add(TransitiveNpmDependency(key, version))
}
}
private val File.isZip
get() = isFile && (name.endsWith(".jar") || name.endsWith(".zip"))
private fun isKotlinJsRuntimeFile(file: File): Boolean {
if (!file.isFile) return false
val name = file.name
return (name.endsWith(".js") && !name.endsWith(".meta.js"))
|| name.endsWith(".js.map")
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.gradle.targets.js.yarn
import com.google.gson.stream.JsonReader
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
import java.io.File
import java.io.StringReader
@@ -16,23 +17,11 @@ data class YarnLock(val entries: List<Entry>) {
val resolved: String?,
val integrity: String?,
val dependencies: List<Dependency>
) {
val packageKey = key.substringBeforeLast("@")
}
)
data class Dependency(val key: String, val version: String?) {
val group: String?
val packageName: String
init {
if (key.contains("/")) {
group = key.substringBeforeLast("/").removePrefix("@")
packageName = key.substringAfterLast("/")
} else {
group = null
packageName = key
}
}
val scopedName: PackageJson.ScopedName
get() = PackageJson.scopedName(key)
}
companion object {
@@ -14,8 +14,8 @@ import java.io.File
import javax.xml.ws.Action
open class YarnSetupTask : DefaultTask() {
private val settings = YarnRootExtension[project]
private val env by lazy { settings.buildEnv() }
private val settings = project.yarn
private val env by lazy { settings.environment }
@Suppress("MemberVisibilityCanBePrivate")
val downloadUrl
@@ -48,7 +48,7 @@ open class KotlinNativeTest : KotlinTest() {
val clientSettings = TCServiceMessagesClientSettings(
name,
testNameSuffix = targetName,
prepandSuiteName = targetName != null,
prependSuiteName = targetName != null,
treatFailedTestOutputAsStacktrace = true,
stackTraceParser = ::parseKotlinNativeStackTraceAsJvm
)