[Gradle, JS] Reporting in case when yarn.lock was changed during build
---- [Gradle, JS] Change naming [Gradle, JS] storeYarnLock not finalizing kotlinNpmInstall [Gradle, JS] Add double clean to test for check correctness without input yarn.lock [Gradle, JS] Consider reportNewYarnLock in yarn.lock service [Gradle, JS] actualizeYarnLock for explicit actualizing yarn.lock [Gradle, JS] Not update yarn.lock automatically [Gradle, JS] Add test on yarn.lock reporting [Gradle, JS] Error report by default [Gradle, JS] Report with failing after build, not failing task [Gradle, JS] Report about yarn.lock mismatch reporting Merge-request: KT-MR-6614 Merged-by: Ilya Goncharov <Ilya.Goncharov@jetbrains.com> ^KT-52950 fixed
This commit is contained in:
+203
-2
@@ -17,6 +17,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
|||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectModules
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectModules
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
|
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.fromSrcPackageJson
|
import org.jetbrains.kotlin.gradle.targets.js.npm.fromSrcPackageJson
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YARN_LOCK_MISMATCH_MESSAGE
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||||
import org.jetbrains.kotlin.gradle.tasks.USING_JS_INCREMENTAL_COMPILATION_MESSAGE
|
import org.jetbrains.kotlin.gradle.tasks.USING_JS_INCREMENTAL_COMPILATION_MESSAGE
|
||||||
import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE
|
import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE
|
||||||
import org.jetbrains.kotlin.gradle.testbase.*
|
import org.jetbrains.kotlin.gradle.testbase.*
|
||||||
@@ -25,7 +28,6 @@ import org.jetbrains.kotlin.gradle.util.normalizePath
|
|||||||
import org.junit.jupiter.api.Disabled
|
import org.junit.jupiter.api.Disabled
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.condition.DisabledIf
|
import org.junit.jupiter.api.condition.DisabledIf
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
@@ -33,7 +35,6 @@ import kotlin.io.path.*
|
|||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertNotEquals
|
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@JsGradlePluginTests
|
@JsGradlePluginTests
|
||||||
@@ -867,6 +868,10 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build(UPGRADE_YARN_LOCK) {
|
||||||
|
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||||
|
}
|
||||||
|
|
||||||
build("jsJar") {
|
build("jsJar") {
|
||||||
val archive = Files.list(projectPath.resolve("build").resolve("libs")).use { files ->
|
val archive = Files.list(projectPath.resolve("build").resolve("libs")).use { files ->
|
||||||
files
|
files
|
||||||
@@ -1178,6 +1183,202 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DisplayName("Failing with yarn.lock update")
|
||||||
|
@GradleTest
|
||||||
|
fun testFailingWithYarnLockUpdate(gradleVersion: GradleVersion) {
|
||||||
|
project("kotlin-js-yarn-lock-project", gradleVersion) {
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
it + "\n" +
|
||||||
|
"""
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock = true
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
it + "\n" +
|
||||||
|
"""
|
||||||
|
dependencies {
|
||||||
|
implementation(npm("decamelize", "6.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
// yarn.lock was not updated
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
build(UPGRADE_YARN_LOCK) {
|
||||||
|
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||||
|
replaced + "\n" +
|
||||||
|
"""
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.WARNING
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
build(UPGRADE_YARN_LOCK) {
|
||||||
|
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
it + "\n" +
|
||||||
|
"""
|
||||||
|
dependencies {
|
||||||
|
implementation(npm("decamelize", "6.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
build(UPGRADE_YARN_LOCK) {
|
||||||
|
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||||
|
replaced + "\n" +
|
||||||
|
"""
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL_AFTER_BUILD
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksUpToDate(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
it + "\n" +
|
||||||
|
"""
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock =
|
||||||
|
true
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
it + "\n" +
|
||||||
|
"""
|
||||||
|
dependencies {
|
||||||
|
implementation(npm("decamelize", "6.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockAutoReplace =
|
||||||
|
true
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
//yarn.lock was updated
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildGradleKts.modify {
|
||||||
|
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||||
|
replaced + "\n" +
|
||||||
|
"""
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||||
|
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||||
|
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL_AFTER_BUILD
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildAndFail("compileKotlinJs") {
|
||||||
|
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||||
|
|
||||||
|
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
//yarn.lock was updated
|
||||||
|
build("compileKotlinJs") {
|
||||||
|
assertTasksUpToDate(":$STORE_YARN_LOCK_NAME")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if everything ok without build/js/yarn.lock
|
||||||
|
build("clean") {
|
||||||
|
assertTasksExecuted(":clean")
|
||||||
|
}
|
||||||
|
|
||||||
|
build("clean") {
|
||||||
|
assertTasksUpToDate(":clean")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DisplayName("webpack configuration is valid")
|
@DisplayName("webpack configuration is valid")
|
||||||
@GradleTest
|
@GradleTest
|
||||||
fun testWebpackConfig(gradleVersion: GradleVersion) {
|
fun testWebpackConfig(gradleVersion: GradleVersion) {
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
plugins {
|
||||||
|
id("org.jetbrains.kotlin.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-js")
|
||||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test-js")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
js {
|
||||||
|
useCommonJs()
|
||||||
|
binaries.executable()
|
||||||
|
nodejs {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
kotlin.tests.individualTaskReports=true
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.example
|
||||||
|
|
||||||
|
fun best(): Int {
|
||||||
|
return 42
|
||||||
|
}
|
||||||
|
|
||||||
|
fun simpleBest(): Int {
|
||||||
|
return 73
|
||||||
|
}
|
||||||
+10
-2
@@ -52,7 +52,11 @@ internal class DukatCompilationResolverPlugin(
|
|||||||
it.group = DUKAT_TASK_GROUP
|
it.group = DUKAT_TASK_GROUP
|
||||||
it.description = "Integrated generation Kotlin/JS external declarations for .d.ts files in $compilation"
|
it.description = "Integrated generation Kotlin/JS external declarations for .d.ts files in $compilation"
|
||||||
it.externalsOutputFormat = externalsOutputFormat
|
it.externalsOutputFormat = externalsOutputFormat
|
||||||
it.dependsOn(nodeJs_.npmInstallTaskProvider, npmProject.packageJsonTask)
|
it.dependsOn(
|
||||||
|
nodeJs_.npmInstallTaskProvider,
|
||||||
|
nodeJs_.storeYarnLockTaskProvider,
|
||||||
|
npmProject.packageJsonTask
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +86,11 @@ internal class DukatCompilationResolverPlugin(
|
|||||||
) {
|
) {
|
||||||
it.group = DUKAT_TASK_GROUP
|
it.group = DUKAT_TASK_GROUP
|
||||||
it.description = "Generate Kotlin/JS external declarations for .d.ts files of all NPM dependencies in ${compilation}"
|
it.description = "Generate Kotlin/JS external declarations for .d.ts files of all NPM dependencies in ${compilation}"
|
||||||
it.dependsOn(nodeJs_.npmInstallTaskProvider, npmProject.packageJsonTask)
|
it.dependsOn(
|
||||||
|
nodeJs_.npmInstallTaskProvider,
|
||||||
|
nodeJs_.storeYarnLockTaskProvider,
|
||||||
|
npmProject.packageJsonTask
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -47,7 +47,11 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
|||||||
get() = "Run all ${target.name} tests inside browser using karma and webpack"
|
get() = "Run all ${target.name} tests inside browser using karma and webpack"
|
||||||
|
|
||||||
override fun configureTestDependencies(test: KotlinJsTest) {
|
override fun configureTestDependencies(test: KotlinJsTest) {
|
||||||
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
|
test.dependsOn(
|
||||||
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
|
nodeJs.nodeJsSetupTaskProvider
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
||||||
@@ -256,6 +260,7 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
|||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
nodeJs.npmInstallTaskProvider,
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
target.project.tasks.named(compilation.processResourcesTaskName)
|
target.project.tasks.named(compilation.processResourcesTaskName)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -47,7 +47,11 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun configureTestDependencies(test: KotlinJsTest) {
|
override fun configureTestDependencies(test: KotlinJsTest) {
|
||||||
test.dependsOn(nodeJs.npmInstallTaskProvider, nodeJs.nodeJsSetupTaskProvider)
|
test.dependsOn(
|
||||||
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
|
nodeJs.nodeJsSetupTaskProvider
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
||||||
|
|||||||
+4
-1
@@ -100,7 +100,10 @@ constructor(
|
|||||||
it.nodeJs = nodeJs
|
it.nodeJs = nodeJs
|
||||||
it.executable = nodeJs.requireConfigured().nodeExecutable
|
it.executable = nodeJs.requireConfigured().nodeExecutable
|
||||||
it.workingDir = npmProject.dir
|
it.workingDir = npmProject.dir
|
||||||
it.dependsOn(nodeJs.npmInstallTaskProvider)
|
it.dependsOn(
|
||||||
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
|
)
|
||||||
it.dependsOn(compilation.compileKotlinTaskProvider)
|
it.dependsOn(compilation.compileKotlinTaskProvider)
|
||||||
if (compilation.platformType == KotlinPlatformType.wasm) {
|
if (compilation.platformType == KotlinPlatformType.wasm) {
|
||||||
it.nodeArgs.addWasmExperimentalArguments()
|
it.nodeArgs.addWasmExperimentalArguments()
|
||||||
|
|||||||
+11
@@ -19,6 +19,9 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmCachesSetup
|
|||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.RESTORE_YARN_LOCK_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
@@ -73,6 +76,14 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat
|
|||||||
val npmCachesSetupTaskProvider: TaskProvider<out KotlinNpmCachesSetup>?
|
val npmCachesSetupTaskProvider: TaskProvider<out KotlinNpmCachesSetup>?
|
||||||
get() = rootProject?.tasks?.withType(KotlinNpmCachesSetup::class.java)?.named(KotlinNpmCachesSetup.NAME)
|
get() = rootProject?.tasks?.withType(KotlinNpmCachesSetup::class.java)?.named(KotlinNpmCachesSetup.NAME)
|
||||||
|
|
||||||
|
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY") // TODO: investigate this warning; fixing it breaks integration tests.
|
||||||
|
val storeYarnLockTaskProvider: TaskProvider<out YarnLockCopyTask>?
|
||||||
|
get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(STORE_YARN_LOCK_NAME)
|
||||||
|
|
||||||
|
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY") // TODO: investigate this warning; fixing it breaks integration tests.
|
||||||
|
val restoreYarnLockTaskProvider: TaskProvider<out YarnLockCopyTask>?
|
||||||
|
get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(RESTORE_YARN_LOCK_NAME)
|
||||||
|
|
||||||
val rootPackageDir: File by lazy {
|
val rootPackageDir: File by lazy {
|
||||||
rootProject.buildDir.resolve("js")
|
rootProject.buildDir.resolve("js")
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -80,7 +80,10 @@ internal class KotlinCompilationNpmResolver(
|
|||||||
npmProject.publicPackageJsonTaskName,
|
npmProject.publicPackageJsonTaskName,
|
||||||
listOf(compilation)
|
listOf(compilation)
|
||||||
) {
|
) {
|
||||||
it.dependsOn(nodeJs_.npmInstallTaskProvider)
|
it.dependsOn(
|
||||||
|
nodeJs_.npmInstallTaskProvider,
|
||||||
|
nodeJs_.storeYarnLockTaskProvider,
|
||||||
|
)
|
||||||
it.dependsOn(packageJsonTaskHolder)
|
it.dependsOn(packageJsonTaskHolder)
|
||||||
}.also { packageJsonTask ->
|
}.also { packageJsonTask ->
|
||||||
if (compilation.isMain()) {
|
if (compilation.isMain()) {
|
||||||
|
|||||||
+4
-1
@@ -68,7 +68,10 @@ internal class KotlinProjectNpmResolver(
|
|||||||
nodeJs.taskRequirements.addTaskRequirements(task)
|
nodeJs.taskRequirements.addTaskRequirements(task)
|
||||||
}
|
}
|
||||||
task.dependsOn(packageJsonTaskHolder)
|
task.dependsOn(packageJsonTaskHolder)
|
||||||
task.dependsOn(nodeJs.npmInstallTaskProvider)
|
task.dependsOn(
|
||||||
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -133,6 +133,7 @@ abstract class KotlinPackageJsonTask : DefaultTask() {
|
|||||||
nodeJs.rootPackageJsonTaskProvider!!.configure { it.mustRunAfter(packageJsonTask) }
|
nodeJs.rootPackageJsonTaskProvider!!.configure { it.mustRunAfter(packageJsonTask) }
|
||||||
|
|
||||||
compilation.compileKotlinTaskProvider.dependsOn(npmInstallTask!!)
|
compilation.compileKotlinTaskProvider.dependsOn(npmInstallTask!!)
|
||||||
|
compilation.compileKotlinTaskProvider.dependsOn(nodeJs.storeYarnLockTaskProvider!!)
|
||||||
compilation.compileKotlinTaskProvider.dependsOn(packageJsonTask)
|
compilation.compileKotlinTaskProvider.dependsOn(packageJsonTask)
|
||||||
|
|
||||||
return packageJsonTask
|
return packageJsonTask
|
||||||
|
|||||||
+1
@@ -278,6 +278,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
|
|||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
nodeJs.npmInstallTaskProvider,
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
target.project.tasks.named(compilation.processResourcesTaskName)
|
target.project.tasks.named(compilation.processResourcesTaskName)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -106,7 +106,12 @@ abstract class KotlinJsSubTarget(
|
|||||||
val compileOutputFile = compileTask.flatMap { it.outputFileProperty }
|
val compileOutputFile = compileTask.flatMap { it.outputFileProperty }
|
||||||
testJs.inputFileProperty.fileProvider(compileOutputFile)
|
testJs.inputFileProperty.fileProvider(compileOutputFile)
|
||||||
|
|
||||||
testJs.dependsOn(nodeJs.npmInstallTaskProvider, compileTask, nodeJs.nodeJsSetupTaskProvider)
|
testJs.dependsOn(
|
||||||
|
nodeJs.npmInstallTaskProvider,
|
||||||
|
nodeJs.storeYarnLockTaskProvider,
|
||||||
|
compileTask,
|
||||||
|
nodeJs.nodeJsSetupTaskProvider
|
||||||
|
)
|
||||||
|
|
||||||
testJs.onlyIf {
|
testJs.onlyIf {
|
||||||
compileOutputFile.get().exists()
|
compileOutputFile.get().exists()
|
||||||
|
|||||||
+3
@@ -17,4 +17,7 @@ data class YarnEnv(
|
|||||||
val ivyDependency: String,
|
val ivyDependency: String,
|
||||||
val standalone: Boolean,
|
val standalone: Boolean,
|
||||||
val ignoreScripts: Boolean,
|
val ignoreScripts: Boolean,
|
||||||
|
val yarnLockMismatchReport: YarnLockMismatchReport,
|
||||||
|
val reportNewYarnLock: Boolean,
|
||||||
|
val yarnLockAutoReplace: Boolean,
|
||||||
) : Serializable
|
) : Serializable
|
||||||
|
|||||||
+59
-1
@@ -6,15 +6,18 @@
|
|||||||
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
abstract class YarnLockCopyTask : DefaultTask() {
|
abstract class YarnLockCopyTask : DefaultTask() {
|
||||||
|
|
||||||
@get:InputFile
|
@get:InputFile
|
||||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||||
abstract val inputFile: RegularFileProperty
|
abstract val inputFile: RegularFileProperty
|
||||||
@@ -35,11 +38,66 @@ abstract class YarnLockCopyTask : DefaultTask() {
|
|||||||
abstract val fs: FileSystemOperations
|
abstract val fs: FileSystemOperations
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun copy() {
|
open fun copy() {
|
||||||
fs.copy { copy ->
|
fs.copy { copy ->
|
||||||
copy.from(inputFile)
|
copy.from(inputFile)
|
||||||
copy.rename { fileName.get() }
|
copy.rename { fileName.get() }
|
||||||
copy.into(outputDirectory)
|
copy.into(outputDirectory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val STORE_YARN_LOCK_NAME = "kotlinStoreYarnLock"
|
||||||
|
val RESTORE_YARN_LOCK_NAME = "kotlinRestoreYarnLock"
|
||||||
|
val UPGRADE_YARN_LOCK = "kotlinUpgradeYarnLock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class YarnLockStoreTask : YarnLockCopyTask() {
|
||||||
|
@get:Internal
|
||||||
|
abstract val yarnLockMismatchReportService: Property<YarnLockMismatchReportService>
|
||||||
|
|
||||||
|
@Input
|
||||||
|
lateinit var yarnLockMismatchReport: Provider<YarnLockMismatchReport>
|
||||||
|
|
||||||
|
@Input
|
||||||
|
lateinit var reportNewYarnLock: Provider<Boolean>
|
||||||
|
|
||||||
|
@Input
|
||||||
|
lateinit var yarnLockAutoReplace: Provider<Boolean>
|
||||||
|
|
||||||
|
override fun copy() {
|
||||||
|
val outputFile = outputDirectory.get().asFile.resolve(fileName.get())
|
||||||
|
|
||||||
|
val shouldReportMismatch = if (!outputFile.exists()) {
|
||||||
|
reportNewYarnLock.get()
|
||||||
|
} else {
|
||||||
|
yarnLockMismatchReport.get() != YarnLockMismatchReport.NONE && !contentEquals(inputFile.get().asFile, outputFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outputFile.exists() || yarnLockAutoReplace.get()) {
|
||||||
|
super.copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldReportMismatch) {
|
||||||
|
when (yarnLockMismatchReport.get()) {
|
||||||
|
YarnLockMismatchReport.NONE -> {}
|
||||||
|
YarnLockMismatchReport.WARNING -> {
|
||||||
|
logger.warn(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
YarnLockMismatchReport.FAIL -> throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
YarnLockMismatchReport.FAIL_AFTER_BUILD -> {
|
||||||
|
yarnLockMismatchReportService.get().failOnClose()
|
||||||
|
}
|
||||||
|
else -> error("Unknown yarn.lock mismatch report kind")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class YarnLockMismatchReport {
|
||||||
|
NONE,
|
||||||
|
WARNING,
|
||||||
|
FAIL,
|
||||||
|
FAIL_AFTER_BUILD,
|
||||||
}
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.yarn
|
||||||
|
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.file.RegularFileProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.services.BuildService
|
||||||
|
import org.gradle.api.services.BuildServiceParameters
|
||||||
|
import org.gradle.tooling.events.FinishEvent
|
||||||
|
import org.gradle.tooling.events.OperationCompletionListener
|
||||||
|
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||||
|
|
||||||
|
abstract class YarnLockMismatchReportService : BuildService<YarnLockMismatchReportService.Params>, AutoCloseable,
|
||||||
|
OperationCompletionListener {
|
||||||
|
@Volatile
|
||||||
|
private var shouldFailOnClose: Boolean = false
|
||||||
|
|
||||||
|
// Some parameters for the web server
|
||||||
|
internal interface Params : BuildServiceParameters {
|
||||||
|
val inputFile: RegularFileProperty
|
||||||
|
val outputFile: RegularFileProperty
|
||||||
|
val shouldFailOnClose: Property<Boolean>
|
||||||
|
val reportNewYarnLock: Property<Boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun failOnClose() {
|
||||||
|
shouldFailOnClose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinish(event: FinishEvent?) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
if (!parameters.inputFile.get().asFile.exists()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.reportNewYarnLock.get() && !parameters.outputFile.get().asFile.exists()) {
|
||||||
|
throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFailOnClose || parameters.shouldFailOnClose.get() && !contentEquals(parameters.inputFile.get().asFile, parameters.outputFile.get().asFile)) {
|
||||||
|
throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val YARN_LOCK_MISMATCH_MESSAGE = "yarn.lock was changed. Run `kotlinActualizeYarnLock` task to actualize yarn.lock file"
|
||||||
+38
-7
@@ -9,12 +9,16 @@ import org.gradle.api.Plugin
|
|||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.plugins.BasePlugin
|
import org.gradle.api.plugins.BasePlugin
|
||||||
import org.gradle.api.tasks.TaskProvider
|
import org.gradle.api.tasks.TaskProvider
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.BuildEventsListenerRegistryHolder
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
|
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
|
import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
|
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.RESTORE_YARN_LOCK_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||||
|
|
||||||
@@ -69,14 +73,42 @@ open class YarnPlugin : Plugin<Project> {
|
|||||||
it.dependsOn(packageJsonUmbrella)
|
it.dependsOn(packageJsonUmbrella)
|
||||||
}
|
}
|
||||||
|
|
||||||
val storeYarnLock = tasks.register("kotlinStoreYarnLock", YarnLockCopyTask::class.java) {
|
val service = project.gradle.sharedServices
|
||||||
it.dependsOn(kotlinNpmInstall)
|
.registerIfAbsent("kotlin-store-yarn-lock-mismatch-reporter", YarnLockMismatchReportService::class.java) {
|
||||||
it.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
it.parameters.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||||
it.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
it.parameters.outputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
||||||
it.fileName.set(yarnRootExtension.lockFileName)
|
it.parameters.shouldFailOnClose.set(
|
||||||
|
provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport == YarnLockMismatchReport.FAIL_AFTER_BUILD }
|
||||||
|
)
|
||||||
|
it.parameters.reportNewYarnLock.set(
|
||||||
|
provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildEventsListenerRegistryHolder.getInstance(project).listenerRegistry
|
||||||
|
.onTaskCompletion(service)
|
||||||
|
|
||||||
|
val storeYarnLock = tasks.register(STORE_YARN_LOCK_NAME, YarnLockStoreTask::class.java) { task ->
|
||||||
|
task.dependsOn(kotlinNpmInstall)
|
||||||
|
task.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||||
|
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||||
|
task.fileName.set(yarnRootExtension.lockFileName)
|
||||||
|
|
||||||
|
task.yarnLockMismatchReportService.set(service)
|
||||||
|
|
||||||
|
task.yarnLockMismatchReport = provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport }
|
||||||
|
task.reportNewYarnLock = provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||||
|
task.yarnLockAutoReplace = provider { yarnRootExtension.requireConfigured().yarnLockAutoReplace }
|
||||||
}
|
}
|
||||||
|
|
||||||
val restoreYarnLock = tasks.register("kotlinRestoreYarnLock", YarnLockCopyTask::class.java) {
|
tasks.register(UPGRADE_YARN_LOCK, YarnLockCopyTask::class.java) { task ->
|
||||||
|
task.dependsOn(kotlinNpmInstall)
|
||||||
|
task.inputFile.set(nodeJs.rootPackageDir.resolve("yarn.lock"))
|
||||||
|
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||||
|
task.fileName.set(yarnRootExtension.lockFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
val restoreYarnLock = tasks.register(RESTORE_YARN_LOCK_NAME, YarnLockCopyTask::class.java) {
|
||||||
val lockFile = yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName)
|
val lockFile = yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName)
|
||||||
it.inputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
it.inputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
||||||
it.outputDirectory.set(nodeJs.rootPackageDir)
|
it.outputDirectory.set(nodeJs.rootPackageDir)
|
||||||
@@ -88,7 +120,6 @@ open class YarnPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
kotlinNpmInstall.configure {
|
kotlinNpmInstall.configure {
|
||||||
it.dependsOn(restoreYarnLock)
|
it.dependsOn(restoreYarnLock)
|
||||||
it.finalizedBy(storeYarnLock)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
@@ -44,6 +44,12 @@ open class YarnRootExtension(
|
|||||||
|
|
||||||
var ignoreScripts by Property(true)
|
var ignoreScripts by Property(true)
|
||||||
|
|
||||||
|
var yarnLockMismatchReport: YarnLockMismatchReport by Property(YarnLockMismatchReport.FAIL)
|
||||||
|
|
||||||
|
var reportNewYarnLock: Boolean by Property(false)
|
||||||
|
|
||||||
|
var yarnLockAutoReplace: Boolean by Property(false)
|
||||||
|
|
||||||
val yarnSetupTaskProvider: TaskProvider<YarnSetupTask>
|
val yarnSetupTaskProvider: TaskProvider<YarnSetupTask>
|
||||||
get() = project.tasks
|
get() = project.tasks
|
||||||
.withType(YarnSetupTask::class.java)
|
.withType(YarnSetupTask::class.java)
|
||||||
@@ -107,6 +113,9 @@ open class YarnRootExtension(
|
|||||||
standalone = !download,
|
standalone = !download,
|
||||||
ivyDependency = "com.yarnpkg:yarn:$version@tar.gz",
|
ivyDependency = "com.yarnpkg:yarn:$version@tar.gz",
|
||||||
ignoreScripts = ignoreScripts,
|
ignoreScripts = ignoreScripts,
|
||||||
|
yarnLockMismatchReport = yarnLockMismatchReport,
|
||||||
|
reportNewYarnLock = reportNewYarnLock,
|
||||||
|
yarnLockAutoReplace = yarnLockAutoReplace,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
@@ -95,3 +95,23 @@ internal inline fun <T> withTemporaryDirectory(prefix: String, action: (director
|
|||||||
directory.deleteRecursively()
|
directory.deleteRecursively()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun contentEquals(file1: File, file2: File): Boolean {
|
||||||
|
file1.useLines { seq1 ->
|
||||||
|
file2.useLines { seq2 ->
|
||||||
|
val iterator1 = seq1.iterator()
|
||||||
|
val iterator2 = seq2.iterator()
|
||||||
|
|
||||||
|
while(iterator1.hasNext() == iterator2.hasNext()) {
|
||||||
|
|
||||||
|
if (!iterator1.hasNext()) return true
|
||||||
|
|
||||||
|
if (iterator1.next() != iterator2.next()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user