From 7c7aa98875b0bcb4fd44891f9d030d3de1f712be Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Fri, 7 Jul 2023 13:50:04 +0000 Subject: [PATCH] [K/JS] Throw error when a module contains file with the same name and package (ignoring case) --- .../irToJs/ModuleFragmentToExternalName.kt | 17 ++++++++++++++- .../kotlin/gradle/Kotlin2JsGradlePluginIT.kt | 21 +++++++++++++++++++ .../build.gradle.kts | 16 ++++++++++++++ .../custom/main.txt | 0 .../gradle.properties | 8 +++++++ .../src/main/kotlin/base1/Base.kt | 16 ++++++++++++++ .../src/main/kotlin/base2/base.kt | 9 ++++++++ .../gradle/targets/js/ir/CompilerFlags.kt | 1 + .../gradle/targets/js/ir/OutputGranularity.kt | 10 ++++++--- 9 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt index a3c58afa14b..575add95c32 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt @@ -15,9 +15,24 @@ import org.jetbrains.kotlin.ir.declarations.path private const val EXPORTER_FILE_POSTFIX = ".export" class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map) { + private val externalNameToItsFile = hashMapOf() + fun getExternalNameFor(file: IrFile, granularity: JsGenerationGranularity): String { assert(granularity == JsGenerationGranularity.PER_FILE) { "This method should be used only for PER_FILE granularity" } - return file.module.getJsOutputName().getExternalModuleNameForPerFile(file) + return file.module.getJsOutputName().getExternalModuleNameForPerFile(file).also { + val alreadyReservedBy = externalNameToItsFile.putIfAbsent(it.lowercase(), file) + + if (alreadyReservedBy != null && alreadyReservedBy != file) { + error( + """ + |There are two files in module '${file.module.name}' that have the similar package and file names. + | - Package "${file.packageFqName.asString()}" and path "${file.path}" + | - Package "${alreadyReservedBy.packageFqName.asString()}" and path "${alreadyReservedBy.path}" + |Note, that if the difference is only in letter cases, it also could lead to a clash of the compiled artifacts + """.trimMargin() + ) + } + } } fun getExternalNameForExporterFile(file: IrFile, granularity: JsGenerationGranularity): String { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 9e5252999c6..f9dd9cc23c6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -359,6 +359,27 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { } } + @DisplayName("per-file with the declarations validation") + @GradleTest + fun testPerFileProjectWithResultFilesClash(gradleVersion: GradleVersion) { + project("kotlin-js-invalid-per-file-project", gradleVersion) { + buildAndFail("compileDevelopmentExecutableKotlinJs") { + assertTasksFailed(":compileDevelopmentExecutableKotlinJs") + assertOutputContains(""" + |There are two files in module '' that have the similar package and file names. + | - Package "com.example" and + """.trimMargin()) + } + buildAndFail("compileProductionExecutableKotlinJs") { + assertTasksFailed(":compileProductionExecutableKotlinJs") + assertOutputContains(""" + |There are two files in module '' that have the similar package and file names. + | - Package "com.example" and path + """.trimMargin()) + } + } + } + @DisplayName("fully qualified names can be used in the sourcemap") @GradleTest fun testKotlinJsSourceMapGenerateFqNames(gradleVersion: GradleVersion) { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts new file mode 100644 index 00000000000..26eb11c7cee --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("js") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + js(IR) { + useEsModules() + binaries.executable() + nodejs() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties new file mode 100644 index 00000000000..abb12f59146 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties @@ -0,0 +1,8 @@ +kotlin.tests.individualTaskReports=true +kotlin.js.ir.output.granularity=per-file +kotlin.incremental=false +kotlin.incremental.js=false +kotlin.incremental.js.ir=false +kotlin.incremental.js.klib=false +kotlin.incremental.multiplatform=false +kotlin.incremental.useClasspathSnapshot=false \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt new file mode 100644 index 00000000000..2d3a770d00f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt @@ -0,0 +1,16 @@ +/* + * 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 + +@JsExport +fun best(): Int { + return 42 +} + +@JsExport +fun simpleBest(): Int { + return 73 +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt new file mode 100644 index 00000000000..d6904f24f79 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt @@ -0,0 +1,9 @@ +/* + * 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 + +@JsExport +public val foo = 44 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt index cea307e856c..2c6172db31a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt @@ -24,6 +24,7 @@ internal const val MINIMIZED_MEMBER_NAMES = "-Xir-minimized-member-names" internal const val KLIB_MODULE_NAME = "-Xir-module-name" +internal const val PER_FILE = "-Xir-per-file" internal const val PER_MODULE = "-Xir-per-module" internal const val PER_MODULE_OUTPUT_NAME = "-Xir-per-module-output-name" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt index a26d054ee1d..937daab7b7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt @@ -7,7 +7,8 @@ package org.jetbrains.kotlin.gradle.targets.js.ir enum class KotlinJsIrOutputGranularity { WHOLE_PROGRAM, - PER_MODULE; + PER_MODULE, + PER_FILE; companion object { fun byArgument(argument: String): KotlinJsIrOutputGranularity? = @@ -17,6 +18,9 @@ enum class KotlinJsIrOutputGranularity { } fun KotlinJsIrOutputGranularity.toCompilerArgument(): String { - val perModule = this == KotlinJsIrOutputGranularity.PER_MODULE - return "$PER_MODULE=$perModule" + return when (this) { + KotlinJsIrOutputGranularity.PER_FILE -> PER_FILE + KotlinJsIrOutputGranularity.PER_MODULE -> PER_MODULE + KotlinJsIrOutputGranularity.WHOLE_PROGRAM -> "" + } } \ No newline at end of file