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 312c0551031..50b73a400da 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 @@ -127,6 +127,47 @@ class Kotlin2JsGradlePluginIT : AbstractKotlin2JsGradlePluginIT(false) { return super.defaultBuildOptions().copy(warningMode = WarningMode.Summary) } + @Test + fun testIncrementalCompilation() = Project("kotlin2JsICProject").run { + setupWorkingDir() + val modules = listOf("app", "lib") + val mainFiles = modules.flatMapTo(LinkedHashSet()) { + projectDir.resolve("$it/src/main").allKotlinFiles() + } + + build("build") { + assertSuccessful() + checkIrCompilationMessage() + assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) + if (irBackend) { + assertCompiledKotlinSources(project.relativize(mainFiles)) + } else { + assertCompiledKotlinSources(project.relativize(allKotlinFiles)) + } + } + + build("build") { + assertSuccessful() + assertCompiledKotlinSources(emptyList()) + } + + projectFile("A.kt").modify { + it.replace("val x = 0", "val x = \"a\"") + } + build("build") { + assertSuccessful() + checkIrCompilationMessage() + assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) + val affectedFiles = project.projectDir.getFilesByNames("A.kt", "useAInLibMain.kt", "useAInAppMain.kt", "useAInAppTest.kt") + if (irBackend) { + // only klib ic is supported for now, so tests are generated non-incrementally with ir backend + assertCompiledKotlinSources(project.relativize(affectedFiles.filter { it in mainFiles })) + } else { + assertCompiledKotlinSources(project.relativize(affectedFiles)) + } + } + } + @Test fun testKotlinJsBuiltins() { val project = Project("kotlinBuiltins") @@ -257,7 +298,7 @@ class Kotlin2JsGradlePluginIT : AbstractKotlin2JsGradlePluginIT(false) { } } -abstract class AbstractKotlin2JsGradlePluginIT(private val irBackend: Boolean) : BaseGradleIT() { +abstract class AbstractKotlin2JsGradlePluginIT(val irBackend: Boolean) : BaseGradleIT() { override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy( jsIrBackend = irBackend, @@ -284,10 +325,12 @@ abstract class AbstractKotlin2JsGradlePluginIT(private val irBackend: Boolean) : val jarPath = "build/libs/kotlin2JsNoOutputFileProject.jar" assertFileExists(jarPath) val jar = ZipFile(fileInWorkingDir(jarPath)) - assertEquals( - 1, jar.entries().asSequence().count { it.name == "kotlin2JsNoOutputFileProject.js" }, - "The jar should contain an entry `kotlin2JsNoOutputFileProject.js` with no duplicates" - ) + if (!irBackend) { + assertEquals( + 1, jar.entries().asSequence().count { it.name == "kotlin2JsNoOutputFileProject.js" }, + "The jar should contain an entry `kotlin2JsNoOutputFileProject.js` with no duplicates" + ) + } } } @@ -327,9 +370,7 @@ abstract class AbstractKotlin2JsGradlePluginIT(private val irBackend: Boolean) : project.build("build") { assertSuccessful() checkIrCompilationMessage() - if (irBackend) { - assertFileExists(kotlinClassesDir() + "default/manifest") - } else { + if (!irBackend) { assertFileExists(kotlinClassesDir() + "kotlin2JsNoOutputFileProject.js") } assertFileExists(kotlinClassesDir(sourceSet = "test") + "kotlin2JsNoOutputFileProject_test.js") @@ -445,47 +486,6 @@ abstract class AbstractKotlin2JsGradlePluginIT(private val irBackend: Boolean) : } } - @Test - fun testIncrementalCompilation() = Project("kotlin2JsICProject").run { - setupWorkingDir() - val modules = listOf("app", "lib") - val mainFiles = modules.flatMapTo(LinkedHashSet()) { - projectDir.resolve("$it/src/main").allKotlinFiles() - } - - build("build") { - assertSuccessful() - checkIrCompilationMessage() - assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) - if (irBackend) { - assertCompiledKotlinSources(project.relativize(mainFiles)) - } else { - assertCompiledKotlinSources(project.relativize(allKotlinFiles)) - } - } - - build("build") { - assertSuccessful() - assertCompiledKotlinSources(emptyList()) - } - - projectFile("A.kt").modify { - it.replace("val x = 0", "val x = \"a\"") - } - build("build") { - assertSuccessful() - checkIrCompilationMessage() - assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) - val affectedFiles = project.projectDir.getFilesByNames("A.kt", "useAInLibMain.kt", "useAInAppMain.kt", "useAInAppTest.kt") - if (irBackend) { - // only klib ic is supported for now, so tests are generated non-incrementally with ir backend - assertCompiledKotlinSources(project.relativize(affectedFiles.filter { it in mainFiles })) - } else { - assertCompiledKotlinSources(project.relativize(affectedFiles)) - } - } - } - @Test fun testIncrementalCompilationDisabled() = Project("kotlin2JsICProject").run { val options = defaultBuildOptions().run { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsNoOutputFileProject/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsNoOutputFileProject/build.gradle index f72cdd7d48e..60ce4bbed01 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsNoOutputFileProject/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsNoOutputFileProject/build.gradle @@ -20,6 +20,6 @@ dependencies { } if (project.findProperty("kotlin.js.useIrBackend")?.toBoolean() == true) { - compileKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-klib-dir", "-Xir-produce-js", "-Xir-only"] + compileKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-klib-dir", "-Xir-only"] compileTestKotlin2Js.kotlinOptions.freeCompilerArgs += ["-Xir-produce-js"] } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt index 18a84b1e0c0..2195f8c040f 100755 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt @@ -262,8 +262,13 @@ internal class Kotlin2JsSourceSetProcessor( // outputFile can be set later during the configuration phase, get it only after the phase: project.whenEvaluated { kotlinTask.configure { kotlinTaskInstance -> - kotlinTaskInstance.kotlinOptions.outputFile = kotlinTaskInstance.outputFile.absolutePath - val outputDir = kotlinTaskInstance.outputFile.parentFile + val kotlinOptions = kotlinTaskInstance.kotlinOptions + val outputDir: File = kotlinTaskInstance.outputFile.parentFile + kotlinOptions.outputFile = if (!kotlinOptions.isProduceUnzippedKlib()) { + kotlinTaskInstance.outputFile.absolutePath + } else { + kotlinTaskInstance.outputFile.parentFile.absolutePath + } if (outputDir.isParentOf(project.rootDir)) throw InvalidUserDataException( "The output directory '$outputDir' (defined by outputFile of $kotlinTaskInstance) contains or " + @@ -272,6 +277,20 @@ internal class Kotlin2JsSourceSetProcessor( "To fix this, consider using the default outputFile location instead of providing it explicitly." ) kotlinTaskInstance.destinationDir = outputDir + + if ( + kotlinOptions.freeCompilerArgs.contains(PRODUCE_JS) || + kotlinOptions.freeCompilerArgs.contains(PRODUCE_UNZIPPED_KLIB) || + kotlinOptions.freeCompilerArgs.contains(PRODUCE_ZIPPED_KLIB) + ) { + // Configure FQ module name to avoid cyclic dependencies in klib manifests (see KT-36721). + val baseName = if (kotlinCompilation.isMain()) { + project.name + } else { + "${project.name}_${kotlinCompilation.name}" + } + kotlinTaskInstance.kotlinOptions.freeCompilerArgs += "$MODULE_NAME=${project.klibModuleName(baseName)}" + } } val subpluginEnvironment: SubpluginEnvironment = SubpluginEnvironment.loadSubplugins(project, kotlinPluginVersion) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 70f86a47020..9509274f7e0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -43,6 +43,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure import org.jetbrains.kotlin.gradle.plugin.mpp.ownModuleName import org.jetbrains.kotlin.gradle.report.ReportingSettings +import org.jetbrains.kotlin.gradle.targets.js.ir.isProduceUnzippedKlib import org.jetbrains.kotlin.gradle.utils.* import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.library.impl.isKotlinLibrary @@ -276,7 +277,7 @@ abstract class AbstractKotlinCompile() : AbstractKo // Input is needed to force rebuild even if source files are not changed @get:Input - internal val coroutinesStr: Provider = project.provider {coroutines.get().name} + internal val coroutinesStr: Provider = project.provider { coroutines.get().name } @get:Input internal val coroutines: Provider = project.provider { @@ -397,7 +398,11 @@ abstract class AbstractKotlinCompile() : AbstractKo internal val isMultiplatform: Boolean by lazy { project.plugins.any { it is KotlinPlatformPluginBase || it is KotlinMultiplatformPluginWrapper } } @get:Internal - internal val abstractKotlinCompileArgumentsContributor by lazy { AbstractKotlinCompileArgumentsContributor(KotlinCompileArgumentsProvider(this)) } + internal val abstractKotlinCompileArgumentsContributor by lazy { + AbstractKotlinCompileArgumentsContributor( + KotlinCompileArgumentsProvider(this) + ) + } override fun setupCompilerArgs(args: T, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { abstractKotlinCompileArgumentsContributor.contributeArguments( @@ -496,10 +501,12 @@ open class KotlinCompile : AbstractKotlinCompile(), Kotl K2JVMCompilerArguments() override fun setupCompilerArgs(args: K2JVMCompilerArguments, defaultsOnly: Boolean, ignoreClasspathResolutionErrors: Boolean) { - compilerArgumentsContributor.contributeArguments(args, compilerArgumentsConfigurationFlags( - defaultsOnly, - ignoreClasspathResolutionErrors - )) + compilerArgumentsContributor.contributeArguments( + args, compilerArgumentsConfigurationFlags( + defaultsOnly, + ignoreClasspathResolutionErrors + ) + ) } @get:Internal @@ -635,23 +642,19 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko @get:Internal val outputFile: File - get() = outputFilePath?.let(::File) ?: defaultOutputFile + get() = kotlinOptions.outputFile?.let(::File) ?: defaultOutputFile @get:OutputFile @get:Optional val outputFileOrNull: File? get() = outputFile.let { file -> - if (file.isFile) { + if (!kotlinOptions.isProduceUnzippedKlib()) { file } else { null } } - @get:Input - val outputFilePath: String? - get() = kotlinOptions.outputFile - override fun findKotlinCompilerClasspath(project: Project): List = findKotlinJsCompilerClasspath(project)