diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2Tests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2Tests.kt index 73e1c8bd88e..411289ed43e 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2Tests.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/K2Tests.kt @@ -19,9 +19,10 @@ class K2HierarchicalMppIT : HierarchicalMppIT() { override val defaultBuildOptions: BuildOptions get() = super.defaultBuildOptions.copy(languageVersion = "2.0") } -@Ignore +@MppGradlePluginTests +@DisplayName("KLibs in K2") class K2KlibBasedMppIT : KlibBasedMppIT() { - override fun defaultBuildOptions(): BuildOptions = super.defaultBuildOptions().copy(languageVersion = "2.0") + override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK2() } @Ignore diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt index bc8c8702144..063708d2c42 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KlibBasedMppIT.kt @@ -5,240 +5,401 @@ package org.jetbrains.kotlin.gradle -import org.jetbrains.kotlin.gradle.util.modify +import org.gradle.testkit.runner.BuildResult +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.replaceText import org.jetbrains.kotlin.konan.target.HostManager -import org.junit.Assume +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.condition.OS +import org.junit.jupiter.api.io.TempDir import java.io.File +import java.nio.file.Path import java.util.* import java.util.zip.ZipFile -import kotlin.test.Test +import kotlin.io.path.appendText +import kotlin.io.path.deleteRecursively +import kotlin.io.path.isDirectory +import kotlin.io.path.writeText import kotlin.test.assertFalse import kotlin.test.assertTrue -open class KlibBasedMppIT : BaseGradleIT() { - override val defaultGradleVersion: GradleVersionRequired = GradleVersionRequired.FOR_MPP_SUPPORT - companion object { - private const val MODULE_GROUP = "com.example" - } +@DisplayName("KLibs in K1") +@MppGradlePluginTests +open class KlibBasedMppIT : KGPBaseTest() { - @Test - fun testBuildWithProjectDependency() = testBuildWithDependency { - gradleBuildScript().appendText( - "\n" + """ - dependencies { - commonMainImplementation(project("$dependencyModuleName")) - } - """.trimIndent() - ) - } + override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions.copyEnsuringK1() - @Test - fun testPublishingAndConsumptionWithEmptySourceSet() = testBuildWithDependency { - // KT-36674 - projectDir.resolve("$dependencyModuleName/src/windowsMain").run { - assertTrue { isDirectory } - deleteRecursively() - } - publishProjectDepAndAddDependency(validateHostSpecificPublication = false) - } - - @Test - fun testCommonSourceSetsInTransitiveDependencies() = with(Project("common-klib-lib-and-app")) { - // On macOS KT-41083 is also validated by publishing a lib with host specific source sets depending on another lib with host-specific source sets - setupWorkingDir() - val projectDepName = "dependency" - val publishedGroup = "published" - val producerProjectName = "producer" - embedProject(this, renameTo = projectDepName) - projectDir.resolve("$projectDepName/src").walkTopDown().filter { it.extension == "kt" }.forEach { ktFile -> - // Avoid FQN duplicates between producer & consumer - ktFile.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.lib") } - } - - gradleBuildScript(projectDepName).appendText( - """ - ${"\n"} - group = "$publishedGroup" - """.trimIndent() - ) - gradleBuildScript().modify { - transformBuildScriptWithPluginsDsl(it) + - """ - ${"\n"} - dependencies { "commonMainImplementation"(project(":$projectDepName")) } - group = "$publishedGroup" - """.trimIndent() - } - gradleSettingsScript().appendText("\nrootProject.name = \"$producerProjectName\"") - - build("publish") { - assertSuccessful() - } - - // Then consume the published project. To do that, rename the modules so that Gradle chooses the published ones given the original - // Maven coordinates and doesn't resolve them as project dependencies. - - val localGroup = "local" - gradleBuildScript(projectDepName).appendText("""${"\n"}group = "$localGroup"""") - gradleBuildScript().appendText( - """ - ${"\n"} - repositories { maven("${'$'}rootDir/repo") } - dependencies { "commonMainImplementation"("$publishedGroup:$producerProjectName:1.0") } - group = "$localGroup" - """.trimIndent() - ) - - val commonModules = listOf( - "published-producer-1.0-commonMain-[\\w-]+.klib", - "published-dependency-1.0-commonMain-[\\w-]+.klib", - ).map(::Regex) - - val hostSpecificModules = listOf( - "published-producer-1.0-iosMain-[\\w-]+.klib", - "published-dependency-1.0-iosMain-[\\w-]+.klib", - ).map(::Regex) - - val windowsAndLinuxModules = listOf( - "published-producer-1.0-windowsAndLinuxMain-[\\w-]+.klib", - "published-dependency-1.0-windowsAndLinuxMain-[\\w-]+.klib", - ).map(::Regex) - - checkTaskCompileClasspath( - "compileWindowsAndLinuxMainKotlinMetadata", - checkModulesInClasspath = commonModules + windowsAndLinuxModules, - checkModulesNotInClasspath = hostSpecificModules - ) - - // The consumer should correctly receive the klibs of the host-specific source sets - - if (HostManager.hostIsMac) { - checkTaskCompileClasspath( - "compileIosMainKotlinMetadata", - checkModulesInClasspath = commonModules + hostSpecificModules, - checkModulesNotInClasspath = windowsAndLinuxModules + @DisplayName("Could be compiled with project dependency") + @GradleTest + fun testBuildWithProjectDependency( + gradleVersion: GradleVersion, + @TempDir localRepo: Path, + ) { + testBuildWithDependency(gradleVersion, localRepo) { + buildGradleKts.appendText( + """ + | + |dependencies { + | commonMainImplementation(project("$dependencyModuleName")) + |} + """.trimMargin() ) } } - @Test - fun testHostSpecificBuildWithPublishedDependency() = testBuildWithDependency { - // Host-specific dependencies are only possible on macOS - Assume.assumeTrue(HostManager.hostIsMac) - publishProjectDepAndAddDependency(validateHostSpecificPublication = true) + @DisplayName("KT-36674: Could be compiled with empty source set") + @GradleTest + fun testPublishingAndConsumptionWithEmptySourceSet( + gradleVersion: GradleVersion, + @TempDir localRepo: Path, + ) { + testBuildWithDependency(gradleVersion, localRepo) { + subProject(dependencyModuleName) + .kotlinSourcesDir("windowsMain") + .parent + .run { + assertTrue(isDirectory()) + deleteRecursively() + } + publishProjectDepAndAddDependency(validateHostSpecificPublication = false) + } } - private fun Project.publishProjectDepAndAddDependency(validateHostSpecificPublication: Boolean) { + @DisplayName("Compiles with common sources in transitive dependencies") + @GradleTest + fun testCommonSourceSetsInTransitiveDependencies( + gradleVersion: GradleVersion, + @TempDir localRepo: Path, + ) { + project( + "common-klib-lib-and-app", + gradleVersion, + localRepoDir = localRepo + ) { + // On macOS KT-41083 is also validated by publishing a lib with host specific source sets depending on another lib with host-specific source sets + val projectDepName = "dependency" + val publishedGroup = "published" + val producerProjectName = "producer" + includeOtherProjectAsSubmodule( + otherProjectName = "common-klib-lib-and-app", + newSubmoduleName = projectDepName, + isKts = true, + localRepoDir = localRepo + ) + + subProject(projectDepName) + .projectPath + .allKotlinFiles + .forEach { ktFile -> + // Avoid FQN duplicates between producer & consumer + ktFile.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.lib") + } + + subProject(projectDepName).buildGradleKts.appendText( + """ + | + |group = "$publishedGroup" + """.trimMargin() + ) + + buildGradleKts.appendText( + """ + | + |dependencies { "commonMainImplementation"(project(":$projectDepName")) } + |group = "$publishedGroup" + """.trimMargin() + ) + settingsGradleKts.appendText( + """ + | + |rootProject.name = "$producerProjectName" + | + """.trimMargin() + ) + + build("publish") + + // Then consume the published project. To do that, rename the modules so that Gradle chooses the published ones given the original + // Maven coordinates and doesn't resolve them as project dependencies. + val localGroup = "local" + subProject(projectDepName).buildGradleKts.appendText( + """ + | + |group = "$localGroup" + """.trimMargin() + ) + buildGradleKts.appendText( + """ + | + |repositories { maven("${'$'}rootDir/repo") } + |dependencies { "commonMainImplementation"("$publishedGroup:$producerProjectName:1.0") } + |group = "$localGroup" + """.trimMargin() + ) + + val commonModules = listOf( + "published-producer-1.0-commonMain-[\\w-]+.klib", + "published-dependency-1.0-commonMain-[\\w-]+.klib", + ).map(::Regex) + + val hostSpecificModules = listOf( + "published-producer-1.0-iosMain-[\\w-]+.klib", + "published-dependency-1.0-iosMain-[\\w-]+.klib", + ).map(::Regex) + + val windowsAndLinuxModules = listOf( + "published-producer-1.0-windowsAndLinuxMain-[\\w-]+.klib", + "published-dependency-1.0-windowsAndLinuxMain-[\\w-]+.klib", + ).map(::Regex) + + checkTaskCompileClasspath( + "compileWindowsAndLinuxMainKotlinMetadata", + checkModulesInClasspath = commonModules + windowsAndLinuxModules, + checkModulesNotInClasspath = hostSpecificModules + ) + + // The consumer should correctly receive the klibs of the host-specific source sets + if (HostManager.hostIsMac) { + checkTaskCompileClasspath( + "compileIosMainKotlinMetadata", + checkModulesInClasspath = commonModules + hostSpecificModules, + checkModulesNotInClasspath = windowsAndLinuxModules + ) + } + } + } + + // Host-specific dependencies are only possible on macOS + @OsCondition( + supportedOn = [OS.MAC], + enabledOnCI = [OS.MAC], + ) + @DisplayName("Works with host specific dependencies") + @GradleTest + fun testHostSpecificBuildWithPublishedDependency( + gradleVersion: GradleVersion, + @TempDir localRepo: Path + ) { + testBuildWithDependency(gradleVersion, localRepo) { + publishProjectDepAndAddDependency(validateHostSpecificPublication = true) + } + } + + @DisplayName("Works with Kotlin native transitive dependencies") + @GradleTest + fun testKotlinNativeImplPublishedDeps( + gradleVersion: GradleVersion, + @TempDir localRepo: Path + ) { + testKotlinNativeImplementationDependencies(gradleVersion, localRepo) { + build(":$transitiveDepModuleName:publish", ":$dependencyModuleName:publish") + + buildGradleKts.appendText( + """ + | + |repositories { + | maven("${'$'}rootDir/repo") + |} + | + |dependencies { + | commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0") + |} + """.trimMargin() + ) + + listOf(transitiveDepModuleName, dependencyModuleName).forEach { + // prevent Gradle from linking the above dependency to the project: + subProject(it).buildGradleKts.appendText( + """ + | + |group = "com.some.other.group" + """.trimMargin() + ) + } + } + } + + @DisplayName("Works with project dependencies containing Kotlin Native target") + @GradleTest + fun testKotlinNativeImplProjectDeps( + gradleVersion: GradleVersion, + @TempDir localRepo: Path, + ) { + testKotlinNativeImplementationDependencies(gradleVersion, localRepo) { + buildGradleKts.appendText( + """ + | + |dependencies { + | "commonMainImplementation"(project(":$dependencyModuleName")) + |} + """.trimMargin() + ) + } + } + + @DisplayName("KT-38746: Should not disable compilation of shared source set") + @GradleTest + fun testAvoidSkippingSharedNativeSourceSetKt38746(gradleVersion: GradleVersion) { + project("hierarchical-all-native", gradleVersion) { + val targetNames = listOf( + // Try different alphabetical ordering of the targets to ensure that the behavior doesn't depend on it, + // as with 'first target' + listOf("a1", "a2", "a3"), + listOf("a3", "a1", "a2"), + listOf("a2", "a3", "a1"), + ) + val targetParamNames = listOf("mingwTargetName", "linuxTargetName", "macosTargetName", "currentHostTargetName") + for (names in targetNames) { + val currentHostTargetName = when { + HostManager.hostIsMingw -> names[0] + HostManager.hostIsLinux -> names[1] + HostManager.hostIsMac -> names[2] + else -> error("unexpected host") + } + val params = targetParamNames.zip(names + currentHostTargetName) { k, v -> "-P$k=$v" } + build(":clean", ":compileCurrentHostAndLinuxKotlinMetadata", *params.toTypedArray()) { + assertTasksExecuted(":compileCurrentHostAndLinuxKotlinMetadata", ":compileAllNativeKotlinMetadata") + } + } + } + } + + private fun TestProject.publishProjectDepAndAddDependency(validateHostSpecificPublication: Boolean) { build(":$dependencyModuleName:publish") { - assertSuccessful() - if (validateHostSpecificPublication) - checkPublishedHostSpecificMetadata(this@build) + if (validateHostSpecificPublication) checkPublishedHostSpecificMetadata(this@publishProjectDepAndAddDependency) } - gradleBuildScript().appendText( - "\n" + """ - repositories { - maven("${'$'}rootDir/repo") - } - dependencies { - commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0") - } - """.trimIndent() + buildGradleKts.appendText( + """ + | + |repositories { + | maven("${'$'}rootDir/repo") + |} + | + |dependencies { + | commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0") + |} + """.trimMargin() ) // prevent Gradle from linking the above dependency to the project: - gradleBuildScript(dependencyModuleName).appendText("\ngroup = \"some.other.group\"") + subProject(dependencyModuleName) + .buildGradleKts + .appendText( + """ + | + |group = "some.other.group" + """.trimMargin() + ) } private val dependencyModuleName = "project-dep" + private val transitiveDepModuleName = "transitive-dep" - private fun testBuildWithDependency(configureDependency: Project.() -> Unit) = with(Project("common-klib-lib-and-app")) { - embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName) + private fun testBuildWithDependency( + gradleVersion: GradleVersion, + localRepo: Path, + configureDependency: TestProject.() -> Unit + ) { + project("common-klib-lib-and-app", gradleVersion, localRepoDir = localRepo) { + includeOtherProjectAsSubmodule( + otherProjectName = "common-klib-lib-and-app", + newSubmoduleName = dependencyModuleName, + isKts = true, + localRepoDir = localRepo, + ) - projectDir.resolve("$dependencyModuleName/src/commonMain/kotlin/TestKt37832.kt").writeText( - "package com.example.test.kt37832" + "\n" + "class MyException : RuntimeException()" - ) + subProject(dependencyModuleName) + .kotlinSourcesDir("commonMain") + .resolve("TestKt37832.kt") + .writeText( + """ + |package com.example.test.kt37832 + | + |class MyException : RuntimeException() + """.trimMargin() + ) - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - projectDir.resolve(dependencyModuleName + "/src").walkTopDown().filter { it.extension == "kt" }.forEach { file -> - file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep") } - } - - configureDependency() - - projectDir.resolve("src/commonMain/kotlin/LibUsage.kt").appendText( - "\n" + """ - package com.h0tk3y.hmpp.klib.demo.test - - import com.projectdep.LibCommonMainExpect as ProjectDepExpect - - private fun useProjectDep() { - ProjectDepExpect() - } - """.trimIndent() - ) - - projectDir.resolve("src/linuxMain/kotlin/LibLinuxMainUsage.kt").appendText( - "\n" + """ - package com.h0tk3y.hmpp.klib.demo.test - - import com.projectdep.libLinuxMainFun as libFun - - private fun useProjectDep() { - libFun() - } - """.trimIndent() - ) - - val tasksToExecute = listOf( - ":compileJvmAndJsMainKotlinMetadata", - ":compileLinuxMainKotlinMetadata", - ) + (if (HostManager.hostIsMac) listOf(":compileIosMainKotlinMetadata") else emptyList()) - - build("assemble") { - assertSuccessful() - - assertTasksExecuted(*tasksToExecute.toTypedArray()) - - assertFileExists("build/classes/kotlin/metadata/commonMain/default/manifest") - assertFileExists("build/classes/kotlin/metadata/jvmAndJsMain/default/manifest") - assertFileExists("build/classes/kotlin/metadata/linuxMain/klib/${projectName}_linuxMain") - - // Check that the common and JVM+JS source sets don't receive the Kotlin/Native stdlib in the classpath: - run { - fun getClasspath(taskPath: String): Iterable { - val argsPrefix = " $taskPath Kotlin compiler args:" - return output.lines().single { argsPrefix in it } - .substringAfter("-classpath ").substringBefore(" -").split(File.pathSeparator) + subProject(dependencyModuleName) + .projectPath + .allKotlinFiles + .forEach { file -> + file.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.projectdep") } - fun classpathHasKNStdlib(classpath: Iterable) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") } + configureDependency() + kotlinSourcesDir("commonMain").resolve("LibUsage.kt").writeText( + """ + | + |package com.h0tk3y.hmpp.klib.demo.test + | + |import com.projectdep.LibCommonMainExpect as ProjectDepExpect + | + |private fun useProjectDep() { + | ProjectDepExpect() + |} + """.trimMargin() + ) + + kotlinSourcesDir("linuxMain").resolve("LibLinuxMainUsage.kt").writeText( + """ + | + |package com.h0tk3y.hmpp.klib.demo.test + | + |import com.projectdep.libLinuxMainFun as libFun + | + |private fun useProjectDep() { + | libFun() + |} + """.trimMargin() + ) + + val tasksToExecute = listOfNotNull( + ":compileJvmAndJsMainKotlinMetadata", + ":compileLinuxMainKotlinMetadata", + if (HostManager.hostIsMac) ":compileIosMainKotlinMetadata" else null + ) + + build("assemble") { + assertTasksExecuted(tasksToExecute) + + assertFileInProjectExists("build/classes/kotlin/metadata/commonMain/default/manifest") + assertFileInProjectExists("build/classes/kotlin/metadata/jvmAndJsMain/default/manifest") + assertDirectoryInProjectExists("build/classes/kotlin/metadata/linuxMain/klib/${projectName}_linuxMain") + + // Check that the common and JVM+JS source sets don't receive the Kotlin/Native stdlib in the classpath: assertFalse(classpathHasKNStdlib(getClasspath(":compileCommonMainKotlinMetadata"))) assertFalse(classpathHasKNStdlib(getClasspath(":compileJvmAndJsMainKotlinMetadata"))) } } } - private fun checkPublishedHostSpecificMetadata(compiledProject: CompiledProject) = with(compiledProject) { - val groupDir = project.projectDir.resolve("repo/com/example") + private fun classpathHasKNStdlib(classpath: Iterable) = classpath.any { "klib/common/stdlib" in it.replace("\\", "/") } - assertTasksExecuted( - ":$dependencyModuleName:compileIosMainKotlinMetadata") + private fun BuildResult.getClasspath(taskPath: String): Iterable { + val argsPrefix = " $taskPath Kotlin compiler args:" + return output.lines().single { argsPrefix in it } + .substringAfter("-classpath ").substringBefore(" -").split(File.pathSeparator) + } + + private fun BuildResult.checkPublishedHostSpecificMetadata(project: TestProject) { + val groupDir = project.projectPath.resolve("repo/com/example") + + assertTasksExecuted(":$dependencyModuleName:compileIosMainKotlinMetadata") // Check that the metadata JAR doesn't contain the host-specific source set entries, but contains the shared-Native source set // that can be built on every host: - - ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar")).use { metadataJar -> - assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") } } - assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("linuxMain") } } - } + ZipFile(groupDir.resolve("$dependencyModuleName/1.0/$dependencyModuleName-1.0-all.jar").toFile()) + .use { metadataJar -> + assertTrue(metadataJar.entries().asSequence().none { it.name.startsWith("iosMain") }) + assertTrue(metadataJar.entries().asSequence().any { it.name.startsWith("linuxMain") }) + } // Then check that in the host-specific modules, there's a metadata artifact that contains the host-specific source set but not the // common source sets: - val hostSpecificTargets = when { HostManager.hostIsMac -> listOf("iosArm64", "iosX64") else -> error("Host doesn't support host-specific metadata") @@ -246,99 +407,64 @@ open class KlibBasedMppIT : BaseGradleIT() { hostSpecificTargets.forEach { targetName -> val moduleName = "$dependencyModuleName-${targetName.lowercase(Locale.getDefault())}" - ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar")).use { metadataJar -> - assertTrue { metadataJar.entries().asSequence().any { it.name.startsWith("iosMain") } } - assertTrue { metadataJar.entries().asSequence().none { it.name.startsWith("commonMain") } } - } + ZipFile(groupDir.resolve("$moduleName/1.0/$moduleName-1.0-metadata.jar").toFile()) + .use { metadataJar -> + assertTrue(metadataJar.entries().asSequence().any { it.name.startsWith("iosMain") }) + assertTrue(metadataJar.entries().asSequence().none { it.name.startsWith("commonMain") }) + } } // Also check that the targets that don't include any host-specific sources don't even have the metadata artifact: - - groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar").let { metadataJar -> - assertTrue { !metadataJar.exists() } - } + assertFileNotExists( + groupDir.resolve("$dependencyModuleName-linuxx64/1.0/$dependencyModuleName-linuxx64-1.0-metadata.jar") + ) } - private val transitiveDepModuleName = "transitive-dep" - - @Test - fun testKotlinNativeImplPublishedDeps() = - testKotlinNativeImplementationDependencies { - build(":$transitiveDepModuleName:publish", ":$dependencyModuleName:publish") { - assertSuccessful() - } - - gradleBuildScript().appendText( - "\n" + """ - repositories { - maven("${'$'}rootDir/repo") - } - dependencies { - commonMainImplementation("$MODULE_GROUP:$dependencyModuleName:1.0") - } - """.trimIndent() - ) - - listOf(transitiveDepModuleName, dependencyModuleName).forEach { - // prevent Gradle from linking the above dependency to the project: - gradleBuildScript(it).appendText("\ngroup = \"com.some.other.group\"") - } - } - - @Test - fun testKotlinNativeImplProjectDeps() = - testKotlinNativeImplementationDependencies { - gradleBuildScript().appendText("\ndependencies { \"commonMainImplementation\"(project(\":$dependencyModuleName\")) }") - } - private fun testKotlinNativeImplementationDependencies( - setupDependencies: Project.() -> Unit, - ) = with(Project("common-klib-lib-and-app")) { - embedProject(Project("common-klib-lib-and-app"), renameTo = transitiveDepModuleName) - embedProject(Project("common-klib-lib-and-app"), renameTo = dependencyModuleName).apply { - projectDir.resolve(dependencyModuleName).walkTopDown().filter { it.extension == "kt" }.forEach { file -> - // Avoid duplicate FQNs as in the compatibility mode, the K2Metadata compiler reports duplicate symbols on them: - file.modify { it.replace("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.demo1") } - } - } - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleBuildScript(dependencyModuleName).appendText("\ndependencies { \"commonMainImplementation\"(project(\":$transitiveDepModuleName\")) }") + gradleVersion: GradleVersion, + localRepo: Path, + setupProject: TestProject.() -> Unit, + ) { + project("common-klib-lib-and-app", gradleVersion, localRepoDir = localRepo) { + includeOtherProjectAsSubmodule( + otherProjectName = "common-klib-lib-and-app", + newSubmoduleName = transitiveDepModuleName, + isKts = true, + localRepoDir = localRepo, + ) + includeOtherProjectAsSubmodule( + otherProjectName = "common-klib-lib-and-app", + newSubmoduleName = dependencyModuleName, + isKts = true, + localRepoDir = localRepo + ) - setupDependencies(this@with) + subProject(dependencyModuleName) + .projectPath + .allKotlinFiles + .forEach { file -> + // Avoid duplicate FQNs as in the compatibility mode, the K2Metadata compiler reports duplicate symbols on them: + file.replaceText("package com.h0tk3y.hmpp.klib.demo", "package com.h0tk3y.hmpp.klib.demo1") + } - val compileNativeMetadataTaskName = "compileLinuxMainKotlinMetadata" - build(":$compileNativeMetadataTaskName") { - assertSuccessful() + subProject(dependencyModuleName) + .buildGradleKts + .appendText( + """ + | + |dependencies { + | "commonMainImplementation"(project(":$transitiveDepModuleName")) + |} + """.trimMargin() + ) + + setupProject(this) + + build(":compileLinuxMainKotlinMetadata") } } - @Test - fun testAvoidSkippingSharedNativeSourceSetKt38746() = with(Project("hierarchical-all-native")) { - val targetNames = listOf( - // Try different alphabetical ordering of the targets to ensure that the behavior doesn't depend on it, as with 'first target' - listOf("a1", "a2", "a3"), - listOf("a3", "a1", "a2"), - listOf("a2", "a3", "a1"), - ) - val targetParamNames = listOf("mingwTargetName", "linuxTargetName", "macosTargetName", "currentHostTargetName") - for (names in targetNames) { - val currentHostTargetName = when { - HostManager.hostIsMingw -> names[0] - HostManager.hostIsLinux -> names[1] - HostManager.hostIsMac -> names[2] - else -> error("unexpected host") - } - val params = targetParamNames.zip(names + currentHostTargetName) { k, v -> "-P$k=$v" } - build(":clean", ":compileCurrentHostAndLinuxKotlinMetadata", *params.toTypedArray()) { - assertSuccessful() - assertTasksExecuted(":compileCurrentHostAndLinuxKotlinMetadata", ":compileAllNativeKotlinMetadata") - } - } - } - - private var testBuildRunId = 0 - - private fun BaseGradleIT.Project.checkTaskCompileClasspath( + private fun TestProject.checkTaskCompileClasspath( taskPath: String, checkModulesInClasspath: List = emptyList(), checkModulesNotInClasspath: List = emptyList(), @@ -350,28 +476,27 @@ open class KlibBasedMppIT : BaseGradleIT() { checkPrintedItems(subproject, expression, checkModulesInClasspath, checkModulesNotInClasspath) } - private fun BaseGradleIT.Project.checkPrintedItems( + private fun TestProject.checkPrintedItems( subproject: String?, itemsExpression: String, checkAnyItemsContains: List, checkNoItemContains: List, - ) = with(testCase) { - setupWorkingDir() - - val printingTaskName = "printItems${testBuildRunId++}" - gradleBuildScript(subproject).appendText( + ) { + val printingTaskName = "printItems" + val testProject = if (subproject != null) subProject(subproject) else this + testProject.buildGradleKts.appendText( """ - ${'\n'} - tasks.register("$printingTaskName") { - dependsOn("transformDependenciesMetadata") - doLast { - println("###$printingTaskName" + $itemsExpression) - } - } - """.trimIndent() + + |tasks.register("$printingTaskName") { + | dependsOn("transformDependenciesMetadata") + | doLast { + | println("###$printingTaskName" + $itemsExpression) + | } + |} + """.trimMargin() ) + build("${subproject?.prependIndent(":").orEmpty()}:$printingTaskName") { - assertSuccessful() val itemsLine = output.lines().single { "###$printingTaskName" in it }.substringAfter(printingTaskName) // NOTE: This does not work for commonized libraries, they may contain the ',' naturally val items = itemsLine.removeSurrounding("[", "]").split(", ").toSet() @@ -383,4 +508,8 @@ open class KlibBasedMppIT : BaseGradleIT() { } } } + + companion object { + private const val MODULE_GROUP = "com.example" + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt index 0e73c7d6e3d..122882b3228 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt @@ -397,11 +397,16 @@ class TestProject( */ fun includeOtherProjectAsSubmodule( otherProjectName: String, - pathPrefix: String, + pathPrefix: String = "", newSubmoduleName: String = otherProjectName, isKts: Boolean = false, + localRepoDir: Path? = null, ) { - val otherProjectPath = "$pathPrefix/$otherProjectName".testProjectPath + val otherProjectPath = if (pathPrefix.isEmpty()) { + otherProjectName.testProjectPath + } else { + "$pathPrefix/$otherProjectName".testProjectPath + } otherProjectPath.copyRecursively(projectPath.resolve(newSubmoduleName)) val gradleSettingToUpdate = if (isKts) settingsGradleKts else settingsGradle @@ -412,6 +417,8 @@ class TestProject( include(":$newSubmoduleName") """.trimIndent() ) + + localRepoDir?.let { subProject(newSubmoduleName).configureLocalRepository(localRepoDir) } } fun includeOtherProjectAsIncludedBuild( @@ -814,7 +821,7 @@ private fun TestProject.configureSingleNativeTargetInSubFolders(preset: String = } } -private fun TestProject.configureLocalRepository(localRepoDir: Path) { +private fun GradleProject.configureLocalRepository(localRepoDir: Path) { projectPath.toFile().walkTopDown() .filter { it.isFile && it.name in buildFileNames } .forEach { file -> diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/common-klib-lib-and-app/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/common-klib-lib-and-app/build.gradle.kts index e37514df6c7..4f4213191ae 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/common-klib-lib-and-app/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/common-klib-lib-and-app/build.gradle.kts @@ -1,16 +1,11 @@ plugins { - kotlin("multiplatform").version("") + kotlin("multiplatform") id("maven-publish") } group = "com.example" version = "1.0" -repositories { - mavenLocal() - mavenCentral() -} - kotlin { jvm() js() @@ -65,7 +60,7 @@ kotlin { publishing { repositories { - maven("$rootDir/repo") + maven("") } }